UNIX文件I/O

小编 2026-06-24 阅读:1071 评论:0
第一次用markdown语法写博客,写出来的还比较整齐,感觉博客园对序号的支持不是很好,调了一会...

第一次用markdown语法写博客,写出来的还比较整齐,感觉博客园对序号的支持不是很好,调了一会才有了比较满意的效果,还有有哪位知道使用markdown如何插入frame?

这边博客主要说了APUE中文件I/O的主要知识点,并且尝试写了一些代码,都列在了博客中。

UNIX文件I/O

3.1 文件描述符

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读,写一个文件时,使用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
",STDIN_FILENO);         printf("%d
",STDOUT_FILENO);         printf("%d
",STDERR_FILENO);         //printf("%d
",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
",(int)limit.rlim_cur);                  return 0;    }

3.2 open和openat

使用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
",dir_fd);         fd = openat(dir_fd,"test.txt",O_RDONLY);         printf("%d
",fd);                  fd = open("/tmp/test.txt",O_RDWR);         printf("%d
",fd);         int rv = write(fd,"test",4);         printf("%d
",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
",fd);          }

fd参数把open和openat区分开,共有三种可能:

  • path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat就相当于open.
  • path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取。
  • path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在这种情况下,路径名在当前工作目录中获取,openat函数在操作上和open函数类似。

3.3 函数creat

#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
",fd);    fd = creat("/tmp/creatRW.txt",RWRWRW);//umask 0002 -rw-rw-r-- 1 harlan harlan 0  5月 18 21:51 creatRW.txt.    printf("%d
",fd);    return 0;}

3.4 函数close

关闭一个文件是释放该进程加在文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有的打开文件。

3.5 函数lseek

#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
",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
",zero);           int mid = lseek(fd,len/2,SEEK_CUR);                                       printf("Move to the middle of the file %d
",mid);                    }  

关于lseek函数中参数offset的解释与参数whence的值有关。
若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负。
若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负。

3.6 函数read和write

如果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
",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
",(int)byteNumRead);//print result is 7    close(fd);    return 0;}          

3.7 文件共享

内核使用三种数据结构表示打开文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

  • 每个进程在进程表中都有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。于每个文件描述符相关联的是:
  1. 文件描述符标志(close_on_exec);
  2. 指向一个文件表项的指针。
  • 内核为所有打开文件维持一张文件表。每个文件表项包含:
  1. 文件状态标志;
  2. 当前文件偏移量;
  3. 指向该文件V节点表项的指针。
  • 每个打开文件(或设备)都有一个V节点(v-node)结构。

见下图:
UNIX文件I/O

如果两个独立进程各自打开了同一文件,则有下面的关系图:
UNIX文件I/O


3.8 原子操作

pread是一个原子操作,它用来执行定位并执行I/O,pread相当于调用lseek后调用read,但pread与这种调用顺序有区别。

  • 调用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
",(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]='';                                                                      printf("Read %d bytes:%s
",(int)readBytes,buf);                                  printCurOffset(fd);                                                               return 0;                                                                     }    

3.9 函数dup和dup2

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
",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
",fd);                                                           printf("get FL of /etc/passwd %d
",fcntl(fd,F_GETFL));                             printf("get FD of /etc/passwd %d
",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.....................
");                         test_FD_CLOEXEC();                                                        printf("test_dup.....................
");                                test_dup();                                                               printf("test_dup2.....................
");                               test_dup2();                                                              return 0;                                                             }     

3.10 函数fcntl

fcntl函数可以改变已经打开文件的属性。fcntl函数有以下5种功能:

  1. 复制一个已有的文件描述符(cmd=F_DUPFD)
  2. 获取/设置文件描述符标志(cmd=F_GETFD或者F_SETFD)
  3. 获取/设置文件状态标志(cmd=F_GETFL或者F_SETFL)
  4. 获取设置异步i/o所有权(cmd=F_GETOWN或者F_SETOWN)
  5. 或者/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)

下面是几个例子:

#include <fcntl.h>                                                               #include <stdio.h>                                                               #include <sys/stat.h>                                                            void printFD(int fd)                                                             {                                                                                    printf("The fd num is %d
",fd);                                             }                                                                                                                                                                 void printFDFlags(int fd,int fdflags)                                            {                                                                                    printf("The file description flags of  %d is %d
",fd,fdflags);              }                                                                                                                                                                 void printFLFlags(int fd,int flflags)                                            {                                                                                    printf("The file status flags of  %d is %d
",fd,flflags);                   }                                                                                                                                                                                                                                                  void F_DUPFD_fcntl()                                                             {                                                                                    printf("F_DUPFD_fcntl()..........
");                                           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()..........
");                                       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()..........
");                                       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;                                                                                                                }                                                                                                                                                   


作者:HarlanC

博客地址:http://www.cnblogs.com/harlanc/
个人博客: http://www.harlancn.me/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接

如果觉的博主写的可以,收到您的赞会是很大的动力,如果您觉的不好,您可以投反对票,但麻烦您留言写下问题在哪里,这样才能共同进步。谢谢!

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

上一篇:竞选主席 下一篇:网上报警中心110咨询
热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表