linux 输入输出流和文件描述符浅析

File descriptors 是一个整数 表内存管理的对象,该对象可以由进程进行读写。
一个进程可以获取File descriptors通过打开文件 目录 或者设备,通过创建管道
或者复制一个已经存在的descriptors,
文件描述符将文件 管道 设备都抽象化为一样的东西,都像字节流.文件描述符作为索引映射到
进程表中。每一个进程都有文件描述符的私有化控件,从0开始。进程读取文件 文件描述符为0
写入文件描述符为1 写错误信息到文件标识符为2.

            while((fd = open("console", O_RDWR)) >= 0){
8508             if(fd >= 3){
8509                     close(fd);
8510                     break;
8511                 }
8512         }
上面代码说明 shell对于控制台 总是要保证文件描述符的三种情况
(0标准输入 1标准输出,2标准错误流)都同时打开.

read函数从文件中读取字节
write函数向文件中写入字节
 read(fd, buf, n) 从fd标识的文件中,读取n个字节存入到buf缓冲区中去
 返回读取字节数量。read函数会持续从文件中读取字节,当读到文件末尾返回0 标识文件
 读取结束。
 write(fd, buf, n)从缓冲区读n个字节写入到fd标识的文件中,返回写入的字节数n,
 比n少,就会有错误发生。
 
 下面代码:从标准输入流读取字节 写入到标准输出流
 char buf[512];
int n;
for(;;){
    n = read(0, buf, sizeof buf);//0表示 输入流 从fd=0的文件中 读取sizeof buf个字节 存入缓冲区中
    if(n == 0)//n =0 读取字节失败
        break;
    if(n < 0){//发生错误
        fprintf(2, "read error\n");
        exit();
    }
    if(write(1, buf, n) != n){//将buf缓冲区的数据 前n个字节 读入1 标准输出流中
        fprintf(2, "write error\n");//如果写入的返回值 和n不等 则写入文件失败
        exit();
    }
}



Here is a simplified version of the code a shell runs for the
command cat <input.txt:



    char *argv[2];
    argv[0] = "cat";
    argv[1] = 0;
    if(fork() == 0) {
        close(0);
        open("input.txt", O_RDONLY);
        exec("cat", argv);
    }
    子进程close(0) 将文件为0的fd收回 ,这样open函数保证"input.txt"返回的fd为0 是最小的。
    此时 exec函数 让cat函数从input.txt 读取 fd=0标识标准输入流也就是从文件读取字节
    流到内存中去。
    重定向代码如下:
            struct redircmd {
8376         int type;
8377         struct cmd *cmd;
8378        char *file;
8379         char *efile;
8380         int mode;
8381         int fd;
8382         };
//下面代码在runcmd函数中
8430 case REDIR:
8431         rcmd = (struct redircmd*)cmd;
8432         close(rcmd->fd);//关闭当前文件描述符
8433         if(open(rcmd->file, rcmd->mode) < 0){//打开新的文件描述符 打开失败返回小于0的数
8434             printf(2, "open %s failed\n", rcmd->file);
8435             exit();
8436         }
8437         runcmd(rcmd->cmd);//继续调用本函数 开始重新判断
8438         break;


        
        对于shell执行 脚本命令的主循环关键代码如下    
            if(fork1() == 0)
8525         runcmd(parsecmd(buf));//之所以这里运用runcmd 而不是直接调用exec函数来执行
8526         wait();                            //为了应付各种情况的发生。
8527         }
8528     exit();
8529


父子进程中可以共享文件偏移量的如下面代码
if(fork() == 0) {
        write(1, "hello ", 6);
        exit();
} else {
        wait();
        write(1, "world\n", 6);
}
子进程将"hello "6个字节的字符串写入到标准输出流,退出
父进程 开始运行时候直接进入等待状态,等到hello 写完以后子进程退出后。
它开始将world 写入到标准输出流中去。最后打印出来 为hello world
说明父子进程公用一个文件描述符的偏移量。
类似的命令有(echo hello; echo world)>output.txt.


dup函数复制一个fd 并且返回的新的fd,和原来的fd公用一个文件偏移量。
例如下面代码 也会输出hello world!
fd = dup(1);
write(1, "hello ", 6);
write(fd, "world\n", 6);

进程之间的文件描述符,或者通过dup函数创建的文件描述符,都公用一个文件偏移量。
或者也可以说明代表一个特定的文件流。只有这两种情况,调用open函数打开同一个文件
返回的两个fd不会公用一个文件偏移量的。


Dup allows shells
to implement commands like this: ls existing-file non-existing-file > tmp1
2>&1. The 2>&1 tells the shell to give the command a file descriptor 2 that
is a duplicate of descriptor 1.
Both the name of the existing file and the error message for the
non-existing file will show up in the file tmp1. The xv6 shell doesn’t support
 I/O redirection for the error file descriptor, but now you know how to implement it.
 
 2>&1命令表示文件描述符2 是文件描述符1调用dup(1)的返回值。这样就会把existing-file non-existing-file
 写入到同一个文件temp中去了。错误流不支持重定向 现在通过dup就可以实现它了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值