3.9 i/o efficiency
在重复的读取文件中的数据的时候,每次读取的数据量大小如果接近磁盘的block_size的话,效率是比较高的。
3.13 sync, fsync, fdatasync
1. Sync调用时,只是告知kernel对所有的磁盘缓冲中修改过的页面要被调度写回磁盘,但sync返回时,并不意味着写操作已经完成。那样的话sync可能会等很长时间。
还有一个sync命令,他也会调用sync函数。
2. Fsync(int fd)只对一个file descriptor进行该操作,并且返回时,所有写操作已经完成。
3. fdatasync(int fd)只对一个文件的一部分数据进行该操作。
系统多用一个守护进程来定期调用sync。
3.14 fcntl函数
#include <fcntl.h>
int fcntl(int filedes, int cmd, ... /* int arg */ );
Returns: depends on cmd if OK (see following), 1 on error
功能:
The fcntl function is used for five different purposes.
1.Duplicate an existing descriptor (cmd = F_DUPFD)
2.Get/set file descriptor flags (cmd = F_GETFD or F_SETFD)
3.Get/set file status flags (cmd = F_GETFL or F_SETFL)
4.Get/set asynchronous I/O ownership (cmd = F_GETOWN or F_SETOWN)
5.Get/set record locks (cmd = F_GETLK, F_SETLK, or F_SETLKW)
1.File descriptor的flags中的close-on-exec flag: 我们在进程A中打开了文件f, 当文件f的file descriptor的close-on-exec flag被设置的时候,当我们执行exec系统调用时,进程A的代码段,数据段等都被新的程序代替,而且进程A中打开的文件也会被关闭。如果close-on-exec没有设置,在exec后,旧进程打开的文件就不会被关闭,即file descriptor依然有效,好处是一些daemon经常要fork然后exec一些子进程,这样如果不设置close-on-exec,这些子进程都会共享父进程的文件。
2.File status flag包括:
|
O_RDONLY |
open for reading only |
|
O_WRONLY |
open for writing only |
|
O_RDWR |
open for reading and writing |
|
O_APPEND |
append on each write |
|
O_NONBLOCK |
Non-blocking mode |
|
O_SYNC |
wait for writes to complete (data and attributes) |
|
O_DSYNC |
wait for writes to complete (data only) |
|
O_RSYNC |
synchronize reads and writes |
|
O_FSYNC |
wait for writes to complete (FreeBSD and Mac OS X only) |
|
O_ASYNC |
asynchronous I/O (FreeBSD and Mac OS X only) |
例子:读取一个file descriptor的status flag并打印出来。
$ ./a.out 0 < /dev/tty read only$ ./a.out 1 > temp.foo (输出被重定向到temp.foo中)
$ cat temp.foo write only $ ./a.out 2 2>>temp.foo write only, append $ ./a.out 5 5<>temp.foo read write 上述例子的代码:
Figure 3.10. Print file flags for specified descriptor
#include "apue.h"#include <fcntl.h>intmain(int argc, char *argv[]){ int val; if (argc != 2) err_quit("usage: a.out <descriptor#>"); if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0) err_sys("fcntl error for fd %d", atoi(argv[1])); switch (val & O_ACCMODE) { case O_RDONLY: printf("read only"); break; case O_WRONLY: printf("write only"); break; case O_RDWR: printf("read write"); break; default: err_dump("unknown access mode"); } if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking");#if defined(O_SYNC) if (val & O_SYNC) printf(", synchronous writes");#endif#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) if (val & O_FSYNC) printf(", synchronous writes");#endif putchar('\n'); exit(0);}
Linux ext2不遵从O_SYNC的定义
O_SYNC在linux ext2 file system上并不能起到作用,即如果以这个模式打开文件,我们调用write后,write返回后,并不能保证数据已经写到了磁盘上,而仅仅是将该数据放入了一个将要被写到磁盘上的队列中。因此对使用O_SYNC和不使用 O_SYNC打开的文件进行write操作,在耗时上没什么区别,这个我也测试过。
而使用sync(), sync(fd)的效果接近,即可以保证数据写到了磁盘上了。
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#define BUFFSIZE 4096
int main()
{
int n=0;
char buf[BUFFSIZE];
#if 0
int flags = fcntl( STDOUT_FILENO, F_GETFL, 0 );
if( flags < 0 )
{
printf("get file STDOUT_FILENO flag error!\n ");
return -1;
}
flags |= O_SYNC;
int ret = fcntl( STDOUT_FILENO, F_SETFL, flags );
if( ret < 0 )
{
printf("set flag error on file STDOUT_FILENO\n");
return -1;
}
#endif
while( (n = read( STDIN_FILENO, buf, BUFFSIZE )) > 0 )
{
if( write( STDOUT_FILENO, buf, n ) != n )
{
printf("write error\n");
break;
}
}
// fsync(STDOUT_FILENO);
// sync();
return 0;
}
上面代码中,使用#if 0包括的部分就是设置O_SYNC的代码段,是否设置O_SYNC效果是一样的。且fsync()和sync()的效果也一样。不过都比O_SYNC耗时多。
3.15 ioctl 函数
Ioctl函数的操作可以分为几类:
|
Category |
Constant names |
Header |
Number of ioctls |
|
disk labels |
DIOxxx |
<sys/disklabel.h> |
6 |
|
file I/O |
FIOxxx |
<sys/filio.h> |
9 |
|
mag tape I/O |
MTIOxxx |
<sys/mtio.h> |
11 |
|
socket I/O |
SIOxxx |
<sys/sockio.h> |
60 |
|
terminal I/O |
TIOxxx |
<sys/ttycom.h> |
44 |
3.16 /dev/fd/xxx
打开一个/dev/fd/n文件,如果该文件已经被打开,就类似于调用dup()。好处是,为stdio等提供了一个文件路径名的方式来定位。


