文件描述符

目录

C语言文件操作

fopen

fwrite

fread

fclose

系统文件描述符

预备知识

open

write

read

文件描述符的理解

文件描述符与stdin/stdout/stderr 的对应关系

文件描述符是什么


C语言文件操作

再C语言/C++中,默认会打开三个流:

  1. 标准输入(stdin)

  2. 标准输出(stdout)

  3. 标准错误(stderr)

NAME
       stdin, stdout, stderr - standard I/O streams
​
SYNOPSIS
       #include 
​
       extern FILE *stdin;
       extern FILE *stdout;
       extern FILE *stderr;
  • 其中标准输入对应的是键盘、标准输出对应的是显示器、标准错误也是显示器。

下面我们看看一下 C语言 的文件操作!

fopen

再从C语言中,如果想要打开一个文件,那么使用一个函数 fopen:

NAME
       fopen, fdopen, freopen - stream open functions
​
SYNOPSIS
       #include 
​
       FILE *fopen(const char *path, const char *mode);
  • 该函数第一个参数是 path 表示想要打开的文件的路径。

  • 而第二个参数是打开的模式:

    打开的模式有六种:
    r:表示以读方式打开,且从头开始读
    r+:表示以读写的方式打开,也是从头开始
    w:表示以写的方式打开,如果没有对应的文件,那么就会创建一个,而且每一次打开都会清空文件内容,从头开始写。
    w+:表示以读写的方式打开,如果没有对应文件,就会被创建,同时每一次也会清空文件,从头开始。
    a:表示以写的方式打开。但是写是以追加的方式写,如果没有对应的文件,就会被创建。
    a+:表示以读写的方式打开,但是写是追加的写,如果没有对应的文件,那么就会被创建,如果该文件是读的话,那么就     是以起始位置开始读,如果是写的话,那么就是以结尾位置开始写。

  • 返回值,如果打开失败,那么就会返回空

下面测试一下 fopen:

void test1()
{
  FILE* fd = fopen("log.txt", "w");
  if(fd == NULL)
  {
    perror("fopen");
  }
  else 
  {
    printf("文件打开成功\n");
  }
}
  • 上面我们以 w 的方式打开,那么如果没有的话,就会创建一个。

  • 那么 log.txt 该文件会打开哪里的 log.txt 文件呢?

  • 当前目录下:可是什么是当前目录呢?当前目录就是该程序运行的时候的目录,就叫当前目录。

  • 如果这里写的是绝对路径的话,那么就按照绝对路径的方式打开。

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
文件打开成功
[lxy@hecs-165234 linux103]$ ll
total 20
-rw-rw-r-- 1 lxy lxy    0 Oct  6 16:08 log.txt
-rw-rw-r-- 1 lxy lxy   76 Oct  6 15:40 makefile
-rwxrwxr-x 1 lxy lxy 8488 Oct  6 16:04 myfile
-rw-rw-r-- 1 lxy lxy  215 Oct  6 16:04 myfile.c
  • 我们之前是没有这个文件的。

  • 但是当程序运行后,显示打开成功,而我们的当前目录下确实多了了一个 log.txt 文件。

下面我们以 r 的方式打开,但是i打开之前我们先删除掉 log.txt。

void test1()
{
  FILE* fd = fopen("log.txt", "r");
  if(fd == NULL)
  {
    perror("fopen");
  }
  else 
  {
    printf("文件打开成功\n");
  }
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fopen: No such file or directory
  • 这里我们以 r 的方式打开失败了。

  • 因为 r 方式打开的话,没有对应的文件并不会创建对应的文件。

fwrite

下面我们再看一下 write 接口:

NAME
       fread, fwrite - binary stream input/output
​
SYNOPSIS
       #include 
​
       size_t fwrite(const void *ptr, size_t size, size_t nmemb,
                     FILE *stream);
  • ptr 表示想要写入的数据。

  • size 表示想要写入多少数据。

  • nmemb 表示每一个数据的大小,也就是字节。(假设现在是 int 类型,当前有 3 个 int 类型,每个int 是4 个字节,那么就是 size = 3, nmemb = 4);

  • stream 表示是打开的 FILE 指针。

下面我们以 w 的方式大开文件,然后写入文件:

void test2()
{
  FILE* fd = fopen("log.txt", "w");
  if(fd == NULL)
  {
    perror("fopen");
  }
  // 打开成功
  const char* str = "hello write\n";
  fwrite(str, strlen(str), 1, fd);
}
  • 上面我们写了一个字符串,写到 log.txt 文件当中。

  • 但是我们写的字符串多加了一个 \n ?为什么?

  • 因为写入文件后,文件里面的内容入过没有分隔符分开的话,那么就粘再一起了。

  • 而且将 /n 写进入的话,打印出来还会换行,所以看起来清晰一点。

  • 那我们为什么不将/0写进去呢?实际上文件是不认识 /0 的,所以写入也是乱码!

结果:

[lxy@hecs-165234 linux103]$ cat log.txt 
hello write

下面我们再看一下,我们多调用几次该函数,会多写入吗?

[lxy@hecs-165234 linux103]$ cat log.txt 
hello write
//这里我们的 log.txt 里面只有一行数据,我们多调用几次
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
//查看 log.txt 
[lxy@hecs-165234 linux103]$ cat log.txt 
hello write
  • 上面我们发现多调用了几次也同样只有一行数据,为什么?

  • 因为 w 是每次打开都会清空文件按,然后从头开始写,那么我们要是不想清空呢?

  • a 方式打开

下面我们追加的写入数据:

void test2()
{
  FILE* fd = fopen("log.txt", "a");
  if(fd == NULL)
  {
    perror("fopen");
  }
  // 打开成功
  const char* str = "hello write\n";
  fwrite(str, strlen(str), 1, fd);
}

下面我们多调用几次,看一下是会不会被清空后重写呢?

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ ./myfile 
[lxy@hecs-165234 linux103]$ cat log.txt 
hello write
hello write
hello write
hello write
hello write
  • 这里看到我们全部都写再后面了,并没有被清空。

fread

NAME
       fread, fwrite - binary stream input/output
​
SYNOPSIS
       #include 
​
       size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr 是表示读出来的数据放到哪里。

  • size 表示读取多少个数据。

  • nmemb 表示每个数据大小是多少。

  • stream 表示从那个 FILE 指针里面读。

但是这次我们并不想使用这个接口,我们使用一个其他的接口,这个接口和 fwrite 基本一样,所以换一个接口使用,C语言中还有很多不同类型的接口

fgets

NAME
       fgetc, fgets, getc, getchar, gets, ungetc - input of characters and strings
​
SYNOPSIS
       #include 
​
       char *fgets(char *s, int size, FILE *stream);
  • 上面是一批接口,但是使用起来都是差不多的,所以这里也只是介绍一下。

  • fgets 表示每一次都是行读取。

  • s 表示读取到 s 的缓冲区。

  • size 表示读取多少字节。

下面打开我们刚才写入的 log.txt 文件,然后按行读取里面的数据:

void test3()
{
  FILE* fd = fopen("log.txt", "r");
  if(fd == NULL)
  {
    perror("fopen");
  }
  // 打开成功
  char buffer[64] = {0};
  while(1)
  {
    if(fgets(buffer, sizeof(buffer) - 1, fd) == NULL) break;
    printf("%s", buffer);
  }
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
hello write
hello write
hello write
hello write
hello write

那么我们下面如果将我们的 myfile.c 改一下。

我们通过命令行参数传入一个文件,然后打印这个文件会怎么样子?

int main(int argc, char* argv[])
{
  if(argc != 2)
  {
    printf("Usage: %s + file_name\n", argv[0]);
    exit(1);
  }
  FILE* fd = fopen(argv[1], "r");
  char buffer[128] = {0};
  while(1)
  {
    if(fgets(buffer, sizeof(buffer) - 1, fd) == NULL) break;
    printf("%s", buffer);
  }
  return 0;
}
  • 这样我们就可以通过命令行参数将我们的文件内容打印出来。

结果:

[lxy@hecs-165234 linux103]$ ./myfile log.txt 
hello write
hello write
hello write
hello write
hello write
​[lxy@hecs-165234 linux103]$ ./myfile makefile
myfile:myfile.c
        gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
        rm -rf myfile
  • 这不仅可以打印 log.txt 还可以打印其他的文件,和 cat 命令很像,所以这就实现了一个我们自己的 cat命令。

C语言的文件惭怍就介绍到这里吗,下面我们看系统提供的函数。

同时我们再看一下C语言的文件操作与系统的文件操作有什么区别。

fclose

NAME
       fclose - close a stream
​
SYNOPSIS
       #include 
​
       int fclose(FILE *fp);
  • 这个函数就是关闭打开的文件描述符的,所以不介绍了。

系统文件描述符

  • 再介绍系统的文件描述符之前,我们先做一下预备知识!

预备知识

  • 再系统中创建一个文件,当该文件为空的时候,那么该文件有大小吗?

  • 回答:有大小!

  • 实际上,文件不仅只有文件的内容。文件 = 内容 + 属性。

  • 既然我们现在知道,一个文件等于内容加属性,那么我们现在需要对文件操作,那么会有哪些操作?

  • 回答:既然文件等于内容加属性,那么对文件的操作当然也就是对文件的内容和属性的操作。

  • 文件时放在哪里的?回答:磁盘。

  • 那么磁盘是什么?回答:外设。

  • 那么访问外设普通人能访问吗?回答:不能。

  • 那么谁才能访问外设?回答:操作系统。

  • 既然只有操作系统可以访问外设,那么我们想要访问一个文件怎么办?回答当然需要系统为我们提供接口!

  • 那么我们要访问文件的步骤是什么?回答:代码 -> 编译 -> .exe -> 运行 -> 访问文件。

  • 那么最后是谁再访问文件?回答:进程再访问文件。

  • 所以,访问文件实际上是进程再替我们访问文件,帮我们读写修改!

  • 显示器是硬件吗?是的!

  • 那么向显示器输入和向磁盘写入一样吗?一样!

  • 所以我们发现向硬件写入都是一样的,本质上都是向文件里面写入!

  • 所以这就是 Linux 中的一切皆文件。

open

NAME
       open, creat - open and possibly create a file or device
​
SYNOPSIS
       #include 
       #include 
       #include 
​
       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
  • open 就是系统调用接口,用来打开一个文件。

  • 首先我们看第一个 open 函数。

  • 他的第一个参数是 pathname ,表示就是一个文件名/路径。

  • 而第二个参数是一个 flag 也就是一格标志位,那么这个标志位是干什么的?

    实际上这个标志位是一格位图!
    可以通过一些标志来控制传入的值的各个比特位!
    下面看一下常用的标志位!
    再 Linux 中一般的标志位都是宏定义!
    O_WRONLY: 表示以读写方式打开
    O_RDONLY: 表示以读方式打开
    O_APPEND: 表示以追加的方式来打开
    O_CREAT: 表示如果没有对应的文件,就创建
    O_TRUNC: 表示每次打开都清空文件

  • 返回值是一个 int 类型,这里先不说,先使用!如果返回值小于0,表示打开失败!

下面我们先用第一个函数测试一下:

void test4()
{
  int fd = open("log.txt", O_WRONLY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
}
  • 当前我们目录下有 log.txt 文件,然后我们以 读写方式打开。

  • 如果成功的话,那么我们再打印一下 fd 也就是返回值。

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
  • 上面打开成功了,看到返回值是 3

下面我们删掉 log.txt 文件后再试一下:

[lxy@hecs-165234 linux103]$ ./myfile 
open: No such file or directory
  • 这里就打开失败了,但是为什么呢?因为我们只是以读写方式打开。

  • 如果没有对应的文件就会报错!那么我们想要没有文件就创建呢?

  • 我们 open 的时候再将 O_CREAT标志位设置到 flag 中。

void test4()
{
  //int fd = open("log.txt", O_WRONLY);
  int fd = open("log.txt", O_WRONLY|O_CREAT);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
}

下面我们继续打开以读写方式打开 log.txt 文件,入锅没有文件的话,就创建:

[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ ll
total 24
---sr-s--T 1 lxy lxy    0 Oct  6 17:42 log.txt
-rw-rw-r-- 1 lxy lxy   76 Oct  6 15:40 makefile
-rw-rw-r-- 1 lxy lxy  358 Oct  6 17:27 mycat.c
-rwxrwxr-x 1 lxy lxy 8872 Oct  6 17:40 myfile
-rw-rw-r-- 1 lxy lxy 1010 Oct  6 17:40 myfile.c
  • 这里看到确实,没有 log.txt 文件就创建了,但是为什么,创建的 log.txt 文件那么奇怪呢?

  • log.txt 文件的权限和下面的普通文件的权限不一样呢?

  • 这是因为入锅我们直接这样创建出来的话,这个文件的权限就是随机的,所以我们需要使用第二个 open

int open(const char *pathname, int flags, mode_t mode);
  • 第二个 open 比第一个多了一格参数,而前面两个参数是一样的!

  • 那么最后一个参数是干什么的呢?

  • 最后一格参数表示权限!

下面我们试一下使用第二个函数打开文件:

void test5()
{
  int fd = open("log.txt", O_WRONLY|O_CREAT,0666);
  if(fd < 0)
  {
    // 打开失败
    perror("open");
    exit(1);
  }
  // 打开成功
   
}
  • 这里打开 log.txt 但是没有 log.txt 文件,所以会创建。

  • 我们传入的权限是 666 - rw-rw-rw-,这里的 0 表示八进制

  • 那么我们看一下是否为能创建好!

结果:

[lxy@hecs-165234 linux103]$ ll
total 24
-rw-rw-r-- 1 lxy lxy    0 Oct  6 18:06 log.txt
-rw-rw-r-- 1 lxy lxy   76 Oct  6 15:40 makefile
-rw-rw-r-- 1 lxy lxy  358 Oct  6 17:27 mycat.c
-rwxrwxr-x 1 lxy lxy 8936 Oct  6 18:05 myfile
-rw-rw-r-- 1 lxy lxy 1266 Oct  6 18:05 myfile.c
  • 这里创建的是 rw-rw-rw- 为什么?

  • 因为有权限掩码 0002,所以我们写入的权限也是受到权限掩码的影响。

  • 那么要怎么办呢?

  • 我们可以调用 umask 函数然后将当前进程的权限掩码修改为 0 ,这样我们设置的是多少,那么创建的文件的权限就是多少。

umask

NAME
       umask - set file mode creation mask
​
SYNOPSIS
       #include 
       #include 
​
       mode_t umask(mode_t mask);

下面看一下修改后的函数:

void test5()
{
  umask(0);
  int fd = open("log.txt", O_WRONLY|O_CREAT,0666);
  if(fd < 0)
  {
    // 打开失败
    perror("open");
    exit(1);
  }
  // 打开成功
   
}

结果:

[lxy@hecs-165234 linux103]$ ll
total 24
-rw-rw-rw- 1 lxy lxy    0 Oct  6 18:16 log.txt
-rw-rw-r-- 1 lxy lxy   76 Oct  6 15:40 makefile
-rw-rw-r-- 1 lxy lxy  358 Oct  6 17:27 mycat.c
-rwxrwxr-x 1 lxy lxy 8984 Oct  6 18:15 myfile
-rw-rw-r-- 1 lxy lxy 1278 Oct  6 18:15 myfile.c
  • 这里看到创建的文件的权限就是我们设置的了。

write

NAME
       write - write to a file descriptor
​
SYNOPSIS
       #include 
​
       ssize_t write(int fd, const void *buf, size_t count);
  • 首先是第一个参数 fd 表示open 打开的文件描述符!

  • 第二个参数想要写入的数据。

  • 第三个表示想要写入多少字节。

  • 返回值实际写入了多少字节。

下面我们再向文件里面写内容:

void test4()
{
  //int fd = open("log.txt", O_WRONLY);
  int fd = open("log.txt", O_WRONLY|O_CREAT);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
  //写入数据
  const char* str = "hello write\n";
  write(fd, str, strlen(str));
}

这里将 str 写入到 log.txt 文件中。

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ cat log.txt 
hello write
  • 成功写入了,没问题!

下面我们将 str 改一下:

void test4()
{
  //int fd = open("log.txt", O_WRONLY);
  int fd = open("log.txt", O_WRONLY|O_CREAT);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
  //写入数据
  const char* str = "nihao";
  write(fd, str, strlen(str));
}

这里将 str 改成 nihao 后我们再写入试一下:

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ cat log.txt 
nihao write
  • 这里看到把前面几个字符给覆盖了,后面的没有修改。

  • 也就是写的时候重头开始写。

  • 那么我们要是想要每一次打开都清空后开始写呢?我们需要将 O_TRUNC 设置进标志位!

void test4()
{
  umask(0);
  int fd = open("log.txt", O_WRONLY|O_TRUNC,0666 );
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
  //写入数据
  const char* str = "nihao";
  write(fd, str, strlen(str));
}
  • 现在我们执行一下,看是否会清空后重写!

结果:

[lxy@hecs-165234 linux103]$ cat log.txt 
nihao

那么我们想要追加呢?我们只需要添加 O_APPEND

void test4()
{
  umask(0);
  int fd = open("log.txt", O_WRONLY|O_APPEND,0666 );
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
  //写入数据
  const char* str = "nihao\n";
  write(fd, str, strlen(str));
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
[lxy@hecs-165234 linux103]$ ./myfile 
fd: 3
​
结果
[lxy@hecs-165234 linux103]$ cat log.txt 
nihaonihao
nihao
nihao
nihao
nihao

上面就可以解决追加的问题了。

read

NAME
       read - read from a file descriptor
​
SYNOPSIS
       #include 
​
       ssize_t read(int fd, void *buf, size_t count);
  • fd 表示打开的文件描述符

  • buf 表示读取到那个缓冲区

  • count 表示读取多少个字节

  • 返回值表示实际读取到多少字节

下面读取 log.txt 文件,实际上入锅只是读取的话,那么可以使用 open 的第一个函数:

void test6()
{
  int fd = open("log.txt", O_RDONLY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  char buffer[64] = {0};
  ssize_t s = read(fd, buffer, sizeof(buffer) - 1);
  if(s > 0)
  {
    // 读取成功
    buffer[s] = '\0';
  }
  printf("%s", buffer);
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
nihaonihao
nihao
nihao
nihao
nihao
nihao

这里看到读取成功了。

文件描述符的理解

  • 上面我们一直再 open 打开文件,然后回返回一个 int 类型的值,而这个值就叫做文件描述符!

  • 那么文件描述符究竟是什么呢?

文件描述符与stdin/stdout/stderr 的对应关系

我们下面多打开一些文件,看一下返回值:

void test7()
{
  int fd1 = open("log.txt1", O_WRONLY|O_CREAT, 0666);
  printf("fd1: %d\n", fd1);
  int fd2 = open("log.txt2", O_WRONLY|O_CREAT, 0666);
  printf("fd2: %d\n", fd2);
  int fd3 = open("log.txt3", O_WRONLY|O_CREAT, 0666);
  printf("fd3: %d\n", fd3);
  int fd4 = open("log.txt4", O_WRONLY|O_CREAT, 0666);
  printf("fd4: %d\n", fd4);
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
fd1: 3
fd2: 4
fd3: 5
fd4: 6
  • 这里从 3 开始打印,然后依次递增。

  • 再系统中,我们打开对文件操作是什么来操作文件的?

  • 回答:进程来操作!

  • 进程操作文件要怎么操作,直接再磁盘中能操作吗?

  • 回答:不可以,需要加载到内存中才能操作!

  • 那么一个进程可以对多个文件操作吗?

  • 回答:可以!

  • 那么也就是内存中很多被打开的文件,既然有很多被打开的文件,那么操作系统要不要管理这些文件?

  • 回答:需要管理!

  • 怎么管理?

  • 回答:先描述,再组织!(将文件用数据结构来描述,然后可以使用双链表链接起来)。

那么我们看到我们的文件描述符是从 3 开始增长的,不绝对奇怪吗?

为什么是从3开始呢?前面的文件描述符呢?

  • 实际上,前面我们已经说过了,C语言/C++回默认打开三个流:标准输入、标准输出、标准错误。

  • 标准输入就是 0 号文件描述符。

  • 标准输出就是 1 号文件描述符。

  • 标准错误就是 2 号文件描述符。

那么我们如何验证呢?

我们可以像stdout 和 2 号文件描述符里面写,看是否会打印到显示器上,然后我们向stdin 和 2 号文件描述符里读,看是否是键盘,实际上 stderr 也是显示器,所以这里就不测试了。

void test8()
{
  char buffer[64] = {0};
  //向标准输出中写
  const char* str = "hello stdout\n";
  fprintf(stdout,"%s", str);
  write(2, str, strlen(str));
}
  • 这里 fprintf 向 标准输出中写,也就是显示器。

  • wreit 向 2 号文件描述符中写。

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
hello stdout
hello stdout
  • 这里看到都写到显示器上了,所以说明 stdout 就是 2 号文件描述符

void test8()
{
//  // 向stdin中读
  fgets(buffer, sizeof(buffer) - 1, stdin);
  printf("%s", buffer);
  int s = read(2, buffer, sizeof(buffer) - 1);
  if(s > 0)
  {
    buffer[s] = '\0';
  }
  printf("%s", buffer);
}

结果:

[lxy@hecs-165234 linux103]$ ./myfile 
hello fgets
hello fgets
hello read
hello read

下面的 stderr 就不验证了,但是我们已经知道他们是意义对应的!

文件描述符是什么

现在有一个疑问,为什么是从 0 开始递增呢?

我们这个 0 开始的像什么呢?

  • 实际上 有一个 struct file 结构体,里面存的是打开的文件的信息。

  • 而PCB里面有一个struct file 的数组,里面放的就是struct file 这个与下标一一对应。

  • 只要有下标就可以找到对应的文件信息,只要有信息,那么就可以对对应的文件进行操作。

所以实际上文件描述符就是数组的下标,那么每一次都是如何设置文件描述符的?

我们将 0 号文件描述符关闭后,我们再打开一个文件,看一下文件描述符是多少:

void test1()
{
  int fd = open("log.txt", O_WRONLY|O_CREAT,0666 );
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
}

结果:

[lxy@hecs-165234 linux104]$ ./myfile 
fd: 3
  • 首先是我们没有关闭任何一个文件描述符,那么下面我们关闭掉 0号文件描述符

  • 上面打开的文件的文件描述符就是 3,因为 0、1、2 都已经有了

void test1()
{
  // 关闭 0 号文件描述符
  close(0);
​
  int fd = open("log.txt", O_WRONLY|O_CREAT,0666 );
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
}

结果:

[lxy@hecs-165234 linux104]$ ./myfile 
fd: 0
  • 这里看到的文件描述符是 0,而我们也关闭了 0 号文件描述符

  • 那么我们下面关掉2号文件描述符,看一下打开的文件描述符是多少

void test1()
{
  // 关闭 0 号文件描述符
  close(2);
​
  int fd = open("log.txt", O_WRONLY|O_CREAT,0666 );
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  // 打开成功
  printf("fd: %d\n", fd);
}

结果:

[lxy@hecs-165234 linux104]$ ./myfile 
fd: 2
  • 这里看到的文件描述符是 2

  • 也是我们关掉的文件描述符。

结论:

  • 我们每一次打开文件,那么该文件返回值的文件描述符就是最小没有被占用的文件描述符

你可能感兴趣的:(linux)