《Linux/Unix系统编程手册》读书笔记2


《Linux/Unix系统编程手册》读书笔记 目录

第5章:

主要介绍了文件I/O更深入的一些内容。

原子操作,将一个系统调用所要完成的作为一个的操作,一次性执行;这样可以避免竞争状态(两个或多个共享资源的进程或线程的运行结果是一个无法预期的顺序)。

以独占方式创建一个文件:对文件是否存在的检查和创建文件属于同一个原子操作。防止新建文件的时候因为检查文件是否存在和新建文件之间发生中断(而其他进程也在新建相同文件名的文件),导致两个进程都认为自己是文件的创建者。

向文件尾部追加数据:将文件的偏移量的移动与数据的写操作属于同一个原子操作。防止多个进程同时往同一个文件尾部添加数据导致数据混乱。

 

,对一个打开的文件描述符执行一系列的操作。

 #include <fcntl.h>
 
  fcntl( fd,  cmd, ...);

fd为文件描述符,cmd是决定具体操作,第三个参数(可选)用来设置为不同的类型。

cmd参数(部分),具体查看man手册:

F_DUPFD 复制文件描述符
F_GETFD 获取文件描述符
F_SET_FD 设置文件描述符
F_GETFL 获取文件访问模式和状态标志
F_SETFL 设置文件访问模式和状态标志

 

文件描述符与打开文件之间的关系:多个文件描述符可以指向同一个打开文件。他们的关系如下

文件描述符表、打开文件表和i-node表。打开文件表的条目成为打开文件句柄(open file handle)。

 

,复制一个打开的文件描述符oldfd,并返回新的描述符。

,复制oldfd指定的文件描述符,返回newfd参数指定的描述符。

,参数与dup2()相同,添加了flags,用于修改系统调用行为。

 #include <unistd.h>
 
  dup( 
  dup2( oldfd,  
  dup3( oldfd,  newfd, int flags);

成功调用返回新的文件描述符,失败返回-1。

 

,在指定参数所指定的位置进行文件I/O操作,但不改变文件的偏移量。

 #include <unistd.h>
 
 ssize_t pread( fd,  * 
 ssize_t pwrite( fd,   *buf, size_t count, off_t offset);

fd为文件描述符,buf为缓冲区, count为缓冲区字节数, offset为偏移量。

pread()成功调用返回读取的字节数,失败返回-1

pwrite()成功调用返回写入的字节数,失败返回-1

 

 

练习:

5-1,请使用标准文件I/O系统调用(open()和lseek())和off_t数据类型修改程序清单5-3中的程序。将宏_FILE_OFFSET_BITS的值设置为64进行编译,并测试该程序是否能够成功创建一个大文件。

_FILE_OFFSET_BITS 64 #include <sys/stat.h> #include <fcntl.h> #include main( argc, * (argc != || strcmp(argv[], ) == usageErr(, argv[ fd = open(argv[], O_RDWR | O_CREAT, S_IRUSR | (fd == - errExit( off = atoll(argv[ (lseek(fd, off, SEEK_SET) == - errExit( (write(fd, , ) == - errExit( } View Code

测试结果:

 lancelot@debian:~/Code/tlpi$ ./large_file largefile 
 lancelot@debian:~/Code/tlpi$  - -rw-------  lancelot lancelot   4月   : largefile

5-2,编写一个程序,使用O_APPEND标志并以写方式打开一个已存在的文件,且将文件偏移量置于起始位置,再写入数据。数据会显示在文件中的哪个位置?为什么?

#include <sys/stat.h> #include <ctype.h> #include <fcntl.h> #include main( argc, * (argc != || strcmp(argv[], ) == usageErr(, argv[ fd = open(argv[], O_RDWR | O_APPEND, S_IRUSR | (fd == - errExit( off = lseek(fd, (off == - errExit( numWritten = write(fd, , (numWritten == - errExit( } View Code

测试结果:

 lancelot@debian:~/Code/tlpi$     
 lancelot@debian:~/Code/tlpi$ ./ lancelot@debian:~/Code/tlpi$     
 Kevin Durant

 

5-3,本习题的设计目标在于展示为何以O_APPEND标志打开文件来保障操作的原子性是必要的。请编写一程序,可接收多达3个命令行参数:

$ automic_append filename num-bytes [x]

该程序应打开制定的文件,然后以每次调用write()写入一个字节的方式,向文件尾部追加num-bytes个字节。缺省情况下,程序使用O_APPEND标志打开文件,但若存在第三个命令行参数(x),那么打开文件时将不再使用O_APPEND标志,代之以调用write()前调用lseek(fd, 0, SEEK_END)。同时运行该程序的两个实例,不带x参数,将100万字节写入同一个文件:

$ automic_append f1 1000000 & automic_append f1 1000000

重复上述操作,将数据写入另一个文件,但运行时加入x参数

$ automic_append f2 1000000 x & automic_append f2 1000000 x

使用ls -l命令检查文件f1和f2的大小, 并解释两文件大小不同的原因。

#include <sys/stat.h> #include <fcntl.h> #include main( argc, * flags = O_RDWR | (argc < || strcmp(argv[], ) == usageErr( (argc != flags = flags | numBytes = getInt(argv[], , fd = open(argv[], flags, S_IRUSR | (fd == - errExit( (i = ; i < numBytes; ++ (argc > && argv[] == (lseek(fd, , SEEK_END) == - errExit( (write(fd, , ) != fatal( } View Code

测试结果:

 lancelot@debian:~/Code/tlpi$  - -rw-------  lancelot lancelot   4月   : -rw-------  lancelot lancelot   4月   : f2

 5-4,使用fcntl()和close()来实现dup()和dup2()。务必牢记dup2()需要处理的一种特殊情况,即oldfd与newfd相等。这时,应检查oldfd是否有效,测试fcntl(oldfd, F_GETFL)是否成功就能达到这一目标。若oldfd无效,则dup2()将返回-1,并将errno置为EBADF。

#include <sys/stat.h> #include <fcntl.h> #include Dup( Dup2( oldfd, ((fd = open(, O_RDONLY)) == - errExit( newfd = (newfd == - errExit( printf( printf( scanf(, & newfd = (newfd == - errExit( printf( Dup( newfd = fcntl(oldfd, F_DUPFD, oldfd+ Dup2( oldfd, fd = (fcntl(oldfd, F_GETFL) == - errno = - (oldfd == (fcntl(newfd, F_GETFL) != - newfd = (newfd != newfd = } View Code

测试结果:

 lancelot@debian:~/Code/tlpi$ ./ old fd is , new fd is 
 Input the new fd:
 old fd is , new fd is 

 

相关内容