qidao123.com技术社区-IT企服评测·应用市场

标题: 【Linux】文件描述符 - fd [打印本页]

作者: 光之使者    时间: 2024-8-20 05:47
标题: 【Linux】文件描述符 - fd

1. open 接口介绍

利用 man open 指令查察手册:
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. int open(const char *pathname, int flags);
  5. int open(const char *pathname, int flags, mode_t mode);
  6. pathname: 要打开或创建的目标文件
  7. flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
  8. 参数:
  9.                 O_RDONLY: 只读打开
  10.                 O_WRONLY: 只写打开
  11.                 O_RDWR  : 读,写打开
  12.                                   这三个常量,必须指定一个且只能指定一个
  13.                 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
  14.                 O_APPEND: 追加写       
  15. 返回值:
  16.                 成功:新打开的文件描述符
  17.                 失败:-1
复制代码
open 函数详细利用哪个,和详细应用场景有关。如:目标文件不存在,需要 open 创建,则第三个参数表示创建文件的默认权限;否则利用两个参数的 open。
write read close lseek ,类比 C 文件相关接口。
1.1 代码演示

操作文件,除了利用 C 语言的接口【Linux】回顾 C 文件接口,还可以采用体系接口来举行文件访问;
写文件:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. int main()
  8. {
  9.     umask(0);
  10.     int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
  11.     if (fd < 0)
  12.     {
  13.         perror("open");
  14.         return 1;
  15.     }
  16.     int count = 5;
  17.     const char* msg = "hello open!\n";
  18.     int len = strlen(msg);
  19.     while (count--)
  20.     {
  21.         write(fd, msg, len);
  22.         // fd : 下面介绍
  23.         // msg : 缓冲区首地址
  24.         // len : 本次读取,期望写入多少个字节的数据
  25.         // 返回值 : 实际写了多少字节数据
  26.     }
  27.     close(fd);
  28.     return 0;
  29. }
复制代码
读文件:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. int main()
  8. {
  9.     int fd = open("myfile", O_RDONLY);
  10.     if (fd < 0)
  11.     {
  12.         perror("open");
  13.         return 1;
  14.     }
  15.    
  16.     const char* msg = "hello open!\n";
  17.     char buf[1024];
  18.     while (1)
  19.     {
  20.         ssize_t s = read(fd, buf, strlen(msg)); // 类比write
  21.         if (s > 0)
  22.         {
  23.             printf("%s", buf);
  24.         }
  25.         else
  26.         {
  27.             break;
  28.         }
  29.     }
  30.     close(fd);
  31.     return 0;
  32. }
复制代码
1.2 open 函数返回值

在认识返回值之前,先来认识两个概念:体系调用 和 库函数 :



2. 文件描述符 fd


2.1 0 / 1 / 2


  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <string.h>
  6. int main()
  7. {
  8.     char buf[1024];
  9.     ssize_t s = read(0, buf, sizeof(buf));
  10.     if (s > 0)
  11.     {
  12.         buf[s] = 0;
  13.         write(1, buf, strlen(buf));
  14.         write(2, buf, strlen(buf));
  15.     }
  16.     return 0;
  17. }
复制代码


2.2 文件描述符的分配规则

直接看代码:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. int main()
  6. {
  7.     int fd = open("myfile", O_RDONLY);
  8.     if (fd < 0)
  9.     {
  10.         perror("open");
  11.         return 1;
  12.     }
  13.     printf("fd: %d\n", fd);
  14.     close(fd);
  15.     return 0;
  16. }
复制代码
输出发现是 fd: 3 ,
关闭 0 或者 2,再看:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. int main()
  6. {
  7.     close(0);
  8.     //close(2);
  9.     int fd = open("myfile", O_RDONLY);
  10.     if (fd < 0)
  11.     {
  12.         perror("open");
  13.         return 1;
  14.     }
  15.     printf("fd: %d\n", fd);
  16.     close(fd);
  17.     return 0;
  18. }
复制代码
发现结果是:fd: 0 或者 fd: 2 ,
可见,文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被利用的最小的一个下标,作为新的文件描述符,会分配给最新打开的文件。
3. 重定向

那如果关闭 1 呢?看代码:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <stdlib.h>
  6. int main()
  7. {
  8.     close(1);
  9.     int fd = open("myfile", O_WRONLY | O_CREAT, 00644);
  10.     if (fd < 0)
  11.     {
  12.         perror("open");
  13.         return 1;
  14.     }
  15.     printf("fd: %d\n", fd);
  16.     fflush(stdout);
  17.     close(fd);
  18.     exit(0);
  19. }
复制代码
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中 fd = 1。这种现象叫做输出重定向。
常见的重定向有:> ,>> ,< 。
那重定向的本质是什么呢?

3.1 dup2 体系调用函数

函数原型如下:
  1. #include <unistd.h>
  2. int dup2(int oldfd, int newfd);
复制代码
函数简介:
  1. makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:
  2. 将newfd设置为oldfd的副本,并在必要时先关闭newfd,但请注意以下事项:
  3. *        If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
  4.         如果oldfd不是有效的文件描述符,则调用失败,newfd不会关闭。
  5. *        If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
  6.         如果oldfd是一个有效的文件描述符,并且newfd与oldfd具有相同的值,那么dup2()什么都不做,并返回newfd。
复制代码
示例代码:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. int main()
  5. {
  6.     int fd = open("./log", O_CREAT | O_RDWR, 0644);
  7.     if (fd < 0)
  8.     {
  9.         perror("open");
  10.         return 1;
  11.     }
  12.     close(1);
  13.     dup2(fd, 1);
  14.     int i = 0;
  15.     for (i = 0; i < 5; i++)
  16.     {
  17.         char buf[1024] = { 0 };
  18.         ssize_t read_size = read(0, buf, sizeof(buf) - 1);
  19.         if (read_size < 0)
  20.         {
  21.             perror("read");
  22.             break;
  23.         }
  24.         printf("%s", buf);
  25.         fflush(stdout);
  26.     }
  27.     return 0;
  28. }
复制代码

4. FILE 与 缓冲区


来段代码研究一下:
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5.     const char* msg0 = "hello printf\n";
  6.     const char* msg1 = "hello fwrite\n";
  7.     const char* msg2 = "hello write\n";
  8.     printf("%s", msg0);
  9.     fwrite(msg1, strlen(msg0), 1, stdout);
  10.     write(1, msg2, strlen(msg2));
  11.     fork();
  12.     return 0;
  13. }
复制代码
运行出结果:
  1. hello printf
  2. hello fwrite
  3. hello write
复制代码
但如果对历程实现输出重定向呢?./a.out > file ,我们发现结果酿成了:
  1. hello write
  2. hello printf
  3. hello fwrite
  4. hello peintf
  5. hello fwrite
复制代码
我们发现 printf 和 fwrite(库函数)都输出了 2 次,而 write 只输出了一次(体系调用)。
为什么呢?肯定和 fork 有关:

综上:printf fwrite 库函数会自带缓冲区,而 write 体系调用没有带缓冲区。别的,我们这里所说的缓冲区,都是用户级缓冲区。实在为了提升整机性能,OS 也会提供相关内核级缓冲区,不外不在我们讨论范围之内。那这个缓冲区谁提供呢?printf fwrite 是库函数,writre 是体系调用,库函数在体系调用的“上层”,是对体系调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以阐明,该缓冲区是二次加上的,又由于是 C,以是由 C 标准库提供。

   END
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4