目录
创新互联主营丰林网站建设的网络公司,主营网站建设方案,APP应用开发,丰林h5微信小程序搭建,丰林网站营销推广欢迎丰林等地区企业咨询什么是缓冲区?
刷新策略
模拟实现重定向
标准输出和标准错误有什么区别?
上文提到关闭1号文件(标准输出文件),根据文件描述符分配规则,再打开的文件的描述符就是1,看以下代码:
运行代码后,前10s中file.txt文件被创建出来了,但是没有内容:
过了10秒钟,程序退出了,内容才被刷新到了磁盘文件上
为什么数据没有被马上打印到文件上呢?
因为存在缓冲区,这里的缓冲区是C语言封装的缓冲区。
什么是缓冲区?缓冲区的本质其实就是一段内存,用来暂时存放数据的地方。
为什么要有缓冲区呢?
联系生活,比如快递的出现,让货物的配送交给物流,这样就可以解放你的时间 ,而且集中配送货物可以提高总体的效率。对于缓冲区而言,缓冲区对于进程而言效果也是类似的,首先缓冲区解放使用缓冲区的进程的时间,比如进程往磁盘写入的时候,可以直接往缓冲区去写入,进程不用等待写入磁盘完成,可以继续执行后续的代码,其次缓冲区集中处理缓冲区中数据的刷新,减少了IO次数,因为外设上IO的效率是很低的(相比其他硬件),故而提高整机的效率。
缓冲区在哪里?
将上面的代码中的printf函数换成系统调用write:
发现,在sleep期间,进程已经把数据打印到了file.txt上,可见write内部并没有缓冲区,printf封装了write,这个缓冲区是C语言的缓冲区。
在C语言的文件描述结构体FILE当中封装了该FILE对应的语言级别的缓冲区,每次打开文件,每个FILE都有对应的fd和属于自己的语言级别缓冲区,写入时先写到缓冲区,当缓冲区需要刷新的时候,内部再调用write刷新到对应文件中,那么何时刷新呢?这取决于C语言的刷新策略。
刷新策略1.无缓冲:也就是立即刷新
进程退出时:当进程正常退出前,C语言内部就会一系列工作,包括调用fclose函数刷新缓冲区
用户强制刷新:用户调用fflush函数直接刷新缓冲区
2.行缓冲:逐行刷新,当输入的字符中有\n就直接刷新之前缓冲区中的内容,显示器文件就是这个刷新策略。
3.全缓冲:也即当缓冲区写满了才刷新,块设备对应的磁盘文件是采用全缓冲刷新策略的。
现在就可以解释原本的代码为什么不立即刷新到磁盘上了,首先1号文件标准输出被关闭了,而打开了磁盘文件,printf会往1号文件打印,也就是说标准输出被重定向到了磁盘文件,而标准输出的刷新策略本来是行刷新的,现在改变了刷新策略变成了全缓冲刷新策略,因为缓冲区没有被写满所以在sleep期间也没有看见数据输出到文件中,当进程退出或调用fflush时,缓冲区就被刷新到了磁盘文件上,于是进程退出后数据就才刷新到了文件当中。
模拟实现重定向在上上篇博客中,实现了一个简易版的shell,现在根据现有知识再模拟实现一下重定向符号输出重定向>,追加重定向>>和输入重定向<添加入minishell吧。
的博客-博客">进程程序替换_且随疾风前行->的博客-博客
实现方法:
先用一些宏标识几种重定向方法,用全局变量记录一下要重定向到的文件,和如何进行重定向,比如下面代码:
在命令分割之前检查一下命令中是否有重定向符号,并记录之。
然后再在创建子进程之后程序替换之前,将要重定向的文件打开,并用dup2函数重定向之即可。
代码如下:
#include#include#include#include#include#include#include#include#define NONE_REDIR -1
#define INPUT_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2
int g_redir_flag=NONE_REDIR;
char* g_redir_filename =NULL;
void CheckRdir(char*command)
{
char*begin=command;
while(*begin)
{
if(*begin=='>')
{
if(*(begin+1)=='>')
{
g_redir_flag=APPEND_REDIR;
*begin='\0';
begin+=2;
while(*begin==' ')++begin;
g_redir_filename=begin;
break;
}
else
{
g_redir_flag=OUTPUT_REDIR;
*begin='\0';
begin+=1;
while(*begin==' ')++begin;
g_redir_filename=begin;
break;
}
}
else if(*begin=='<')
{
g_redir_flag=INPUT_REDIR;
*begin='\0';
begin+=1;
while(*begin==' ')++begin;
g_redir_filename=begin;
break;
}
else ++begin;
}
}
int main()
{
//保持完整的字符串
char inbuff[1024];
//保持打散之后的字符串
char *argv[32];
char g_envar[64];//保存环境变量
while(1)
{
printf("Please Enter#");
fflush(stdout);
memset(inbuff,'\0',sizeof(inbuff));
if(fgets(inbuff,sizeof inbuff,stdin)==NULL)
continue;
inbuff[strlen(inbuff)-1]='\0';
//检查重定向符号,并记录重定向方法
CheckRdir(inbuff);
argv[0]=strtok(inbuff," ");
int index=1;
if(strcmp(argv[0],"ls")==0)argv[index++]="--color=auto";
if(strcmp(argv[0],"ll")==0)argv[0]="ls",argv[2]="-l",argv[1]="--color=auto",index=3;
while(argv[index++]=strtok(NULL," "));//第二次调用解析原字符串时,传入NULL
if(strcmp(argv[0],"export")==0&&argv[1]!=NULL)
{
strcpy(g_envar,argv[1]);
if(putenv(g_envar)==0)
printf("%s export success!\n",argv[1]);
continue;
}
//内建命令
if(strcmp(argv[0],"cd")==0 && argv[1]!=NULL)
{
chdir(argv[1]);
printf("cd success!\n");
continue;
}
pid_t id=fork();
if(id==0)//child
{
if(g_redir_flag!=NONE_REDIR)
{
int fd;
switch(g_redir_flag)
{
case INPUT_REDIR:
fd=open(g_redir_filename,O_RDONLY,0666);
dup2(fd,0);
break;
case OUTPUT_REDIR:
fd=open(g_redir_filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
dup2(fd,1);
break;
case APPEND_REDIR:
fd=open(g_redir_filename,O_WRONLY|O_CREAT|O_APPEND,0666);
dup2(fd,1);
break;
default:
break;
}
}
execvp(argv[0],argv);
exit(0);
}
int status=0;
pid_t ret=waitpid(id,&status,0);
if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));
}
return 0;
}
编译运行程序,我们的minshell就可以进行重定向了:
在打印的时候标准输出和标准错误都往屏幕上打印,二者有区别吗?
标准输出和标准错误有什么区别?看如下代码:
运行程序,标准输出和标准错误都输出到屏幕中:
但是我们使用重定向符号将程序运行的重定向输出输出到文件时,只重定向了标准输出到file.txt文件中,标准错误还是打印到屏幕中:
这是因为重定向默认是使用输出重定向,在命令行中可以显式写出要如何重定向,
比如:
./a.out 1>stdout.txt 2>stderr.txt 让文件输出到标准输出文件的重定向输出到stdout.txt,标准错误输出的重定向输出到stderr.txt 文件:
还可以用 ./a.out>all.txt 2>&1表示让输出到标准输入和标准错误都重定向输出到all.txt文件中:
之所以有标准输出和标准错误,主要是用于区分哪些是程序的日常输出,哪些是程序运行中产生的错误。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧