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就可以实现它了。
版权声明:本文为博主原创文章,未经博主允许不得转载。