ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux】从open到write:系统文件I/O 的奥秘与实战指南 [打印本页]

作者: 老婆出轨    时间: 2024-11-3 21:01
标题: 【Linux】从open到write:系统文件I/O 的奥秘与实战指南


  
1.经典回首C文件接口

在使用C语言时,我们需要访问文件通常会用到fopen、 fwrite、和fread还有fclose等函数。
1.2 fwrite

比如此时我需要往文件中写入一些信息:
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5.     FILE* fp = fopen("test.txt","w");
  6.     if(fp == NULL)
  7.     {
  8.         perror("fopen failed");
  9.         return 1;
  10.     }
  11.     const char* str = "i am yui~\n";
  12.     int len = strlen(str);
  13.     int num = 5;
  14.     while(num--)
  15.     {
  16.         fwrite(str,len,1,fp);
  17.     }
  18.     fclose(fp);
  19.     return 0;
  20. }
复制代码
执行结果:
  1. ubuntu@VM-20-9-ubuntu:~/FILETEST$ cat test.txt
  2. i am yui~
  3. i am yui~
  4. i am yui~
  5. i am yui~
  6. i am yui~
复制代码
1.2 fread

下面再来读一读文件中的内容:
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5.     FILE* fp = fopen("test.txt","r");
  6.     if(!fp)
  7.     {
  8.         perror("fopen failed");
  9.         return 1;
  10.     }
  11.     const char* str = "i am yui~\n";
  12.     char s[1024];
  13.     int len = strlen(str);
  14.     while(1)
  15.     {
  16.         ssize_t n  = fread(s,1,len,fp);
  17.         if(n == len)
  18.         {
  19.             s[len] = 0;
  20.             printf("%s",s);
  21.         }
  22.         if(feof(fp))
  23.             break;
  24.     }
  25.         
  26.     return 0;
  27. }
复制代码
输出结果:
  1. i am yui~
  2. i am yui~
  3. i am yui~
  4. i am yui~
  5. i am yui~
复制代码
2. 系统文件I/O

除了使用上述C接口,我们还可以接纳系统接口来访问文件。
系统文件 I/O(输入/输出)是指在操作系统层面进行文件的读写操作。在 Linux 和其他类 Unix 系统中,系统文件 I/O 通常通过系统调用(system call)完成。与 C 尺度库的文件 I/O 函数(如 fopen、fread、fwrite)相比,系统文件 I/O 提供了更底层的控制和更高的效率,但操作也稍显复杂。
为了更好的理解系统文件I/O,我会用系统接口来实现上面的功能,并进行讲解。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <fcntl.h>
  6. #include <sys/stat.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 failed");
  14.         return 1;
  15.     }
  16.     int num = 5;
  17.     const char* str = "i am yui\n";
  18.     int len = strlen(str);
  19.     while(num--)
  20.     {
  21.         write(fd,str,len);
  22.     }
  23.     close(fd);
  24.     return 0;
  25. }
复制代码
2 open函数

由于如今我们需要用系统接口来打开文件,那么我们会用到open函数而不是fopen函数。
open 函数是 Unix 和类 Unix 操作系统中的一个系统调用,用于打开文件并返回一个文件形貌符。这个文件形貌符用于后续的文件操作,如读、写、关闭等。相比 C 尺度库的 fopen 函数,open 提供了更底层的控制,更得当系统级编程。
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. int open(const char *pathname, int flags, mode_t mode);
复制代码
2.1 参数介绍:


在下面的写入操作我们只需要选择O_WRONLY|O_CREAT就可以了。
2.2 返回值(文件形貌符)


  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. int main()
  8. {
  9.     char buf[1024];
  10.     ssize_t s = read(0,buf,sizeof(buf));//从键盘读入数据
  11.     if(s>0)
  12.     {
  13.         buf[s] = 0;
  14.         write(1,buf,strlen(buf));
  15.         write(2,buf,strlen(buf));
  16.     }
  17.     return 0;
  18. }
  19. //运行结果
  20. /**
  21. ubuntu@VM-20-9-ubuntu:~/FILETEST$ ./a.out
  22. hello world
  23. hello world
  24. hello world
  25. */
复制代码
从这段代码我们也可以更加清晰地认识到Linux下的统统皆文件。
一些底层知识:
文件形貌符是从0开始地小整数,当我们打开文件时,操作系统在内存中要创建相对应地数据结构来形貌目标文件,于是就有了file结构体来表现一个已经打开地文件对象。而历程执行open系统调用,必须让历程和文件关联起来。每一个历程都有一个指针*file,指向一张表file_struct该表最重要地部分包罗一个指针数组,每个元素都是一个指向文件地指针。本质上文件形貌符就是该数组地下标,所以只要拿着文件形貌符就可以找到对应地文件。

2.2.1 文件形貌符的分配规则

先看代码:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <string.h>
  7. int main()
  8. {
  9.     int fd = open("myfile",O_RDONLY);//只读模式打开
  10.     if(fd>0)
  11.     {
  12.         printf("%d\n",fd);
  13.     }
  14.     close(fd);
  15.     return 0;
  16. }
  17. //运行结果:
  18. /*
  19. ubuntu@VM-20-9-ubuntu:~/FILETEST$ ./a.out
  20. 3
  21. */
复制代码
结果是3。
那么当我们关闭0这个文件形貌符试试看呢?
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <string.h>
  7. int main()
  8. {
  9.     close(0);//关闭文件描述符0
  10.     int fd = open("myfile",O_RDONLY);//只读模式打开
  11.     if(fd<0)
  12.     {
  13.         perror("open");
  14.         return 1;
  15.     }
  16.     printf("%d\n",fd);
  17.     close(fd);
  18.     return 0;
  19. }
  20. //打印结果:
  21. /*
  22. ubuntu@VM-20-9-ubuntu:~/FILETEST$ ./a.out
  23. 0
  24. */
复制代码
结果是0,你猜到了吗。
由此可见,文件形貌符的分配规则:在file_struct数组当中,找到当前没有直接使用的最小的一个下标,作为新的文件形貌符。
最后在来看看重定向
2.2.2 重定向

如今我们将标记输出1给关闭了,然后再打开一个文件再往内里写点东西,看看会发生什么。
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <string.h>
  7. int main()
  8. {
  9.     close(1);//关闭文件描述符1
  10.     int fd = open("myfile",O_WRONLY|O_CREAT,0644);//写模式打开
  11.     if(fd<0)
  12.     {
  13.         perror("open");
  14.         return 1;
  15.     }
  16.     printf("fd:%d\n",fd);
  17.     fflush(stdout);
  18.     close(fd);
  19.     return 0;
  20. }
复制代码
打开myfile发现,文件中存在fd:1。
也是就说,本该再表现屏中表现的内容被写进了myfile文件。我们把这种现象叫做重定向。常见的重定向>, >>, <
重定向的本质:

3. write函数

write 函数是 Unix 和 Linux 系统中进行文件写入操作的系统调用,用于将数据从用户空间的缓冲区写入到文件或设备(例如文件、管道、网络套接字)中。write 是一种底层 I/O 操作,它绕过尺度 I/O 缓冲区,直接写入文件形貌符指向的目标,常用于处理系统资源的原始数据读写。
语法
  1. ssize_t write(int fd, const void *buf, size_t count);
复制代码
参数说明

4. read函数

read 是 Unix 和 Linux 系统中的一个系统调用,用于从文件或其他输入资源(如管道、网络套接字等)中读取数据到用户提供的缓冲区中。与 write 相对应,read 直接从文件形貌符中获取数据,不经过尺度 I/O 缓冲区,得当低级别的 I/O 操作。
语法
  1. ssize_t read(int fd, void *buf, size_t count);
复制代码
参数说明

5. 总结

fopen、fclose、fread、fwrite这些都是C语言尺度库的函数,也就是库函数。
open、close、read、write都是系统提供的接口,也就是系统调用接口。
而这部分库函数会区调用系统接口。

可以认为,f*系列的函数,都是对系统的封装,方便二次开辟。

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




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4