《UNIX环境高级编程》---3.文件I/O

一、打开、创建文件、关闭文件

  1. 文件描述符:一个非负整数,范围是0~OPEN_MAX-1。内核用它来标识进程正在访问的文件。当进程创建时,默认为它打开了3个文件描述符,它们都链接向终端:

    • 0: 标准输入
    • 1: 标准输出
    • 2: 标准错误输出

    通常我们应该使用STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO来替代这三个幻数,从而提高可读性。这三个常量位于<unistd.h>中。

2. openopenat函数:打开文件

```
#include<fcntl.h>
int open(const char* path,int oflag,.../*mode_t mode*/);
int openat(int fd,const char*path,int oflag,.../*mode_t mode */);
```

- 参数:
    - `path`:要打开或者创建文件的名字
    - `oflag`:用于指定函数的操作行为,定义在`<fcntl.h>`中。
    -  `mode`:文件访问权限。文件访问权限常量在 `<sys/stat.h>` 中定义,有下列九个:
        - `S_IRUSR`:用户读
        - `S_IWUSR`:用户写
        - `S_IXUSR`:用户执行
        - `S_IRGRP`:组读
        - `S_IWGRP`:组写
        - `S_IXGRP`:组执行
        - `S_IROTH`:其他读
        - `S_IWOTH`:其他写
        - `S_IXOTH`:其他执行 

- 对于`openat`函数,被打开的文件名由`fd`和`path`共同决定:
    **- 如果`path`指定的是绝对路径,此时`fd`被忽略。`openat`等价于`open`**
    **- 如果`path`指定的是相对路径名,则`fd`是一个目录打开的文件描述符。被打开的文件的绝对路径由该`fd`描述符对应的目录加上`path`组合而成**
    **- 如果`path`是一个相对路径名,而`fd`是常量`AT_FDCWD`,则`path`相对于当前工作目录。被打开文件在当前工作目录中查找。**

- 返回值:
    - 成功:返回文件描述符。
    - 失败:返回 -1

由 `open/openat` 返回的文件描述符一定是最小的未使用的描述符数字。

3. creat函数:创建一个新文件

```
#include<fcntl.h>
int creat(const char*path,mode_t mode);
```
该函数等价于`open(path,O_WRONLY|O_CREAT|O_TRUNC,mode)`。注意:
- 它以只写方式打开,因此若要读取该文件,则必须先关闭,然后重新以读方式打开。
- 若文件已存在则将文件截断为0。
-

4. close函数:关闭文件

```
#include<unistd.h>
int close(int fd);
```

- 参数:
    - `fd`:待关闭文件的文件描述符
- 返回值:
    - 成功:返回 0
    - 失败:返回 -1

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

二、定位、读、写文件

  1. lseek函数:设置打开文件的偏移量

    
    #include<unistd.h>
    
    off_t lseek(int fd, off_t offset,int whence);
    • 参数:

      • fd:打开的文件的文件描述符
      • whence:必须是 SEEK_SETSEEK_CURSEEK_END三个常量之一
      • offset
        • 如果 whenceSEEK_SET,则将该文件的偏移量设置为距离文件开始处offset个字节
        • 如果 whenceSEEK_CUR,则将该文件的偏移量设置为当前值加上offset个字节,offset可正,可负
        • 如果 whenceSEEK_END,则将该文件的偏移量设置为文件长度加上offset个字节,offset可正,可负
    • 返回值:
      • 成功: 返回新的文件偏移量
      • 失败:返回 -1

    每个打开的文件都有一个与其关联的“当前文件偏移量”。它通常是个非负整数,用于度量从文件开始处计算的字节数。通常读、写操作都从当前文件偏移量处开始,并且使偏移量增加所读写的字节数。注意:

    • 打开一个文件时,除非指定O_APPEND选项,否则系统默认将该偏移量设为0
    • 如果文件描述符指定的是一个管道、FIFO、或者网络套接字,则无法设定当前文件偏移量,则lseek将返回 -1 ,并且将 errno 设置为 ESPIPE
    • 对于普通文件,其当前文件偏移量必须是非负值。但是某些设备运行负的偏移量出现。因此比较lseek的结果时,不能根据它小于0 就认为出错。要根据是否等于 -1 来判断是否出错。
    • lseek 并不会引起任何 I/O 操作,lseek仅仅将当前文件的偏移量记录在内核中。
    • 当前文件偏移量可以大于文件的当前长度。此时对该文件的下一次写操作将家常该文件,并且在文件中构成一个空洞。空洞中的内容位于文件中但是没有被写过,其字节被读取时都被读为0
/*
    test lseek function for std file io
*/
#include "apue.h"
int main(void)
{
    if (lseek(STDIN_FILENO,0,SEEK_CUR) == -1) /*there is must be is equal operation*/
        printf("cannot seek\n");
    else
        printf("seek ok\n");
    exit(0);
}
  1. read函数:读取文件内容

    
    #include<unistd.h>
    
    ssize_t read(int fd,void *buf,size_t nbytes);
    • 参数:

      • fd:打开的文件的文件描述符
      • buf:存放读取内容的缓冲区的地址(由程序员手动分配)
      • nbytes:期望读到的字节数
    • 返回值:
      • 成功:返回读到的字节数,若已到文件尾则返回 0
      • 失败:返回 -1

    读操作从文件的当前偏移量开始,在成功返回之前,文件的当前偏移量会增加实际读到的字节数。有多种情况可能导致实际读到的字节数少于期望读到的字节数:

    • 读普通文件时,在读到期望字节数之前到达了文件尾端
    • 当从终端设备读时,通常一次最多读取一行(终端默认是行缓冲的)
    • 当从网络读时,网络中的缓存机制可能造成返回值小于期望读到的字节数
    • 当从管道或者FIFO读时,若管道包含的字节少于所需的数量,则 read只返回实际可用的字节数
    • 当从某些面向记录的设备(如磁带)中读取时,一次最多返回一条记录
    • 当一个信号造成中断,而已读了部分数据时。
  2. write函数:想文件写数据
    
    #include<unistd.h>
    
    ssize_t write(int fd,const void *buf,size_t nbytes);
    • 参数:

      • fd:打开的文件的文件描述符
      • buf:存放待写的数据内容的缓冲区的地址(由程序员手动分配)
      • nbytes:期望写入文件的字节数
    • 返回值:
      • 成功:返回已写的字节数
      • 失败:返回 -1

    write的返回值通常都是与nbytes相同。否则表示出错。write出错的一个常见原因是磁盘写满,或者超过了一个给定进行的文件长度限制

    对于普通文件,写操作从文件的当前偏移量处开始。如果打开文件时指定了O_APPEND选项,则每次写操作之前,都会将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。

  3. 测试lseek,read,write
    
    #include <stdio.h>
    
    #include<unistd.h>
    
    #include<fcntl.h>
    
    #include<string.h>
    
    #include<errno.h>
    
    void test_read(int fd,void* read_buffer,ssize_t len)
    {
      int read_num;
       read_num=read(fd,read_buffer,len);
       if(read_num<0)
        {
           printf("\tread error!\n");
            fprintf(stderr,"\tBeause:%s\n",strerror(errno));
        }else
        {
           printf("\tread num:%d(expexted read :%d)\n",read_num,len);
       }
    }
    void test_write(int fd,void*write_buffer,ssize_t len)
    {
      int write_num;
       write_num=write(fd,write_buffer,len);
       if(write_num<0)
       {
          printf("\twrite error!\n");
          fprintf(stderr,"\tBeause:%s\n",strerror(errno));
        }else
       {
          printf("\twrite num:%d(expexted write :%d)\n",write_num,len);
       }
    }
    
    void test_lseek(int fd,off_t offset,int loc)
    {
      int seek_ok;
       seek_ok=lseek(fd,offset,loc);
       if(-1==seek_ok)
       {
          printf("\tlseek error!\n");
          fprintf(stderr,"\tBeause:%s\n",strerror(errno));
        }else
       {
            printf("\tlseek ok.New offset is:%d\n",seek_ok);
        }
    }
    
    int main(int argc, char *argv[])
    {
       int fd;
        char read_buffer[20];
       char write_buffer[10];
       strcpy(write_buffer,"123456789");
    
        fd=openat(AT_FDCWD,"test",O_RDWR|O_TRUNC);
    
       printf("File is empty:\n");
        test_read(fd,read_buffer,20);
       test_write(fd,write_buffer,10);
       test_read(fd,read_buffer,20);
       printf("Lseek to begin:\n");
      test_lseek(fd,0,SEEK_SET);
       test_read(fd,read_buffer,20);
       printf("Lseek to end+10:\n");
        test_lseek(fd,10,SEEK_END);
       test_read(fd,read_buffer,20);
       test_write(fd,write_buffer,10);
       test_read(fd,read_buffer,20);
       printf("Lseek to begin:\n");
       test_lseek(fd,0,SEEK_SET);
       test_read(fd,read_buffer,20);
       test_read(fd,read_buffer,20);
       return 0;
    }

    测试序列为:

    • 开始文件为空,所以读取20个字节的read只读取0
    • 写入文件10个字节
    • 读取文件。此时读和写共享一个当前文件偏移。而且当前文件偏移被write置于文件结尾。此时读取0个字节
    • 执行lseek将当前文件偏移量重置到文件开头,返回0(新的文件偏移量)
    • 读取文件,只能读取10个字节(因为文件此时就10个字节)
    • 执行lseek将文件偏移量放到文件末尾之后的10个字节,返回20(新的文件偏移量)
    • 读取文件。此时当前文件偏移被置于文件结尾。此时读取0个字节
    • 写入文件10个字节
    • 读取文件。此时当前文件偏移被write置于文件结尾。此时读取0个字节
    • 执行lseek将当前文件偏移量重置到文件开头,返回0(新的文件偏移量)
    • 读取文件,读取20个字节(因为文件结尾的偏移是30个字节)
    • 读取文件,只能读取10个字节(因为文件结尾的偏移是30个字节)

三、 原子操作、同步、复制、修改文件描述符

  1. 内核使用三种数据结构描述打开文件。它们之间的关系决定了一个进程与另一个进程在打开的文件之间的相互影响。

    • 内核为每个进程分配一个进程表项(所有进程表项构成进程表),进程表中都有一个打开的文件描述符表。每个文件描述符占用一项,其内容为:

      • 文件描述符标志
      • 指向一个文件表项的指针
    • 内核为每个打开的文件分配一个文件表项(所有的文件表项构成文件表)。每个文件表项的内容包括:
      • 文件状态标志(读、写、添写、同步和阻塞等)
      • 当前文件偏移量
      • 指向该文件 v 结点表项的指针
    • 每个打开的文件或者设备都有一个 v 结点结构。 v 结点结构的内容包括:
      • 文件类型和对此文件进行各种操作函数的指针。
      • 对于大多数文件, v 结点还包含了该文件的 i 结点。

    这些信息都是在打开文件时从磁盘读入内存的。如 i 结点包含了文件的所有者、文件长度、指向文件实际数据在磁盘上所在位置的指针等等。 v 结点结构和 i 结点结构实际上代表了文件的实体。

    现在假设进程 A 打开文件 file1,返回文件描述符 3;进程 B 也打开文件 file2,返回文件描述符 4:

    • 内核在文件表上新增两个表项:

      • 这两个文件表项指向同一个 v 结点表项
      • 进程 A 、B 各自的文件描述符表项分别指向这两个文件表项;
    • 对文件的操作结果:
      • 进程 A 每次 write 之后,进程 A 对应的文件表项的当前文件偏移量即增加所写入的字节数。

        • 若这导致当前文件偏移量超过当前文件长度,则修改 i 节点的当前文件长度,设为当前文件偏移量
      • 如果进程 B 用 O_APPEND标志打开一个文件,在相应标志也设置到进程 B 对于的文件表项的文件状态标志中。
        • 每次进程 B 对具有追加写标志的文件执行写操作时,文件表项中的当前文件偏移量首先被置为 i 结点中的文件长度。
      • 若进程 B 用 lseek 定位到文件当前的尾端,则进程 B 对应的文件表项的当前文件偏移量设置为 i 结点中的当前长度
      • lseek 函数只是修改文件表项中的当前文件偏移量,不进行任何 I/O 操作

    可能一个进程中有多个文件描述符指向同一个文件表项。

  2. 原子操作:
    • 追加一个文件时,不能通过lseek到末尾然后write。要用O_APPEND选项打开文件,然后直接write

      • 通过lseek到末尾然后write时,如果多个进程同时执行这两个操作,则会引起竞争条件
      • 通过 O_APPEND选项打开文件,然后直接write时,内核每一次在写操作之前,都会将进程的当前偏移量设置到文件的末尾,于是就不需要执行lseek定位操作
    • pread/pwrite可以执行原子性的定位读/定位写
    • O_CREAT|O_EXCL选项打开文件时,可以原子性的检查文件是否存在和创建文件这两个操作。
  3. pread/pwrite:原子定位读和原子定位写
    
    #include<unistd.h>
    
    ssize_t pread(int fd,void*buf,size_t nbytes,off_t offset);
    ssize_t pwrite(int fd,const void*buf,size_t nbytes,off_t offset);
    • 参数:

      • fd:打开的文件描述符
      • buf:读出数据存放的缓冲区/ 写到文件的数据的缓冲区
      • nbytes:预期读出/写入文件的字节数
      • offset:从文件指定偏移量开始执行read/write
    • 返回:
      • 成功:读到的字节数/已写的字节数
      • 失败: -1

    调用pread相当于先调用lseek再调用read.但是调用pread时,无法中断其定位和读操作,并且不更新当前文件偏移量;调用pwrite相当于先调用lseek再调用write.但是调用pwrite时,无法中断其定位和写操作,并且不更新当前文件偏移量

  4. dup/dup2:复制一个现有的文件描述符:
    
    #include<unistd.h>
    
    int dup(int fd);
    int dup2(int fd,int fd2);
    • 参数:

      • fd:被复制的文件描述符(已被打开)
      • fd2:指定的新的文件描述符(待生成)

        -返回值:

      • 成功: 返回新的文件描述符
      • 失败: 返回 -1

    对于dup函数,返回的新的文件描述符一定是当前可用的文件描述符中最小的数字。对于dup2函数:

    • 如果 fd2已经是被打开的文件描述符且不等于fd,则先将其关闭,然后再打开(注意关闭再打开是一个原子操作)
    • 如果 fd2等于fd,则直接返回fd2(也等于fd),而不作任何操作

    任何情况下,这个返回的新的文明描述符与参数fd共享同一个文件表项(因此文件状态标志以及文件偏移量都会共享)。 任何情况下,这个返回的新的文明描述符的close-on-exec标志总是被清除

  5. UNIX操作系统在内核中设有缓冲区,大多数磁盘 I/O 都通过缓冲区进行。当我们想文件写入数据时,内核通常都首先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘。这种方式称为延迟写。
    • 当内核需要重用缓冲区来存方其他数据时,它会把所有延迟写的数据库写入磁盘
    • 你也可以调用下列函数来显式的将延迟写的数据库写入磁盘
    
    #include<unistd.h>
    
    int fsync(int fd);
    int fdatasync(int fd);
    void sync(void);
    • 参数(前两个函数):

      • fd:指定的打开的文件描述符
    • 返回值(前两个函数):
      • 成功:返回 0
      • 失败: 返回 -1

    区别:

    • sync:将所有修改过的块缓冲区排入写队列,然后返回,它并不等待时机写磁盘结束
    • fsync:只对由fd指定的单个文件起作用,等待写磁盘操作结束才返回
    • fdatasync:只对由fd指定的单个文件起作用,等待写磁盘操作结束才返回,但是它只影响文件的数据部分(fsync会同步更新文件的属性)

    update 守护进程会周期性的调用sync函数。命令sync也会调用sync函数

  6. fcntl函数:改变已经打开的文件的属性
    
    #include<fcntl.h>
    
    int fcntl(int fd,int cmd,.../* int arg */);
    • 参数:

      • fd:已打开文件的描述符
      • cmd:有下列若干种:
        • F_DUPEF常量:复制文件描述符 fd。新文件描述符作为函数值返回。它是尚未打开的个描述符中大于或等于arg中的最小值。新文件描述符与fd共享同一个文件表项,但是新描述符有自己的一套文件描述符标志,其中FD_CLOEXEC文件描述符标志被清除
        • F_DUPFD_CLOEXEC常量:复制文件描述符。新文件描述符作为函数值返回。它是尚未打开的个描述符中大于或等于arg中的最小值。新文件描述符与fd共享同一个文件表项,但是新描述符有自己的一套文件描述符标志,其中FD_CLOEXEC文件描述符标志被设置
        • F_GETFD常量:对应于fd的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志FD_CLOEXEC
        • F_SETFD常量:设置fd的文件描述符标志为arg
        • F_GETFL常量:返回fd的文件状态标志。文件状态标志必须首先用屏蔽字 O_ACCMODE 取得访问方式位,

          然后与O_RDONLYO_WRONLYO_RDWRO_EXECO_SEARCH比较

          (这5个值互斥,且并不是各占1位)。剩下的还有:O_APPENDO_NONBLOCKO_SYNC

          O_DSYNCO_RSYNCF_ASYNCO_ASYNC

        • F_SETFL常量:设置fd的文件状态标志为 arg。可以更改的标志是:

          O_APPENDO_NONBLOCKO_SYNCO_DSYNCO_RSYNCF_ASYNCO_ASYNC

        • F_GETOWN常量:获取当前接收 SIGIOSIGURG信号的进程 ID或者进程组 ID
        • F_SETOWN常量:设置当前接收 SIGIOSIGURG信号的进程 ID或者进程组 IDarg。若 arg是个正值,则设定进程 ID;若 arg是个负值,则设定进程组ID
        • F_GETLKF_SETLKF_SETLKW:获取/设置文件记录锁
      • arg:依赖于具体的命令
    • 返回值:
      • 成功: 依赖于具体的命令
      • 失败: 返回 -1
    
    #include <stdio.h>
    
    #include<fcntl.h>
    
    #include<unistd.h>
    
    #include<string.h>
    
    #include<errno.h>
    
    void print_error(int fd,const char* action,int result)
    {
       if(result==-1)
       {
           printf("\t %s on fd(%d) error:beause %s!\n",action,fd,strerror(errno));
       }
    }
    
    void test_get_fd(int fd)
    {
        printf("\tget_fd on fd(%d):",fd);
        int result;
        result=fcntl(fd,F_GETFD);
        print_error(fd,"F_GETFD",result);
        if(result!=-1)
           printf("return:%d !\n",result);
    
    }
    void test_set_fd(int fd, int flag)
    {
       printf("\tset_fd on fd(%d) of flag(%d):",fd,flag);
      int result;
       result=fcntl(fd,F_SETFD,flag);
      print_error(fd,"F_SETFD",result);
      if(result!=-1)
            printf("set_fd ok !\n");
    }
    int test_dup_fd(int fd,int min_fd)
    {
        printf("\tdup_fd on fd(%d),set min_fd(%d),:",fd,min_fd);
       int result;
       result=fcntl(fd,F_DUPFD,min_fd);
       print_error(fd,"F_DUPFD",result);
      if(result!=-1)
            printf("return:%d !\n",result);
      return result;
    }
    int test_dup_exec_fd(int fd,int min_fd)
    {
       printf("\tdup_exec_fd on fd(%d),set min_fd(%d),:",fd,min_fd);
      int result;
      result=fcntl(fd,F_DUPFD_CLOEXEC,min_fd);
      print_error(fd,"F_DUPFD_CLOEXEC",result);
      if(result!=-1)
         printf("return:%d !\n",result);
      return result;
    }
    void test_get_fl(int fd)
    {
       printf("\tget_fl on fd(%d):",fd);
       int result;
      result=fcntl(fd,F_GETFL);
      print_error(fd,"F_GETFL",result);
      if(result!=-1)
      {
         printf("F_GETFL on fd(%d) has ",fd);
            if(result&O_APPEND) printf("\tO_APPEND;");
            if(result&O_NONBLOCK) printf("\tO_NONBLOCK;");
            if(result&O_SYNC) printf("\tO_SYNC;");
            if(result&O_DSYNC) printf("\tO_DSYNC;");
            if(result&O_RSYNC) printf("\tO_RSYNC;");
            if(result&O_FSYNC) printf("\tO_FSYNC;");
            if(result&O_ASYNC) printf("\thas O_ASYNC;");
            if((result&O_ACCMODE)==O_RDONLY)printf("\tO_RDONLY;");
           if((result&O_ACCMODE)==O_WRONLY)printf("\thas O_WRONLY;");
           if((result&O_ACCMODE)==O_RDWR)printf("\tO_RDWR;");
           printf("\n");
      }
    }
    void test_set_fl(int fd,int flag)
    {
        printf("\tset_fl on fd(%d) of flag(%d):",fd,flag);
       int result;
       result=fcntl(fd,F_SETFL,flag);
      print_error(fd,"F_SETFL",result);
       if(result!=-1)
         printf("set_fl ok !\n");
    }
    void test_get_own(int fd)
    {
       printf("\tget_own on fd(%d):",fd);
      int result;
      result=fcntl(fd,F_GETOWN);
      print_error(fd,"F_GETOWN",result);
      if(result!=-1)
               printf("return:%d !\n",result);
    }
    void test_set_own(int fd,int pid)
    {
        printf("\tset_own on fd(%d) of pid(%d):",fd,pid);
       int result;
       result=fcntl(fd,F_SETOWN,pid);
       print_error(fd,"F_SETOWN",result);
      if(result!=-1)
             printf("set_own ok !\n");
    }
    int main(int argc, char *argv[])
    {
        int fd;
      fd=openat(AT_FDCWD,"test.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
      printf("Test dup:\n");
      test_get_fd(test_dup_fd(fd,10));
      test_get_fd(test_dup_fd(fd,0));
      test_get_fd(test_dup_exec_fd(fd,10));
      test_get_fd(test_dup_exec_fd(fd,0));
     printf("Test set_get_fd:\n");
     test_get_fd(fd);
      test_set_fd(fd,~FD_CLOEXEC);
      test_get_fd(fd);
      test_set_fd(fd,FD_CLOEXEC);
      test_get_fd(fd);
      printf("Test set_get_fl:\n");
      test_get_fl(fd);
        test_set_fl(fd,O_RDWR);
     test_get_fl(fd);
        test_set_fl(fd,O_RDONLY|O_NONBLOCK);
     test_get_fl(fd);
     printf("Test set_get own:\n");
     test_get_own(fd);
     test_set_fl(fd,1);
     test_get_own(fd);
        return 0;
    }

    注意:

    • Linux 下,不支持文件状态标志: F_EXEC与F_SEARCH
    • (result&O_ACCMODE)==O_RDONLY 表达式中, &优先级较低
    • F_SETFL命令:当文件读打开时,你无法将文件状态标志修改为O_WRONLYO_WRWR这两种中任何一个。你只能修改:O_APPENDO_NONBLOCKO_SYNCO_DSYNCO_RSYNCF_ASYNCO_ASYNC等标志
  7. /dev/fd目录:该目录下是名为0、1、2等的文件。打开文件/dev/fd/n等效于复制描述符(假定描述符n是打开的)
    • fd=open("/dev/fd/0",mod)fd和文件描述符0共享同一个文件表项。
    • 大多数系统忽略mod参数
    • 在 Linux 操作系统上, /dev/fd/0是个例外,它是个底层物理文件的符号链接。因此在它上面调用creat 会导致底层文件被截断
时间: 2024-10-29 05:12:08

《UNIX环境高级编程》---3.文件I/O的相关文章

Unix环境高级编程(一)文件I/O

Unix系统中大多数文件I/O只需用到五个函数:open.read.write.lseek.close.本章说介绍的I/O是不带缓冲的,即:每个read和write都调用内核中的一个系统调用.不是ISO C的组成部分.对于内核而言,所有打开的文件都通过文件描述符引用. 在<unistd.h>中定义三个标准的文件描述符: STDIN_FILENO 标准输入 STDOUT_FILENO 标准输出 STDERR_FILENO 标准出错输出 具体函数描述:在<fcntl.h>头文件下 in

Unix环境高级编程(二)文件和目录

本章主要介绍的是文件结构及目录.重点是通过stat函数获取文件的结构信息,然后是文件目录及其遍历.学完本章后,编写了一个输出给的目录下的文件信息的程序. 首先是包含在<sys/stat.h>文件下的stat.fstat.lstat三个函数,三个函数的原型如下: int stat(const char *path, struct stat *buf);int fstat(int fd, struct stat *buf);int lstat(const char *path, struct st

《Unix环境高级编程》文件I/O

对于内核而言,所有打开的文件都通过文件描述符引用. #include"apue.h"//3-1 测试能否对标准输入设置偏移量 int main(int argc,char *argv[]) { if(lseek(STDIN_FILENO,0,SEEK_CUR) == -1) printf("can't seek"); else printf("seek OK!"); exit(0); }

【UNIX环境高级编程】文件 IO 操作 - 基础函数 open close creat lseek write read 详解

博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271 一. 文件打开关闭操作相关函数介绍 1. open 函数 (1) open 函数简介 open 函数解析 : -- 函数定义 : #include <fcntl.h> int open(const char *path, int oflag, ...); -- 函数作用 : 打开或者创建一个文件; -- 返回值 : 打开文件成功, 返回文件描述符; 如果失败, 返回

UNIX环境高级编程笔记之文件I/O

一.看图说话 一图胜过千言,看图! 二.唠一唠 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是“哇”这种很吃惊的表情.其实大概三年前,那会大三,我就买了这本书,也看过一些,但好像没有留下什么印象,今天再看,依然觉得像新的一样.很大的原因我想是一直以来都在用windows(用windows做开发为什么学不到真正的技术,我想大家都懂的),当然知识结构不完整,学习能力这些就不说了.所以,对于那些致力于想在Linux下做开发的人来说,

《Unix环境高级编程》读书笔记 第3章-文件I/O

1. 引言 Unix系统的大多数文件I/O只需用到5个函数:open.read.write.lseek以及close 本章描述的函数经常被称为不带缓冲的I/O.术语不带缓冲指的是在用户的进程中对其不会自动缓冲,每个read和write都调用内核中的一个系统调用.但是,所有磁盘I/O都要经过内核的块缓存区(也称为内核的缓冲区高速缓存).唯一例外的是对原始磁盘设备的I/O. 2. 文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数,其变化范围是0~OPEN_MAX

unix环境高级编程(第三版)中apue.h文件的配置问题

最近刚开始学习unix环境高级编程(第三版),其中有个作者自己写的apue.h文件,在这归总下相应的配置方法,希望对有需要的朋友们有所帮助 首先http://www.apuebook.com/code3e.html 上去下载相应的压缩包,注意自己书的版本. 下载完成之后,鉴于大多数朋友学习linux都是基于虚拟机的,所以顺便附上虚拟机与本地主机传输文件的方式 首先下载SSH Secure Shell 这个工具,然后直接点击quick connect, 弹出如下界面,输入虚拟机的ip地址,和登录用

《UNIX环境高级编程》读书笔记 —— 文件 I/O

一.打开或创建一个文件 #include <fcntl.h> int open(const char *pathname, int oflag, .../*mode_t mode*/); 返回值:若成功则返回文件描述符,若出错则返回-1 oflag选项: O_RDONLY O_WRONLY O_RDWR 以上三个常量中必须指定一个,且只能指定一个. 以下常量都是可选的: O_APPED     每次写时追加到文件尾 O_CREAT     若文件不存在,则创建 O_EXCL      若同时指

Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字 . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APU

Linux - Unix环境高级编程(第三版) 代码编译

Unix环境高级编程(第三版) 代码编译 本文地址:http://blog.csdn.net/caroline_wendy 时间:2014.10.2 1. 下载代码:http://www.apuebook.com/code3e.html 2. 安装依赖库:sudo apt-get install libbsd-dev  3. 进入下载目录make 4. 复制头文件和动态链接库 sudo cp ./include/apue.h /usr/include/ sudo cp ./lib/libapue