重定向_缓冲区

目录

 

重定向

文件属性操作

 浅谈重定向​编辑

 深入重定向

dup2

缓冲区

缓冲区的理论理解

 代码分析


 

重定向

文件属性操作

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

656e1ff010adf7e3edfe8f9e0ff4eaa5.png

b36ebf7547edd6072c1ce23fd407d721.png

9aa3ff127b870d49616789d9ae5cb0de.png

ssize_t read(int fd, void *buf, size_t count);文件内容操作
read() 函数用于从文件描述符 fd 指定的文件中读取数据,将读取的数据存储到buf指向的内存缓冲区中,最多读取count字节的数据。函数返回实际读取的字节数,如果返回值为0,则表示已到达文件末尾;如果返回值为-1,则表示出现错误,此时会设置errno变量以指示具体的错误类型。

 浅谈重定向2f4c8525ae6be052e5450a16048ee234.png

f572b4d20c4b9846b2609f6849d7d3d4.png

fileno是struct FILE 封装了fd

 那么重定向的本质就是在内核中改变文件描述符表特定下标的内容,和上层无关。接口调用只管1,不管指向谁。

说白了就是上层不变,底层改变

 当不加fflush(stdout)时数据出不来,而加fflush(stdout)数据就出来了
47539653ef246b2a26475fafd4d273c6.png
c93a6dcddd425abb7d1a21a430298a5d.png
正常进程结束后会刷新缓冲区,但是这次不正常,在return之前要刷新的时候我close了,数据还在1号stdout那里,但是我无法通过1号写到os里,所以直接没了。把close注释掉就可以。

 深入重定向

dup2

那么每次都要fflush和close很麻烦,要没有简洁的方法?就是dup2()

      #include <unistd.h>

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);

5b4ee9055b30902e8f595530c5ac14e9.png

 #include<stdio.h>
 #include<stdlib.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<sys/stat.h>
 #include<fcntl.h>
 int main(){
 int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
 if(fd<0){
          perror("open");
          return 2;
     }
      dup2(fd,1);                                                                
     printf("fd:%d\n",fd);
      fprintf(stdout,"fprintf,fd:%d\n",fd);
      close(fd);
      return 0;
  }

 如果不关闭文件描述符,数据可能不会立即刷新到文件中。这是因为操作系统通常会对文件进行缓冲,直到缓冲区满或文件被关闭时,数据才会被写入磁盘。

如果程序长时间运行而不关闭文件,可能会出现以下情况:

  1. 数据未写入:输出可能会滞留在缓冲区中,而不及时写入文件。
  2. 数据丢失风险:在程序异常终止的情况下,缓冲区中的数据可能会丢失。

为确保数据及时写入文件,使用 fflush(stdout) 可以手动刷新输出缓冲区,但最好还是在不再需要写入时调用 close(fd)。这样可以确保资源被妥善管理,并确保所有数据都已写入。

dc291d62db0da7b066bfd31f20a58eb2.png

最终不在显示屏输出数据,而是在log.txt中输出因为上层调用fd值1,但是fd值1指向了log.txt 

缓冲区

缓冲区的理论理解

缓冲区:用户缓冲区和内核缓冲区。
几乎所有的缓冲区都有两种好处:解耦和提高效率

提高效率:主要提高的是使用者的效率和提高刷新IO的效率

我们知道调用系统调用是有成本的(没有量化的标准),所以尽量少调用效率就提高了。

比如我们使用10次printf/fprintf把数据放到c语言提供的缓冲区,10次调用printf数据都在c语言提供的缓冲区,最后只需调用一次系统调用就可以把数据写到os缓冲区,效率提高了

缓冲区是什么?其实就是一段内存空间。由c语言维护就叫语言级缓冲区,由c++维护就叫语言级缓冲区,由os维护就叫内核级缓冲区。

 为什么要有缓冲区?要给上层提供良好的IO体验,(并且对大家都好,我os积攒一大批数据往硬件上刷新,我只需要硬件配合我一次。我语言层积攒一大批数据往os刷新只需os配合我一次),间接提高整体效率

 缓冲区怎么办?

a.刷新策略

1. 立即刷新(相当于这个缓冲区存在价值不大,相当于没缓冲而且多了次拷贝)--fflush/fsync

2. 行刷新。显示器(显示器是给用户看的,所以行刷新主要照看用户的习惯,不快不慢)

3. 全缓冲。缓冲区写满才刷新,对应普通文件

b. 特殊情况

1. 进程退出,强制退出

2. 强制

策略代表都适用,所以上述策略用户级能用,内核级也能用,但是我们不关心内核策略我们不关心,所以只关心用户级。

 代码分析

 #include<unistd.h>
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 int main(){
  6     printf("hello printf\n");
  7     fprintf(stdout,"hello fprintf\n");
  8     const char*ch="hello write\n";
  9     write(1,ch,strlen(ch));
 10   // fork();                                                             
 11     return 0;                    
 12 } 

a6e2df583648195b31b6e3fc75835786.png

fork之后父子进程各有一份C语言输出缓冲区了,所以父子进程都会输出,但是write因为不向缓冲区中写入,所以子进程创建出来无法输出write的数据

vim /usr/include/libio.h 查看c语言提供的缓冲区,同时也可以看出条件编译
08171b3759f82d1244125f2d62150768.png
那么c++也得有,其他任何语言也得有

进程的程序替换不会影响进程关联的或者打开的文件。

代码详解

重定向shell

输入重定向:

f426339cb33eaf703dc31c0862b26e38.png

filename是指针加下标方式

 

测试代码:
7281309538901120c73a83c888b1d4a0.png

 测试:
003c7de8e75d2410a4786beec7aae25d.png

 

完整代码: 
 

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h> 
#include<sys/stat.h>
#include <fcntl.h>
#include<errno.h>
#include<ctype.h>
#define SIZE 512
#define NUM 32 //最多32个选项
#define skippath(p)do{p+=(strlen(p)-1);while(*p!='/')p--;}while(0)
#define skipspace(cmd,pos) do{while(1){if(isspace(cmd[pos]))pos++;else break;}}while(0)
#define NONE_REDIR 0 //无重定向
#define IN_REDIR   1 //输入重定向
#define OUT_REDIR  2 //输出重定向
#define APP_REDIR  3 //追加重定向
int redir_type=NONE_REDIR;//重定向类型
char*filename=NULL;//文件名
char cwd[SIZE*2];
int lastcode=0;
const char* getpwd(){
    const char* cwd=getenv("PWD");
    if(cwd==NULL)return "None";
    return cwd;
}
const char* getname(){
    const char* name=getenv("USER");
    if(name==NULL)return "None";
    return name;
}
const char* gethostname(){
    const char* hostname=getenv("HOSTNAME");
    if(hostname==NULL)return "None";
    return hostname;
}
const char* gethome(){
    const char*home=getenv("HOME");
    if(home==NULL)home="/root";
    return home;
}
void makeline(){
    char line[SIZE];//命令行
    const char* hostname=gethostname();
    const char* name=getname();
    const char* cwd=getpwd();
    skippath(cwd);
    snprintf(line,sizeof(line),"[%s@%s %s]>",name,hostname,strlen(cwd)==1?"/":cwd+1);
    printf("%s",line);
    fflush(stdout);
}
int getcommandline(char usercommand[],size_t n){
    char*s=fgets(usercommand,n,stdin);
    if(s==NULL)return -1;
    s[strlen(s)-1]='\0';
    return strlen(usercommand);
}
char*gargv[NUM];
void splitcommand(char usercommand[],size_t t){
   (void)t;//代表暂时不用t
    gargv[0]=strtok(usercommand," ");
    int dex=1;
    while((gargv[dex++]=strtok(NULL," ")));
}
void exectuecommand(){
    pid_t id=fork();
    if(id<0)exit(-1);
    else if(id==0){
        if(filename!=NULL){//文件不为空才执行重定向
        //重定向设置
        if(redir_type==IN_REDIR){
            int fd=open(filename,O_RDONLY);//打开文件
        dup2(fd,0);//替换
        }
        else if(redir_type==OUT_REDIR){
            int fd=open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
            dup2(fd,1);
        }
        else if(redir_type==APP_REDIR){
            int fd=open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
            dup2(fd,1);
        }
        else{}
        }
        //child
        execvp(gargv[0],gargv);
        exit(errno);
    }
    else{
        //father
        int status=0;
        pid_t rid=waitpid(id,&status,0);
        if(rid>0){
            //success
             lastcode=WEXITSTATUS(status);
            if(lastcode!=0)printf("%s:%s:%d\n",gargv[0],strerror(lastcode),lastcode);
        }
}
}
void cd(){

    const char*path=gargv[1];
    if(path==NULL)path=gethome();
    chdir(path);
    //刷新环境变量
    char tmp[SIZE*2];
    getcwd(tmp,sizeof(tmp));
    snprintf(cwd,sizeof(cwd),"PWD=%s",tmp);
    putenv(cwd);
}
int cheak(){
    int yes=0;
    const char*enter=gargv[0];
    if(strcmp(enter,"cd")==0){
        yes=1;
        cd();
    }
    else if(strcmp(enter,"echo")==0&&strcmp(gargv[1],"$?")==0){
      yes=1;
        printf("%d\n",lastcode);
        lastcode=0;
    }
    return yes;
}
void checkdir(char*cmd){
    int pos=0;
    int end=strlen(cmd);
    while(pos<end){
        if(cmd[pos]=='>'){
            if(cmd[pos+1]=='>'){
                cmd[pos++]=0;
                pos++;
                redir_type=APP_REDIR;
                skipspace(cmd,pos);
                filename=cmd+pos;
            }
            else {
                cmd[pos++]=0;
                redir_type=OUT_REDIR;
                skipspace(cmd,pos);
                filename=cmd+pos;
            }
        }
        else if(cmd[pos]=='<'){
            cmd[pos++]=0;//将左右分开
            redir_type=IN_REDIR;
            skipspace(cmd,pos);
            filename=cmd+pos;
        }
        else{
            pos++;
        }
    }
}
int main()
{   
    int quit=0;
    while(!quit){//一直执行
    //0.重置   
     redir_type=NONE_REDIR;//重定向类型
     filename=NULL;//文件名
    makeline();//1.命令行
    char usercommand[SIZE];//命令字符串
    int a=getcommandline(usercommand,sizeof(usercommand));//2.获得字符串
    if(a<=0)return 1;
    checkdir(usercommand);//2.1检查是否有重定向
    splitcommand(usercommand,sizeof(usercommand));//3.分割字符串
   int n=cheak();//4.检查内建命令
   if(n)continue;
    exectuecommand();//n.执行命令
    }
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangsir.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值