第一次用 markdown 语法写博客,写出来的还比较整齐,感觉博客园对序号的支持不是很好, 调了一会才有了比较满意的效果,还有有哪位知道使用 markdown 如何插入 frame?
这边博客主要说了 APUE 中文件 I/O 的主要知识点,并且尝试写了一些代码,都列在了博客中。
对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读,写一个文件时,使用 open 或者 creat 返回的文件描述符标识该文件,并将其作为参数传递给 read 或者 write。 我们把标准输入 (0),标准输出(1) 和标准错误 (2) 文件描述符替换为符号常量 STDIN_FINENO,STDOUT_FILENO,STDERR_FILENO, 系统支持的最大文件描述符数量可以由以下方式获取:
- #include <unistd.h>
- #include <stdio.h>
- #include <sys/resource.h>
- #include <errno.h>
- 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 <fcntl.h>
- #include <stdio.h>
- #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 <stdio.h>
- #include <fcntl.h>
- #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 <stdio.h>
- #include <fcntl.h>
- 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 <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #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 <unistd.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #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 文件描述符标志就会被清除(见下例)。
- #include <stdio.h>
- int 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 <fcntl.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <stdio.h>
- #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 <fcntl.h>
- #include <stdio.h>
- #include <sys/stat.h>
- 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;
- }
来源: http://www.cnblogs.com/harlanc/p/6906142.html