0


【Linux学习】文件系统 - 第三篇


*🍑个人主页:Jupiter.🚀 所属专栏:Linux从入门到进阶欢迎大家点赞收藏评论😊*
在这里插入图片描述

目录


🦅重定向原理以及实现
  • 先看一个这样的代码:
#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<stdlib.h>intmain(){close(1);int fd =open("myfile", O_WRONLY|O_CREAT,00644);if(fd <0){perror("open");return1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);}

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

输出重定向

。常见的重定向有:

>, >>, <

上述代码现象分析:

  • 因为我们关闭了fd=1,标准输出,所以当我们打开一个文件的时候,这个文件分配的fd=1;然后当我们printf打印内容到标准输出的时候,实际上是给标准输出文件里面写内容,但是这时候fd指向的文件已经改变了,所以就写到对应打开的文件中去了。

通过上面类似的的方法,也就可以实现输入重定向追加重定向。

重定向本质,如图:
在这里插入图片描述

🐱dup2系统调用实现重定向

🎈dup2 系统调用

在这里插入图片描述
在这里插入图片描述
翻译:dup2系统调用的功能是:将old文件fd拷贝给new文件的fd。

演示代码:

int fd =open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);dup2(fd,1);//1号文件描述符是标准输出                                                                                                                         if(fd==-1){perror("open");return1;}printf("HHHHHHHHH\n");//向标准输出里面写内容

运行结果与分析:

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

🍑在自定义shell中实现重定向

使用dup2实现一个重定向(>>,>,<),完善之前实现的自定以shell;

代码实现:

实现思路:


🌹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;}
运行出结果:
  • hello printf
  • hello fwrite
  • hello write

但如果在main函数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标准库提供。

如果有兴趣,可以看看

FILE结构体

:
typedef struct _IO_FILE FILE; 在/usr/include/stdio.h

/usr/include/libio.h
struct_IO_FILE{int _flags;/* High-order word is _IO_MAGIC; rest is flags. */#define_IO_file_flags_flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;/* Current read pointer */char* _IO_read_end;/* End of get area. */char* _IO_read_base;/* Start of putback+get area. */char* _IO_write_base;/* Start of put area. *
 char* _IO_write_ptr; /* Current put pointer. */char* _IO_write_end;/* End of put area. */char* _IO_buf_base;/* Start of reserve area. */char* _IO_buf_end;/* End of reserve area. *//* The following fields are used to support backing up and undo. */char*_IO_save_base;/* Pointer to start of non-current get area. */char*_IO_backup_base;/* Pointer to first valid character of backup area */char*_IO_save_end;/* Pointer to end of non-current get area. */struct_IO_marker*_markers;struct_IO_FILE*_chain;int _fileno;//封装的文件描述符#if0int _blksize;#elseint _flags2;#endif
 _IO_off_t _old_offset;/* This used to be _offset but it's too small. */#define__HAVE_COLUMN/* temporary *//* 1+column number of pbase(); 0 is unknown. */unsignedshort _cur_column;signedchar _vtable_offset;char _shortbuf[1];/* char* _save_gptr; char* _save_egptr; */
 
 _IO_lock_t *_lock;#ifdef_IO_USE_OLD_IO_FILE};

📕模拟实现C语言文件系统调用
#pragmaonce   #include<stdio.h>#defineSIZE1024#defineNONE_FLUSH(1<<1)#defineLINE_FLUSH(1<<2)//行刷新#defineFULL_FLUSH(1<<3)//全刷新                                                         typedefstructmy_FILE{char buffer[SIZE];int pos;int cap;int flush_mode;int fileno;}myFILE;voidmy_flush(myFILE* fp);           
 myFILE*my_fopen(constchar* pathname,constchar* mode);intmy_fwrite(myFILE* fp,constchar* s,int size);voidmy_fclose(myFILE* fp);
1 #include<stdio.h>2 #include<stdlib.h>3 #include"mystdio.h"4 #include<string.h>5 #include <sys/types.h>6 #include <sys/stat.h>7 #include <fcntl.h>8 #include<unistd.h>91011 myFILE*my_fopen(constchar* pathname,constchar* mode)12{13int fd =0;14umask(0);15if(strcmp(mode,"r")==0)16{17         fd =open(pathname,O_RDONLY);18}19elseif(strcmp(mode,"w")==0)20{21         fd =open(pathname,O_CREAT|O_WRONLY|O_TRUNC,0666);22}23elseif(strcmp(mode,"a")==0)24{25         fd =open(pathname,O_CREAT|O_WRONLY|O_APPEND,0666);26}27else28{29returnNULL;30}3132if(fd<0)33{34returnNULL;35}36     myFILE* fp =(myFILE*)malloc(sizeof(myFILE));37if(fp==NULL)returnNULL;38     fp->fileno=fd;39     fp->cap=SIZE;40     fp->flush_mode=LINE_FLUSH;41     fp->pos =0;4243return fp;44}46voidmy_flush(myFILE* fp)47{48if(fp->pos==0)return;49write(fp->fileno,fp->buffer,fp->pos);50     fp->pos=0;51}52intmy_fwrite(myFILE* fp,constchar* s,int size)53{54memcpy(fp->buffer+fp->pos,s,size);55     fp->pos+=size;56if(fp->flush_mode==LINE_FLUSH && fp->buffer[fp->pos-1]=='\n')57{58my_flush(fp);59}60//下面可以判断全刷新等等61if(fp->flush_mode==FULL_FLUSH && fp->pos == fp->cap)62{63my_flush(fp);64}65return size;6667}68voidmy_fclose(myFILE* fp)69{70my_flush(fp);71close(fp->fileno);72free(fp);73}

标签: linux 学习 android

本文转载自: https://blog.csdn.net/2301_77509762/article/details/140880119
版权归原作者 Jupiter· 所有, 如有侵权,请联系我们删除。

“【Linux学习】文件系统 - 第三篇”的评论:

还没有评论