• linux--系统文件I/O


    一,系统接口

    1,open

    函数原型:int open(const char *pathname,int flags)

    pathname:要打开或者创建的目标文件。

    flags:打开要做什么,基本上有O_WRONLY(只写) O_RDONLY(只读) O_CREAT(创建)O_APPEND(追加)

    返回值:成功返回文件描述符

                  失败返回 -1;

    如下代码:

     2,write

    函数原型:

            int write(int _filehandle,const void *_Buf,unsigned int _Maxcharcount)

            _filehandle,文件描述符,_Buf写入的字符,_Maxcharcount字符长度

     相比较于其他接口,均是如此,如read/close/lseek

    但对于open的返回值fd为什么是3呢,此时就牵扯到一个文件描述符的概念。

    二,文件描述符

    1,0&1&2

    通过对open的了解,我们知道文件描述符fd是一个整数。

    linux系统会默认打开三个接口,0/1/2,这三个接口分别stdin,stdout,stderr,分别对应键盘,显示器,显示器。

    而我们也可以使用如下的方式进行输出,通过读0,输出到1和2

    1. void test2()
    2. {
    3. char buf[1024];
    4. ssize_t s = read(0, buf, sizeof(buf));
    5. if(s > 0)
    6. {
    7. buf[s] = 0;
    8. write(1, buf, strlen(buf));
    9. write(2, buf, strlen(buf));
    10. }
    11. }

    2,文件描述符是什么东西

    文件描述符就是从0开始的小整数,当我们打开文件后,就类似与创建进程一样,先描述再组织,使用task_struct描述这个文件的信息,被file_struct管理,使用指针指向数组的下标实现,再通过内核去管理这个文件的信息 ,只要知道这个数组下标,就能找到在内核中对应的信息。

    3,文件描述符的分配规则

    1. void test3()
    2. {
    3. close(1);
    4. int fd=open("log.txt",O_WRONLY);
    5. printf("i am printf\n");
    6. fprintf(stdout,"i am fileskkkk\n");
    7. fflush(stdout);
    8. close(fd);
    9. }

     此时我们关闭1号描述符,打开log.txt,使用输出到屏幕,发现屏幕上没有打印任何数据,打开log.txt,发现 i am printf 和i am fileskkkk都写到文件当中了,所以通过这个我们可以看到,

    文件描述符会优先分配到未被使用的最小下标。

    三,缓冲概念

    一般缓冲分为三种:

                            无缓冲(系统接口)

                            行缓冲(常见的对显示器进行刷新数据)(方便人机交互)

                            全缓冲(对文件的写入使用全缓冲)

    缓冲一般由语言层提供,os也有缓冲(为了减少对磁盘的访问)

    四,重定向

    1,重定向的原理

    通过上面程序的演示,我们大概可以知道,关闭标准输出,可以将信息输出到文件中,这就是一种重定向,本质是修改文件描述符fd下标对应的struct_file*里面。

    我们再来看看下面的程序;

    1. void test4()
    2. {
    3. close(1);
    4. int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
    5. if(fd < 0)
    6. {
    7. perror("open");
    8. }
    9. printf("fd: %d\n", fd);
    10. fflush(stdout);
    11. close(fd);
    12. }

    此时,我们发现,本来应该输出到显示器上的内容,输出到了文件myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <,更加说明了这种现象,

    2,重定向的函数

    使用dup2系统调用

    函数原型:

    int dup2(int oldfd,int newfd);

    dup2(fd,1),如这个一样,用fd覆盖1号文件描述符

    这个函数的功能在于覆盖式的把旧的文件描述符给新的文件描述符,两个文件描述符共享权限。

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<sys/types.h>
    4. #include<sys/stat.h>
    5. #include<unistd.h>
    6. #include<fcntl.h>
    7. int main()
    8. {
    9. printf("i am printf\n");
    10. fprintf(stdout,"i am fprintf\n");
    11. fputs("i am fputs",stdout);
    12. fork();
    13. return 0;
    14. }

    我们发现此时打印了6条语句,这是因为什么呢,

    其原因在于父进程打印完以后,fork创建出子进程,父子进程共享一段代码,父子数据发生写时拷贝,因为进程具有独立性,子进程也执行了三条语句。

    所以会输出6行。

    但当我们使用如下代码时,

    1. int main()
    2. {
    3. const char*msg="I am write\n";
    4. printf("i am printf\n");
    5. fprintf(stdout,"i am fprintf\n");
    6. write(1,msg,strlen(msg));
    7. fork();
    8. return 0;
    9. }

    会发现打印了5行数据,

    这是因为,当我们在往屏幕上打印数据时,会存在一个缓冲的概念,我们使用C库函数打印的时候,会先缓冲到C提供的缓冲区里面,子进程写时拷贝,也会缓冲到缓冲区,由于系统的接口没有缓冲区,所以会直接刷新到屏幕,再把父子进程的缓冲区刷新到屏幕上,所以打印五行数据。

    注意:重定向还是不重定向不会更改进程的缓冲方式

               C接口打印两次,osAPI打印1次

    总结

    1,语言层的输入输出均封装了系统给的API接口

    2,文件描述符就是类似进程,先描述(task_struct),在组织(file_struct),通过file提供的数组下标存文件的指针,然后系统对这个指针管理,只要知道下标,就能在内核中找到信息。

    3,重定向就是把本来要输出(输入)通过文件描述符,输出或者输入到我们想让文件去的地方

    4,语言有自己的缓冲区,操作系统也有,互不影响

  • 相关阅读:
    D-莲子的物理热力学
    力扣-消失的数字(两种方法)
    Java框架 SpringMVC--拦截器与异常处理器
    性能测试知多少---系统架构分析
    无法安装Hyper-V 该固件中的虚拟化支持被禁用
    rdma-轮询常用cq函数。
    Java后端开发面试题——JVM虚拟机篇
    量子计算的奥秘与魅力:开启未来科技的钥匙(详解)
    管理类联考——数学——汇总篇——知识点突破——数据分析——计数原理——排列组合——排队
    代码随想录算法训练营Day50 | 动态规划(11/17) LeetCode 123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV
  • 原文地址:https://blog.csdn.net/m0_63111921/article/details/125136096