本文共 22689 字,大约阅读时间需要 75 分钟。
第一次用markdown语法写博客,写出来的还比较整齐,感觉博客园对序号的支持不是很好,调了一会才有了比较满意的效果,还有有哪位知道使用markdown如何插入frame?
这边博客主要说了APUE中文件I/O的主要知识点,并且尝试写了一些代码,都列在了博客中。
对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读,写一个文件时,使用open或者creat返回的文件描述符标识该文件,并将其作为参数传递给read或者write。
我们把标准输入(0),标准输出(1)和标准错误(2)文件描述符替换为符号常量STDIN_FINENO,STDOUT_FILENO,STDERR_FILENO,系统支持的最大文件描述符数量可以由以下方式获取:#include#include #include #include int main(void) { /***************************** * print file descriptors for * standard input * standard output * standard err * ***************************/ printf("%d\n",STDIN_FILENO); printf("%d\n",STDOUT_FILENO); printf("%d\n",STDERR_FILENO); //printf("%d\n",OPEN_MAX); OPEN_MAX is deprecated /************************************* * how to get the OPEN_MAX value ************************************/ struct rlimit limit; if(getrlimit(RLIMIT_NOFILE,&limit)==-1) perror("getrlimit"); printf("getrlimit=%d\n",(int)limit.rlim_cur); return 0; }
使用open和openat可以打开或者创建一个文件,下面是使用open和openat的实例:
#include#include #define RWRWRW (S_IRUSR |S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) int main(void) { /*****************************************************************/ //int open(const char*path,int oflag,.../*mode_t mode*/); //int openat(int fd,const char*path,int oflag,.../*mode_t mode*/) /****************************************************************/ /*One of the following five flags must be specified: // O_RDONLY read only // O_WRONLY write only // O_RDWR read and write // most implementations define O_RDONLY as 0,O_WRONLY as 1, // O_RDWR as 2. // O_EXEC execute-only // O_SEARCH search-only\ ****************************************************************/ /*The following ones are optional: * O_APPEND * O_CLOEXEC * O_CREAT * O_DIRECTORY * O_EXEL * O_NOCTTY * O_NOFOLLOW * O_NONBLOCK * O_SYNC * O_TRUNC * O_TTY_INIT * O_DSYNC * O_RSYNC * */ int fd = openat(0,"/tmp/test.txt",O_WRONLY|O_CREAT,RWRWRW);//0 is ignored if path is absolute path. close(fd); int dir_fd = open("/tmp",O_RDONLY); printf("%d\n",dir_fd); fd = openat(dir_fd,"test.txt",O_RDONLY); printf("%d\n",fd); fd = open("/tmp/test.txt",O_RDWR); printf("%d\n",fd); int rv = write(fd,"test",4); printf("%d\n",rv); /* *Test if a file exists */ fd = open("/tmp/test.txt",O_CREAT|O_EXCL); printf("The file exists,so the open result is %d\n",fd); }
fd参数把open和openat区分开,共有三种可能:
#include#include #define RWRWRW (S_IRUSR |S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)#define RRR (S_IRUSR| S_IRGRP|S_IROTH)int main(void){ /******************************************** * int creat(const char *path,mode_t mode); * is equal to * open(path,O_WRONLY|OCREAT|O_TRUNC,mode) * *****************************************/ int fd = creat("/tmp/creat.txt",RRR);//-r--r--r-- 1 harlan harlan 0 5月 18 21:49 creat.txt. printf("%d\n",fd); fd = creat("/tmp/creatRW.txt",RWRWRW);//umask 0002 -rw-rw-r-- 1 harlan harlan 0 5月 18 21:51 creatRW.txt. printf("%d\n",fd); return 0;}
关闭一个文件是释放该进程加在文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有的打开文件。
#include#include int main(void) { /* *off_t lseek(int fd,off_t offset,int whence); * */ int fd = open("/etc/passwd",O_RDONLY); int len = lseek(fd,0,SEEK_END); printf("The file /etc/passwd 's length is %d\n",len); //back to the beginning of the file int zero = lseek(fd,0,SEEK_SET); printf("The offset of the beginning of the file is %d\n",zero); int mid = lseek(fd,len/2,SEEK_CUR); printf("Move to the middle of the file %d\n",mid); }
关于lseek函数中参数offset的解释与参数whence的值有关。
若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。 若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负。 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负。如果read成功,则返回读到的字节数。如已达到文件的尾端,则返回0。
#include#include #include #include #define RRR (S_IRUSR| S_IRGRP|S_IROTH)int main(void){ /*************************************************** * POSIX.1 * ssize_t read(int fd,void *buf,size_t nbytes) * ISO_C * int read(int fd,char *buf,unsigned nbytes); **************************************************/ int fd = creat("/tmp/read.txt",RRR); int byteNumWrite = write(fd,"abcdefg",7); printf("The string \"abcdefg\" is write to read.txt,the real string length wrote to the file is %d\n",byteNumWrite);//result is 7 close(fd); fd = open("/tmp/read.txt",O_RDONLY); char *buf = (char*)malloc(sizeof(char)*8); ssize_t byteNumRead = read(fd,buf,8); printf("The bytes read from read.txt is %d\n",(int)byteNumRead);//print result is 7 close(fd); return 0;}
内核使用三种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。
见下图:
如果两个独立进程各自打开了同一文件,则有下面的关系图:
pread是一个原子操作,它用来执行定位并执行I/O,pread相当于调用lseek后调用read,但pread与这种调用顺序有区别。
#include#include #include #include #define RRR (S_IRUSR| S_IRGRP|S_IROTH) off_t getCurOffset(int fd)t { return lseek(fd,0,SEEK_CUR); } void printCurOffset(int fd)* { printf("the current file offset is %d\n",(int)getCurOffset(fd)); } int main(void) { /***************************************************************** *ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset); *pread will not update the current file offset,see the following examples *ssize_t pwrite(int fd,const void * buf,size_t nbytes,off_t offset); *******************************************************************/ int fd = creat("/tmp/pread.txt",RRR); int writeBytes = write(fd,"abcdefghij",10); close(fd); fd = open("/tmp/pread.txt",O_RDONLY); printCurOffset(fd); char *buf = (char*)malloc(5); ssize_t readBytes = pread(fd,buf,4,2); buf[4]='\0'; printf("Read %d bytes:%s\n",(int)readBytes,buf); printCurOffset(fd); return 0; }
dup和dup2都用来复制一个现有的文件描述符。对于dup2,可以自己指定新描述符的值,如果新描述符的值已经打开,则现将其关闭。如果新旧描述符值相等,则直接返回旧描述符值。如果不等,新描述符的FD_CLOEXEC文件描述符标志就会被清除(见下例)。
#includeint main(int argc,char *argv[]){ if(2!=argc) { printf("The parameter number is not correct!"); } //get the file descriptor int fd = atoi(argv[1]); char buf[100]={0}; int NumReadBytes = read(fd,buf,5); printf("The number of byte %d: %s\n",NumReadBytes,buf);}
上面代码命名为read.c,执行下面的命令生成read可执行文件
gcc read.c -o read
#include#include #include #include #define RRR (S_IRUSR| S_IRGRP|S_IROTH) #define RWRWRW (S_IRUSR|S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) void printfdInfo(int fd) { printf("fd num %d\n",fd); printf("get FL of /etc/passwd %d\n",fcntl(fd,F_GETFL)); printf("get FD of /etc/passwd %d\n",fcntl(fd,F_GETFD)); } void execRead(int fd) { char fdString[4]={0}; sprintf(fdString,"%d",fd); int pid = 0; int status = 0; if((pid=fork()) != 0)//father process { //wait for child process finish waitpid(pid,&status,0); } else//child process { if(execl("read",fdString,NULL)<0)//fd is open in child process perror("issue read failed."); } } void test_FD_CLOEXEC() { //open success int fd = open("/etc/passwd",O_RDONLY); printfdInfo(fd);//fd is 0 execRead(fd);//read success fcntl(fd,F_SETFD,FD_CLOEXEC); printfdInfo(fd);//fd is 1,the fd is closed in child process. execRead(fd);//read failed close(fd); } void test_dup() { int fd = open("/etc/passwd",O_RDONLY); int dupfd = dup(fd); //FD and FL are all the same. printfdInfo(fd); printfdInfo(dupfd); } void test_dup2() { int fd = open("/etc/passwd",O_RDONLY); fcntl(fd,F_SETFD,FD_CLOEXEC); int newfd = dup2(fd,13); execRead(fd);//read failed,fd is closed. execRead(newfd);//the FD_CLOEXEC is cleared. close(fd); close(newfd); } int main(void) { printf("test_FD_CLOEXEC.....................\n"); test_FD_CLOEXEC(); printf("test_dup.....................\n"); test_dup(); printf("test_dup2.....................\n"); test_dup2(); return 0; }
fcntl函数可以改变已经打开文件的属性。fcntl函数有以下5种功能:
下面是几个例子:
#include#include #include void printFD(int fd) { printf("The fd num is %d\n",fd); } void printFDFlags(int fd,int fdflags) { printf("The file description flags of %d is %d\n",fd,fdflags); } void printFLFlags(int fd,int flflags) { printf("The file status flags of %d is %d\n",fd,flflags); } void F_DUPFD_fcntl() { printf("F_DUPFD_fcntl()..........\n"); int fd = open("/etc/passwd",O_RDONLY); int newfd = fcntl(fd,F_DUPFD,0);//equal to dup(fd) printFD(fd); printFD(newfd); close(fd); close(newfd); } /*void F_DUPFD_CLOEXEC_fcntl() { int fd = open("/etc/passwd",O_RDONLY); int newfd = fcntl(fd,F_DUPFD_CLOEXEC,0); int fdI = fcntl(fd,F_GETFD,0); printFDI(fd,fdI); int newfdI = fcntl(newfd,F_GETFD,0); printFDI(newfd,newfdI); }*/ void F_SET_FD_GET_FD_fcntl() { printf("F_SETFD_GETFD_fcntl()..........\n"); int fd = open("/etc/passwd",O_RDONLY); int fdflag = fcntl(fd,F_GETFD,0); printFDFlags(fd,fdflag); fcntl(fd,F_SETFD,1); fdflag = fcntl(fd,F_GETFD,0); printFDFlags(fd,fdflag); } /********************************************************* * note this command can change only the O_APPEND, * O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags *******************************************************/ #define RWRWRW (S_IRUSR|S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) void F_SETFL_GETFL_fcntl() { printf("F_SETFL_GETFL_fcntl()..........\n"); int fd = open("/etc/passwd",O_RDONLY); int flflag = fcntl(fd,F_GETFL); printFLFlags(fd,flflag); fcntl(fd,F_SETFL,O_APPEND); flflag = fcntl(fd,F_GETFL); printFLFlags(fd,flflag); } int main(void) { /**********************************************/ /* int fcntl(int fd,int cmd,...*//* int arg*///) /**********************************************/ F_DUPFD_fcntl(); //F_DUPFD_CLOEXEC_fcntl(); F_SET_FD_GET_FD_fcntl(); F_SETFL_GETFL_fcntl(); return 0; }
作者: 博客地址: 个人博客: 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 如果觉的博主写的可以,收到您的赞会是很大的动力,如果您觉的不好,您可以投反对票,但麻烦您留言写下问题在哪里,这样才能共同进步。谢谢!