【Linux】文件系统(三)
2026/5/16 14:24:20 网站建设 项目流程

文章目录

  • 💡 重定向原理及实现
    • 🚀dup2系统调用
  • ⚡FILE与文件缓冲区
  • 🐧模拟实现C语言文件系统调用

💡 重定向原理及实现

#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<sys/types.h>#include<sys/stat.h>intmain(){close(1);intfd=open("myfile.txt",O_WRONLY|O_CREAT,0644);if(fd==-1){perror("open");return1;}printf("fd->:%d\n",fd);fflush(stdout);close(fd);exit(0);return0;}


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

上述代码现象分析:

因为我们关闭了fd=1,标准输出,所以当我们打开一个文件的时候,这个文件分配的fd=1;然后当我们printf打印内容到标准输出的时候,实际上是给标准输出文件里面写内容,但是这时候fd指向的文件已经改变了,所以就写到对应打开的文件中去了。
通过上面类似的的方法,也就可以实现输入重定向追加重定向。

重定向本质,如图:

🚀dup2系统调用

  • dup2(oldfd, newfd) 会将 newfd 指向与 oldfd 相同的文件或设备。

dup2(旧fd, 新fd):

    1. 关闭新fd原本指向的东西
    1. 让新fd 指向 旧fd 指向的文件/管道
    1. 两个fd操作同一个位置
#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<sys/types.h>#include<sys/stat.h>intmain(){intfd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);dup2(fd,1);//所有原本输出到标准输出的内容会被重定向到 fd 对应的文件或设备if(fd==-1){perror("open");return1;}printf("HHHHHHHHH\n");//向标准输出里面写内容return0;}

运行结果与分析:

  • 结果:将printf里面的内容输出重定向到了log.txt文件中。
  • 分析:dup2(fd,1) 后,如下图所示,1本来指向的是显示器,3指向的新打开的文件,参数fd=3,dup2过后,1也指向的是新打开的文件,但是C语言接口printf是向显示器打印,也即是1号文件描述符,但是这里1号的指向改变了,所以再使用printf打印,就写入了到log.txt中了。

⚡FILE与文件缓冲区

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
  • 所以C库当中的FILE结构体内部,必定封装了fd

接下来我们看一段代码:

intmain(){constchar*msg0="hello printf\n";constchar*msg1="hello fwrite\n";constchar*msg2="hello write\n";printf("%s",msg0);fwrite(msg1,strlen(msg0),1,stdout);write(1,msg2,strlen(msg2));return0;}


再return前加入了fork()函数之后:
hello write
hello printf
hello fwrite
hello printf
hello fwrite

我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!

一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后,但是进程退出之后,会统一刷新,写入文件当中。
但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。write 没有变化,说明没有所谓的缓冲。

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

🐧模拟实现C语言文件系统调用

#pragmaonce#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#defineLINE_SIZE1024#defineFLUSH_NOW1#defineFLUSH_LINE2#defineFLUSH_FULL4struct_myFILE{unsignedintflags;intfileno;//缓冲区charcache[LINE_SIZE];intcap;intpos;//下次写入的位置};typedefstruct_myFILEmyFILE;myFILE*my_fopen(constchar*path,constchar*flag);voidmy_fflush(myFILE*fp);ssize_tmy_fwrite(myFILE*fp,constchar*data,intlen);voidmy_fclose(myFILE*fp);
#include"mystdio.h"#include<sys/types.h>myFILE*my_fopen(constchar*path,constchar*flag){intflag1=0;intiscreate=0;mode_t mode=0666;if(strcmp(flag,"r")==0){flag1=(O_RDONLY);}elseif(strcmp(flag,"w")==0){flag1=(O_WRONLY|O_CREAT|O_TRUNC);iscreate=1;}elseif(strcmp(flag,"a")==0){flag1=(O_WRONLY|O_CREAT|O_APPEND);iscreate=1;}else{}intfd=0;if(iscreate){fd=open(path,flag1,mode);}elsefd=open(path,flag1);if(fd<0)returnNULL;myFILE*fp=(myFILE*)malloc(sizeof(myFILE));if(!fp)returnNULL;fp->fileno=fd;fp->flags=FLUSH_LINE;fp->cap=LINE_SIZE;fp->pos=0;returnfp;}voidmy_fflush(myFILE*fp){write(fp->fileno,fp->cache,fp->pos);fp->pos=0;}ssize_tmy_fwrite(myFILE*fp,constchar*data,intlen){memcpy(fp->cache+fp->pos,data,len);fp->pos+=len;if((fp->flags&FLUSH_LINE)&&fp->cache[fp->pos-1]=='\n'){my_fflush(fp);}returnlen;}voidmy_fclose(myFILE*fp){my_fflush(fp);close(fp->fileno);free(fp);}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询