*🍑个人主页: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}
版权归原作者 Jupiter· 所有, 如有侵权,请联系我们删除。