重庆模板建站软件,网站建设官网型好呢还是商城型,怎么做网页个人简介,wordpress comment_status重定向缓冲区 1.重定向1.1重定向本质1.2重定向接口1.3重定向分类1.3.1输出重定向1.3.2追加重定向1.3.3输入重定向 2.理解 #xff0c; #xff0c; 3.如何理解linux下一切皆文件4.缓冲区4.1理解缓冲区问题4.1.1为什么要有缓冲区4.1.2缓冲区刷… 重定向缓冲区 1.重定向1.1重定向本质1.2重定向接口1.3重定向分类1.3.1输出重定向1.3.2追加重定向1.3.3输入重定向 2.理解 3.如何理解linux下一切皆文件4.缓冲区4.1理解缓冲区问题4.1.1为什么要有缓冲区4.1.2缓冲区刷新策略的问题4.1.3缓冲区在哪里指的是什么缓冲区4.1.4如何解释fork问题 5.缓冲区该如何理解5.1myStdio.h5.2myStdio.c5.2.1_fopen5.2.2_fwrite5.2.3_fflush5.2.4_fclose 5.3main.c 6.缓冲区和OS的关系 自我名言只有努力才能追逐梦想只有努力才不会欺骗自己。 喜欢的点赞收藏关注一下把 1.重定向 1 #includestdio.h2 #includestring.h3 #includesys/types.h4 #includesys/stat.h5 #includefcntl.h6 #includeunistd.h7 #includestdlib.h8 9 int main()10 {11 // close(0);12 // close(2);13 close(1); 14 umask(0); 15 int fdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666); 16 if(fd -1) 17 { 18 perror(open); 19 exit(1); 20 } 21 printf(fd:%d\n,fd); 22 23 close(fd); 24 25 return 0; 26 } close(1)为什么没有打印新建文件fd呢 printf(“%d\n”,fd); printf会把内容打印到stdout文件中。 但是close(1)关闭标准输出stdout—显示器int fdopen()新打开的文件fd是1。 stdout–fd–1虽然我们手动关闭了stdout但是系统并不知道还以为fd为1的位置是stdout但是这个位置现在已经被新打开的文件占用了所以打印到了新打开的文件里。 1 #includestdio.h2 #includestring.h3 #includesys/types.h4 #includesys/stat.h5 #includefcntl.h6 #includeunistd.h7 #includestdlib.h8 9 int main()10 {11 // close(0);12 // close(2);13 close(1);14 umask(0);15 int fdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);16 if(fd -1)17 {18 perror(open);19 exit(1);20 }21 printf(fd:%d\n,fd);22 23 //这里必须刷新一下不然log.txt里面没有内容这里和缓冲区有关下面讲 24 fflush(stdout); 25 close(fd); 26 27 return 0; 28 } 本来应该打印到显示器文件中但是却写到文件里了。------重定向
1.1重定向本质 1.2重定向接口 这个主要介绍dup2函数。
int dup2(int oldfd, int newfd);那怎么使用dup2来实现刚才的效果把打印到显示器内容写入到log.txt 是上面那样写的还是下面那样写的我们分析分析。 画图分析 因此正确写法如下 4 #includesys/stat.h5 #includefcntl.h6 #includeunistd.h7 #includestdlib.h8 9 int main()10 {11 // close(0);12 // close(2);13 // close(1);14 umask(0);15 int fdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);16 if(fd -1)17 {18 perror(open);19 exit(1);20 }21 //重定向 22 dup2(fd,1); 23 printf(fd:%d\n,fd); 24 25 //这里必须刷新一下不然log.txt里面没有内容 26 fflush(stdout); 27 close(fd); 28 29 return 0; 30 } 1.3重定向分类
1.3.1输出重定向
上面内容就是输出重定向把新打开文件的fd重定向到fd为1默认为显示器的位置。
1.3.2追加重定向 1 #includestdio.h2 #includestring.h3 #includesys/types.h4 #includesys/stat.h5 #includefcntl.h6 #includeunistd.h7 #includestdlib.h8 9 int main()10 {11 // close(0);12 // close(2);13 // close(1);14 umask(0);15 // int fdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);16 int fdopen(log.txt,O_WRONLY|O_CREAT|O_APPEND,0666);17 if(fd -1)18 {19 perror(open);20 exit(1);21 }22 //重定向23 dup2(fd,1);24 printf(你好\n);25 printf(吃了吗\n); 26 27 //这里必须刷新一下不然log.txt里面没有内容 28 fflush(stdout); 29 close(fd); 30 31 return 0; 32 } 1.3.3输入重定向
#includestdio.h
#includestring.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includeunistd.h
#includestdlib.hint main()
{ // close(0); // close(2); // close(1); umask(0); // int fdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666); // int fdopen(log.txt,O_WRONLY|O_CREAT|O_APPEND,0666); int fdopen(log.txt,O_RDONLY); if(fd -1) { perror(open); exit(1); } //输入重定向 dup2(fd,0); char outbuffer[64]; while(1) { printf(); if(fgets(outbuffer,sizeof(outbuffer),stdin) NULL) break; printf(%s,outbuffer); }return 0;} 2.理解
在前面实现过一个自己的shell现在我们给这个shell增加重定向功能来理解重定向。
//增加一个分割指令和文件名的函数
commandstrtok(lineCommand);分割的时候我们需要知道重定向是什么类型文件是什么名字因此增加两个全局变量来记录。 //重定向类型 //第一个为初始重定向 #define DEFAULT_REDIR 0 #define INPUT_REDIR 1 #define OUTPUT_REDIR 2 #define ERROR_EDIRR 3 //重定向类型文件名 int redirTypeDEFAULT_REDIR; char* redirFileNULL; 分割函数 //这里找文件名没有写成函数而写个宏#define trimSpace(start) do{\while(isspace(*start)) start;\}while(0)void commandstrtok(char* cmd){assert(cmd);char* startcmd;char* endcmdstrlen(cmd);while(start end){if(*start ){*start0;start;//这里可能 ls -a -l log.txt,trimSpace(start);redirTypeINPUT_REDIR;redirFilestart;break;}else if(*start ) { *start0; start; if(*start ) { redirTypeAPPEND_REDIR; start; } else { redirTypeOUTPUT_REDIR; } trimSpace(start); redirFilestart; break; } else{ start;}}因为命令是子进程执行的真正重定向的功能一定是由子进程来完成的 但是如何重定向是父进程要告知给子进程的。
//创建子进程
pid_t id fork();
assert(id ! -1);
if(id 0)
{ switch(redirType){case INPUT_REDIR:{int fdopen(log.txt,O_RDONLY);if(fd 0){perror(open);return 1;}//重定向文件已经打开了dup2(fd,0);}break;case OUTPUT_REDIR:case APPEND_REDIR:{umask(0);int flagsO_WRONLY|O_CREAT;if(redirType OUTPUT_REDIR) flags|O_CREAT;else flags|O_APPEND; int fdopen(log.txt,flags,0666);if(fd 0){perror(open);return 1;}dup2(fd,1); }break;default:printf(bug?\n);break;}//程序替换execvp(myargv[0],myargv);exit(1);
}shell完整代码 问子进程重定向会影响父进程吗 不会 问指向程序替换的时候会不会影响曾经进程打开的重定向文件呢 不会
3.如何理解linux下一切皆文件
Linux下一切皆文件那键盘显示器磁盘网卡等硬件在linux下都是文件吗 可以这样说的。 站在struct file上层看来所有的设备和文件统一都是struct file---------Linux下一切皆文件。 这里可能有这样一个问题如果同一个文件被多个指针指向但是某个进程把这个文件关了会不会影响其他进程对这个文件的操作呢
其实并不会一个被打开的文件有引用计数。 表明有几个指针指向这个文件这样做是因为一个文件可能被多个指针指向。如果某个进程关闭文件影响到其他进程就不能保证进程的独立性。close关闭文件其实并不是真正关闭文件只是把引用计数减1当只有一个指针指向这个文件close关闭文件才是真正关闭这个文件。
4.缓冲区
先看一种现象。 1 #includestdio.h 2 #includestring.h 3 #includesys/types.h 4 #includesys/stat.h 5 #includefcntl.h 6 #includeunistd.h 7 #includestdlib.h 8 9 int main() 10 { 11 printf(hello printf\n); 12 fprintf(stdout,%s\n,hello fprintf); 13 14 15 const char* outputhello write\n; 16 write(1,output,strlen(output)); 17 18 19 return 0; 20 } 1 #includestdio.h2 #includestring.h3 #includesys/types.h4 #includesys/stat.h5 #includefcntl.h6 #includeunistd.h7 #includestdlib.h8 9 int main()10 {11 printf(hello printf\n);12 fprintf(stdout,%s\n,hello fprintf);13 14 15 const char* outputhello write\n;16 write(1,output,strlen(output));17 18 fork(); 19 return 0; 20 } 对比两张图片第二段代码补充fork创建子进程重定向之后C接口的函数被打印了两次系统调用的接口前面都只是打印了一次。
这是什么原因现在只是知道肯定是fork函数有关。 其实这里也和缓冲区有关。
4.1理解缓冲区问题
缓冲区本质就是一段内存
4.1.1为什么要有缓冲区
这里讲一个小故事帮助理解。
在四川的张三要给远在北京的李四送一个键盘。 张三为了节省时间选择2 现实中快递行业的意义 节省发送者的时间。
进程把数据打包给磁盘是一个很慢的过程如果让进程一直等着显然不太好。因此我们也需要一个快速的方式-----缓冲区 缓冲区的意义是什么呢 节省进程进行数据IO的时间。 但是我们在用文件写的接口时并没有专门拷贝数据啊以fwrite为例
其实与其理解fwrite是写入到文件的函数倒不如理解fwrie是拷贝函数将数据从进程拷贝到缓冲区或者“外设”中。
4.1.2缓冲区刷新策略的问题
把东西给顺丰之后那顺丰什么时候发货呢
张三第二天在想寄一个鼠标给李四当走到快递站点发现自己昨天的快递还没有发走就问快递人员快递人员说我们的快递都是用大货车和飞机发送的就这一个快递不可能马上就发走等到满足发送件货才发。
缓冲区的刷新策略 不同外设IO访问速度是不同的。 缓冲区结合具体的设备定制自己的刷新策略。3策略2特殊 3策略 a.立即刷新 ---- 无缓冲 b.行刷新 ---- 行缓存 ---- 显示器 显示器是给人看的如果一次给很多信息人看着不舒服 c.缓冲区满 ---- 全缓存 ---- 磁盘文件显示器除外 2特例 1用户强制刷新 fflush 2进程退出 进程退出要刷新缓冲区 4.1.3缓冲区在哪里指的是什么缓冲区
C接口打印了两次系统调用接口打印了一次这种现象一定和缓冲区有关。 虽然现在不知道缓冲区在哪里但是我们知道缓冲区一定不在内核中。 因为C接口底层调用的是系统调用接口如果缓冲区在内核中write也应该打印两次。
那缓冲区到底在哪
我们之前谈论的所有缓冲区都指的是用户级语言层面给我们提供的缓冲区。 还记得C程序默认给我们打开stdinstdoutstderr都是-------FILE*------指向FILE结构体结构体包含------- fd其实还包含一个缓冲区。
因此缓冲区在FILE结构体中。
所以当我们自己要强制刷新fflush文件指针关闭fclose文件指针传的都是FILE*。因为缓存区在FILE结构体中。
4.1.4如何解释fork问题 代码结束之前创建子进程。 1.我们没有进行输出重定向看到了三条信息。 stdout默认使用的是行刷新在fork之前两条C函数已经将数据打印输出到到显示器上你的FILE内部进程内部不存在对应的数据了。这时创建子进程等到子进程父进程退出的时都要刷新缓冲区但是这时缓冲区已经没有内容可以刷新了。因此C函数打印两条信息。 2.输入重定向之后C接口打印两次 当我们进行写入文件就不再是显示器而是普通文件采用的刷新策略是全缓存之前2条C打印函数虽然带了\n但是不足与将stdout缓冲区写满数据并没有被刷新。 fork的时候stdout属于父进程创建子进程紧跟着就是进程退出谁先退出一定要进行缓冲区刷新缓存区刷新----就是修改 这时就有了写时拷贝 因此C接口数据最终会显示两份。 3.write为什么前后只打印一次 上面过程都是write无关write没有FILE而用的是fd就没有C提供的缓冲区。
5.缓冲区该如何理解
自己写一个简易的缓冲区来帮助理解数据刷新策略“数据如何缓存”
5.1myStdio.h 1 #pragma once 2 #includeerrno.h 3 #includestdio.h 4 #includestring.h 5 #includesys/types.h 6 #includesys/stat.h 7 #includefcntl.h 8 #includeunistd.h 9 #includestdlib.h 10 11 #define SIZE 1024 12 #define SYNC_NOW 1 13 #define SYNC_LINE 2 14 #define SYNC_FULL 3 15 16 typedef struct _FILE{ 17 int flags;//刷新方式 18 int fileno; //文件描述符19 int capacity;//buffer容量 20 int size;//buffer当前使用量 21 char buffer[SIZE]; //缓冲区22 }_FILE; 23 24 25 _FILE* _fopen(const char* path_name,const char* mode); 26 void _fwrite(const void* ptr,int num,_FILE* fp); 27 void _fclose(_FILE* fp); 28 void _fflush(_FILE* fp); 5.2myStdio.c
5.2.1_fopen _FILE* _fopen(const char* path_name,const char* mode)
{assert(path_name);int flags0;int defaultmode0666;if(strcmp(mode,r) 0){flags|O_RDONLY;}else if(strcmp(mode,w) 0){flags|(O_WRONLY|O_CREAT|O_TRUNC);}else if(strcmp(mode,a) 0){flags|(O_WRONLY|O_CREAT|O_APPEND);}int fd0;if(flags O_RDONLY){fdopen(path_name,flags);}else{fdopen(path_name,flags,defaultmode); }if(fd 0){const char*errstrerror(errno);write(2,err,strlen(err));return NULL;//这就是为什么创建文件失败返回NULL}_FILE* fp(_FILE*)malloc(sizeof(_FILE));assert(fp);fp-flagsSYNC_LINE;fp-filenofd;fp-capacitySIZE;fp-size0;memset(fp-buffer,0,SIZE);return fp;
}5.2.2_fwrite
void _fwrite(const void* ptr,int num,_FILE* fp)
{ //写到缓冲区里 memcpy(fp-bufferfp-size,ptr,num); //这里没有考虑缓冲区溢出问题 fp-sizenum; //判断是否要刷新 if(fp-flags SYNC_NOW) { write(fp-fileno,fp-buffer,fp-size); fp-size0;//清空缓冲区 } else if(fp-flags SYNC_LINE) { if(fp-buffer[fp-size-1] \n) //这里也没有考虑abc\ndef这种形式如果是这样的可以用for循环 { write(fp-fileno,fp-buffer,fp-size); fp-size0; } } else if(fp-flags SYNC_FULL) { if(fp-size fp-capacity) { write(fp-fileno,fp-buffer,fp-size); fp-size0; } }
} 5.2.3_fflush
void _fflush(_FILE* fp)
{ if(fp-size 0) write(fp-fileno,fp-buffer,fp-size); fsync(fp-fileno); //把数据强制从内核缓冲区刷新到磁盘 fp-size0;
} 这里引入了内核缓冲区下面解释。
5.2.4_fclose
void _fclose(_FILE* fp)
{ _fflush(fp); close(fp-fileno);
} 5.3main.c
行刷新 1 #includemyStdio.h2 3 4 int main()5 {6 _FILE* fp_fopen(log.txt,w);7 if(fp NULL)8 {9 perror(_fopen);10 return 1;11 }12 13 int cnt10;14 const char* msghello linux\n;15 while(1)16 {17 _fwrite(msg,strlen(msg),fp);18 sleep(1);19 printf(count:%d\n,cnt--);20 if(cnt 0) break;21 } 22 _fclose(fp);23 24 return 0;25 }退出刷新 1 #includemyStdio.h2 3 4 int main()5 {6 _FILE* fp_fopen(log.txt,w);7 if(fp NULL)8 {9 perror(_fopen);10 return 1;11 }12 13 int cnt10;14 const char* msghello linux;15 while(1)16 {17 _fwrite(msg,strlen(msg),fp);18 sleep(1);19 printf(count:%d\n,cnt--); 20 if(cnt 0) break;21 } 22 _fclose(fp); 23 24 return 0; 25 } 立即刷新 1 #includemyStdio.h2 3 4 int main()5 {6 _FILE* fp_fopen(log.txt,w);7 if(fp NULL)8 {9 perror(_fopen);10 return 1;11 }12 13 int cnt10;14 const char* msghello linux;15 while(1)16 {17 _fwrite(msg,strlen(msg),fp);18 _fflush(fp); 19 sleep(1);20 printf(count:%d\n,cnt--);21 if(cnt 0) break;22 } 23 _fclose(fp);24 25 return 0; 26 } 6.缓冲区和OS的关系 这里是由write函数直接把数据写到磁盘上的文件中吗 其实并不是这样的。 write并不是直接把缓冲区里的内容刷新到文件中在打开文件对应的struct file{}结构体中其他有一个方法指针还有一个指向内核缓冲区的指针系统会把FILE结构体里面缓冲区内容通过struct file{}找到内核缓冲区再由write()拷贝到内核缓冲区然后再由这个内核缓冲区把内容刷新到文件中区至于如何刷新和用户毫无关系我们所知道的行刷新等这是由语言层面所分类的而内核缓冲区刷新由OS自主决定。
那么我们该如何证明有这个内核缓冲区呢 超级大佬可以证明这里证明不了但是我们可以看到接口。 还有一个问题如果OS突然宕机了会发生什么情况 数据肯定会丢失。如果还是按照OS规定的内核缓冲区刷新策略肯定是不行的。 我们希望可以在用户层就告知OS内核缓冲区不要再给我缓存了赶紧把数据刷新到磁盘中。
这里介绍fsync函数 强制性把该文件对应的内核缓冲区数据持久到磁盘。就是OS不要给我缓存了赶紧把数据给我更新到磁盘上。