5、C/C++——Linux系统编程
cat stu.json >> stu2.json 这就是重定向
一、系统调用:
什么是系统调用:
操作系统实现并提供给外部应用程序的编程接口(Application Programming Interface,API), 是应用程序同系统之间数据交互的桥梁。
C标准函数和系统函数调用关系如一个helloworld 如何打印到屏幕:
二、C标准库文件IO函数:
include unix标准库
1、错误处理函数:
char *strerror(int errnum) ;将errno错误 翻译为文字 void perror(const char *s); 打印错误
2、open函数:
int open(char *pathname, int flag) ##include
参数:
Pathname:欲打开的文件路径名 Flag:文件打开方式 O_RDONLY|O_WRONLY|O_RDWR|O_NONBLOCK|O_CREAT: 若文件不存在则创建。 mode: 使用前提是flags指明了O_CREAT,取值8进制数,
(头文件是#include)
返回值:
成功:打开文件所得的 文件描述符(整数)
失败:-1,设置errno(相当于内核的一个全局变量)
int open(char *pathname, int flag, mode_t mode)
参数:
Pathname:欲打开的文件路径名 Flag:文件打开方式 O_RDONLY|O_WRONLY|O_RDWR …… mode:使用前提是参数2指定为O_CREAT。取八进制数,用来描述文件的访问权限。rwx 创建文件的最终权限=mode&~unmask
返回值:所得的 文件
成功:打开文件描述符(整数)
失败:-1,设置errno(相当于内核的一个全局变量)
2、close函数
int close(int fd);
fd表示open打开文件流的状态码(open的返回值);
close 返回 0 表示 成功 , 或者 -1 表示 有 错误 发生
借助 man 2 func查找对应函数的使用方法
3、read、write函数:
ssize_t read(int fd, void *buf, size_t count)
参数:
fd:文件描述符
buf:存数据的缓冲区 count:缓冲区大小
返回值:
成功:读到的字节数
失败:-1,设置errno
-1:如果errno=EAGIN或EWOULDBLOCK,说明不是read失败,而是read在非阻塞方式读一个设备文件(网络文件),并且文件无数据,
write函数:
ssize_t write(int fd, const void *buf, size_t count) #const 防止在写入到fd的时候被修改
参数:
fd:文件描述符
buf:待写出数据的缓冲区 count:数据大小
返回值:
成功:写入的字节数
失败:-1,设置errno
4、缓冲区
read、write 函数常常被称为 Unbuffered I/O。指的是无用户级缓冲区。但不保证不使用内核缓冲区。
由于磁盘的读写需要大量时间,为优化读写,最底层的内核会采用预读入缓输出的机制进行IO操作
系统调用和库函数的区别:
5、文件描述符:
下图表示一个进程./a.out的存储空间
PCB:process control block进程控制块 PCB实质上是一个结构体,struct task_struct{}。
- 本质: 结构体
- 成员:文件描述符表
文件描述符:0/1/2/3……1023 表中可用的最小的。
0 – STDIN_FILENO 标准输入
1 - STDOUT_FILENO 标准输出
2- STDERR_FILENO 标准错误
6、阻塞、非阻塞
阻塞是设备文件、网络文件的属性。
产生阻塞的场景,读设备文件or读网络文件。(读常规文件无阻塞概念)
/dev/tty ----终端文件
open("/dev/tty",O_RDWR|O_NONBLOCK) --以非阻塞状态打开/dev/tty终端设备文件
阻塞:
非阻塞:
超时非阻塞:
#include<unistd.h> #include<fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); //fcntl用于管理文件记录锁 int fcntl(int fd, int cmd ,struct flock* lock);
int flag = fcntl(fd, F_GETFL); flag |= O_NONBLOCK; fcntl(fd, F_SETFL, flag);
获取文件状态:F_GETFL
设置文件状态:F_SETFL
8、Lseek函数:
Off_t lseek(int fd ,off_t offset, int whence)
参数:
fd:文件描述符 offset:偏移量 whence:启始偏移位置(SEEK_SET/SEEK_CUR/SEEK_END)
返回值:
成功:较启始位置偏移量
失败:-1,设置errno
1、文件读写是使用的同一个偏移量。
2、使用lseek获取文件大小 int lseek(fd , 0 , SEEK_END)
3、扩展文件大小的方式,要使文件大小真正扩展,必须引起IO操作
传入参数:
- 指针作为函数参数
- 通常有const关键字修饰
- 指针指向有效区域,在函数内部做读操作
传出参数:
- 指针作为函数参数
- 在函数调用之前,指针指向的空间可以无意义,但必须有效
- 在函数内部做写操作
- 在函数调用结束后,充当函数返回值
传入传出参数:
- 指针作为函数参数
- 在函数调用之前要有实际意义
- 在函数内部,先做读操作,在做写操作
- 函数调用结束后,充当函数返回值
9、文件系统
文件系统是一组规则,规定对文件的存储及读取的一般方法。文件系统在磁盘格式化过程中指定。常见的文件系统有:fat32 ntfs exfat ext2 、ext3 、ext4
9.1、文件存储:
inode:
其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的 inode 都存储在磁盘上。少量常用、近期使用的 inode 会被缓存到内存中。
dentry:
dictionary entry目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode,...},而文件内容(data)保存在磁盘盘块中。
9.2、文件操作
#include<sys/ stat.h> int state(const char *pathname, struct stat *statbuf);
参数:
Pathname:文件路径
Statbuf:(传出参数)存放文件属性
返回值:
成功:0
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char *argv[]) { struct stat sb; if (argc != 2) { fprintf(stderr, "Usage: %s <pathname>\n", argv[0]); exit(EXIT_FAILURE); } if (stat(argv[1], &sb) == -1) { perror("stat"); exit(EXIT_SUCCESS); } printf("File type: "); switch (sb.st_mode & S_IFMT) { case S_IFBLK: printf("block device\n"); break; case S_IFCHR: printf("character device\n"); break; case S_IFDIR: printf("directory\n"); break; case S_IFIFO: printf("FIFO/pipe\n"); break; case S_IFLNK: printf("symlink\n"); break; case S_IFREG: printf("regular file\n"); break; case S_IFSOCK: printf("socket\n"); break; default: printf("unknown?\n"); break; } printf("I-node number: %ld\n", (long) sb.st_ino); printf("Mode: %lo (octal)\n", (unsigned long) sb.st_mode); printf("Link count: %ld\n", (long) sb.st_nlink); printf("Ownership: UID=%ld GID=%ld\n", (long) sb.st_uid, (long) sb.st_gid); printf("Preferred I/O block size: %ld bytes\n", (long) sb.st_blksize); printf("File size: %lld bytes\n", (long long) sb.st_size); printf("Blocks allocated: %lld\n", (long long) sb.st_blocks); printf("Last status change: %s", ctime(&sb.st_ctime)); printf("Last file access: %s", ctime(&sb.st_atime)); printf("Last file modification: %s", ctime(&sb.st_mtime)); exit(EXIT_SUCCESS); }
int lstat(const char *path, struct stat *buf);
成返回 0;失败返回-1 设置 errno 为恰当值。
文件类型判断方法:st_mode 取高 4 位。 但应使用宏函数: S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
穿透符号链接:stat:会;lstat:不会
Link/unlink:
结合前面dentry和inode的知识,link可以创建一个文件的dentry也即硬连接,而unlink可以释放一个dentry。
int link(const char *oldpath, const char *newpath); int unlink(const char *pathname);
注意 Linux 下删除文件的机制:不断将 st_nlink -1,直至减到 0 为止。无目录项对应的 文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定) 因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。
unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开的文件的进程关闭该文件,系统才会挑时间将文件释放掉。
隐式回收:
当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统 的这一特性称之为隐式回收系统资源。 在写服务器代码的时候一定要避免隐式回收。
9.3、目录操作
cat stu.json >> stu2.json 这就是重定向
文件、目录权限
目录设置黏住位:若有 w 权限,创建不变,删除、修改只能由 root、目录所有者、文件所有者操作。
重定向dup和dup2:
cat stu.json >> stu2.json 这就是重定向
int dup(int oldfd); 复制文件的文件描述符,可以共享绝大部分信息对文件进行操作。
int dup2(int oldfd, int newfd);
命令行:cat test.c > out 操作文件描述符,将输出到屏幕(或者其他文件)的内容,转输出到其他out 文件。
dup代码示例:复制文件描述符,并向文件写数据.
类似于copy函数,就是复制原有的文件描述符赋值给新的文件描述符
输出:
查看test.txt文件,里面有两段字符串:
:Advanced Programming! write by fd
:Advanced Programming! write by newfd
dup类似与copy
dup2代码示例:
int dup2(int oldfd, int newfd);函数将新文件描述符更改为旧文件描述符。当对新文件描述符进行操作时,其实是在对旧文件描述符进行读写等操作:既是将oldfd赋值给newfd。
使用fcntl()函数实现dup2():
#Linux#