目录
1 C库IO函数工作流程
2 Linux文件I/O
2.1 大文件写入另一个文件
2.1.1 perror和errno
2.2 阻塞读和非阻塞读
2.2.1 阻塞读终端
2.2.2 非阻塞读终端
2.3 stat函数
2.3.1 stat命令
2.3.2 stat函数
2.4 文件属性相关的函数
2.5 dup和dup2——重定向
fflush,刷新缓冲区,输入则丢弃;输出,则写入到输出设备(磁盘或屏幕)
1、进程控制块PCB标识唯一进程
2、PCB块中有一个数组——文件描述符表,索引0~1023个文件描述符。每打开文件就占用一个描述符,且占用的时空闲的而且最小的文件描述符。
3、前三个分别为——标准输入文件(STDIN_FILENO),标准输出文件(STDOUT_FILENO),错误文件(STDERR_FILENO)。这三个默认是打开的。
4、通过文件描述符找到磁盘文件
Linux文件I/O
1、open
返回值为文件描述符
(1)int open(const char * pathname, int flags);
(2)int open(const char * pathname, int flags, mode_t mode);
flags——O_RDONLY,O_WRONLY,O_RDWR(必选项);O_CREAT(创建文件),O_APPEND(追加文件),O_TRUNC(文件截断),O_NONBLOCK(设置非阻塞)。flags的每一个位对应不同的操作
mode_t——访问权限。8进制数
2、read
ssize_t read(int fd, void * buf, size_t count); //count表示能存放的最大字节数
fd——文件描述符
成功,>0,返回读出的字节数,=0,文件读完;失败,返回-1。
3、write
ssize_t write(int fd, const void * buf, size_t count); //count,表示buf中字节总数
失败,返回-1;成功,返回写入到文件的字节数
4、lseek
off_t lseek(int fd, off_t offset, int whence); //返回指针距离文件首部的距离
whence:SEEK_SET、SEEK_CUR、SEEK_END
offset:相对于whence来说
(1)文件指针移动到头部
lseek(fd, 0, SEEK_SET); //offset是相对于whence来说的
(2)获取文件指针当前位置
int len = lseek(fd, 0, SEEK_CUR);
(3)获取文件长度
int len = lseek(fd, 0, SEEK_END)
(4)文件扩展
在SEEK_END,进行offset > 0的lseek调用,然后写入一些字符,即可扩展文件的size成功。
扩展的作用——使用扩展,用占位符占用一部分空间先占用,当使用时填充。当多线程写时,也需要现有一块空间,然后不同的线程操作一块空间中的不同部分。
系统函数的read和write直接操纵磁盘,没有I/O缓冲区
首先读取文件到缓冲区,然后把缓冲区的文件写入另一个文件
这里是write使用了len而不是strlen(buf),因为buf中存放的并不是字符串,而是一个个字符,即buf并不是以'\0'结尾的标准字符串。而strlen的计算是根据'\0'出现的位置计算的。
errno是系统函数库的全局变量——当open,read,write,lseek等调用失败时,会设置errno的值,系统根据这个值,提示错误信息
perror()函数调用时,会读取errno的值,然后根据值来提示错误信息
man perror
#include
void perror(const char *s); #include const char * const sys_errlist[]; int sys_nerr; int error;
终端设备默认阻塞,管道默认是阻塞的,套接字默认也是阻塞。
阻塞和非阻塞是文件的属性。——O_NONBLOCK
对于标准输入,它就是阻塞的,等待客户输入完成
这的STDIN_FILENO,是标准输入终端描述符;STDOUT_FILENO,是标准输出描述符
这里从标准输入read,其作用是和scanf一样;向标准输出write,其作用是和printf一样的。
最终生成file1.out可执行程序
执行file1.out结果分析
默认bash是前台程序,当调用file1.out启动一个程序时,bash变成了后台程序。此时的前台程序是file1.out。
当file1.out等待用户输入10个字符,并将输入的10个字符输出到终端后,bash从后台程序变成前台程序。而bash也是一直在等待客户输入命令(阻塞)后执行。此时缓冲区中还有字符fuck,所以bash读取缓冲区的字符(遇到回车时即认为输入完毕,而第一次输入时在最后输入了回车键)作为命令执行。
当缓冲区没有数据时,调用sleep,让出CPU
stat filename #查看文件属性
man 2 stat
#include
#include #include int stat(const char * pathname, struct stat *statbuf); int fstat(int fd, struct stat *statbuf); int lstat(const char * pathname, struct stat *statbuf); #include #include int fstatat(int dirfd, const char * pathname, struct stat *statbuf, int flags); struct stat { dev_t st_dev; //文件的设备编号,ID of device containing file ino_t st_ino; //节点 mode_t st_mode; //文件类型和存取权限 nlink_t st_link; uid_t st_uid; gid_t st_gid; dev_t st_rdev; // off_t st_sizt; //文件字节数 blksize_t st_blksize; //块大小(文件系统的I/O缓冲区大小) blkcnt_t st_blocks; //块数 time_t st_atime; time_t st_mtime; time_t st_ctime; };
#include
#include #include #include #include #include int main(int argc, char *argv[]) { struct stat sb; if (argc != 2){ fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } if (lstat(argv[1], &sb) == -1){ perror("lstat"); exit(EXIT_FAILURE); } printf("ID of containing device : [%lx, %lx]\n", (long)major(sb.st_dev), (long)minor(sb.st_dev)); printf("File type: "); switch (sb.st_mode & S_IFMT){ //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"); } printf("I-node number: %ld\n", (long)sb.st_ino); return 0; }
1、access——检测是否具备某种权限
int access(const char * pathname, int mode); //检测当前用户对文件是否具备某种权限
mode——R_OK,W_OK,X_OK,F_OK
2、chmod——修改文件权限
int chmod(const char * filename, int mode);
3、truncate——扩展文件大小
int truncate(const char *path, off_t length); //length为重新设置的空间,比原来大,则扩展;比原来小,则缩小
1、dup——复制文件描述符
int dup(int oldfd);
返回值:新的文件描述符。
调用成功,有两个文件描述符指向同一个文件
2、dup2——复制文件描述符
int dup2(int oldfd, int newfd); //文件描述符的重定向——把newfd定向到了oldfd所指文件
newfd已经指向了一个文件,首先close(newfd),然后调用dup2,而oldfd和newfd都指向了oldfd指向的文件。