c - 无法跟踪代码的逻辑和输出包括 dup() dup2() 和 fork()
问题描述
在操作系统考试的一个问题中,我一直试图通过以下代码进行跟踪,但没有成功。
问题说假设是:
至少 STDOUT 是打开的。
foo.txt 有 6 个字节的字符串“abcdef”
bar.txt 有字符串“567”
答案中的输出是 a567b。
有人可以跟踪这段代码并为我绘制文件描述符数组吗?提前致谢 ..
main() {
char buf[1024];
int fd_foo = open("foo.txt", O_RDONLY);
if (fd_foo != 4) {
dup2(fd_foo, 4);
close(fd_foo);
}
int fd_bar = open("bar.txt", O_RDONLY);
if (fd_bar != 0) {
close(0);
dup(fd_bar);
close(fd_bar);
}
switch (fork()) {
case -1: exit(1);
case 0:
dup2(4, 5);
close(4);
execl("child", "child", (char *)NULL);
break;
default:
wait(NULL);
read(4, buf, 1);
write(1, buf, 1);
}
} // main
子源文件内容。
int main() {
char buf[3];
read(5, buf, 1);
write(1, buf, 1);
read(0, buf, 3);
write(1, buf, 3);
}
解决方案
让我们main()
先看看主文件中的 ,然后看看main()
子文件中的流程。
在开始之前,让我们回顾一下 C 应用程序在 Linux 下启动时的标准文件句柄分配,来自stdout(3) - Linux 手册页。
在程序启动时,与流 stdin、stdout 和 stderr 关联的整数文件描述符分别为 0、1 和 2。预处理器符号 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 在 . (将 freopen(3) 应用于这些流之一可以更改与流关联的文件描述符编号。)
接下来让我们回顾一下dup()
系统调用的作用,来自DUP(2) Linux Programmer's Manual。
dup() 系统调用创建文件描述符 oldfd 的副本,使用编号最小的未使用文件描述符作为新描述符。
成功返回后,新旧文件描述符可以互换使用。它们引用相同的打开文件描述(参见 open(2)),因此共享文件偏移量和文件状态标志;例如,如果在其中一个文件描述符上使用 lseek(2) 修改了文件偏移量,则另一个文件描述符的偏移量也会改变。
主main()
文件中的注释如下所示:
main() {
char buf[1024];
// open the file foo.txt and then dup() the file handle received from the open()
// to be file handle number 4. Close the original file handle received.
int fd_foo = open("foo.txt", O_RDONLY);
if (fd_foo != 4) {
dup2(fd_foo, 4);
close(fd_foo);
}
// at this point the file handle 4 refers to the file foo.txt
// open the file bar.txt and make sure that the file handle received is file handle
// handle 0. if not then we close file handle 0 and dup the file handle to bar.txt
// File handle 0 is Standard Input or STDIN.
int fd_bar = open("bar.txt", O_RDONLY);
if (fd_bar != 0) {
close(0);
dup(fd_bar);
close(fd_bar);
// Since dup() looks for the lowest numbered file descriptor and we have
// just closed file descriptor 0, the result of dup() is to now have the
// file bar.txt to also be accessed through file handle 0.
}
// at this point we have the following file assignments:
// - file handle 0 which was to Standard In is now file bar.txt
// - file handle 1 is to Standard Out
// - file handle 2 is to Standard Error
// - file handle 4 is to file foo.txt
// now do a fork and the forked process will then execute the program whose
// source code is in the child source file. the child process will
// inherit our open file handles since we did not specify otherwise.
switch (fork()) {
case -1: exit(1); // if error just exit.
case 0:
// we be the forked process so we now
// - dup file handle 4 to file handle 5 and close 4
// - load in the child process on top of ourselves
// - loaded child process will inherit our open file handles
dup2(4, 5);
close(4);
execl("child", "child", (char *)NULL);
// at this point we now jump to the source code of the child source file
break;
default:
// we are the parent process so now lets just wait for the child to
// finish. Once it has finished we will then do some final file I/O
// then exit.
// Note: while the forked process closed file handle 4, the parent process
// has not so file handle 4 is still valid for the parent.
wait(NULL);
read(4, buf, 1); // read 1 character from file foo.txt
write(1, buf, 1); // write it to Standard Output
}
} // main
启动的子进程。
首先查看在子应用程序加载之前由分叉子进程设置的子进程的环境execl()
。
- 文件句柄 5 附加到打开的文件 foo.txt
- 文件句柄 0 附加到打开的文件 bar.txt
- 文件句柄 1 附加到标准输出
子文件源代码是
int main() {
char buf[3];
read(5, buf, 1); // read one character from foo.txt, the letter "a" from the string "abcdef"
write(1, buf, 1); // write it to Standard Out
read(0, buf, 3); // read three characters from bar.txt, the string "567"
write(1, buf, 3); // write them to Standard out
}
所有这些的结果是以下 I/O。
主进程启动、设置文件描述符、分叉并加载子进程
主进程等待子进程完成
子进程从文件 foo.txt 中读取“a”,而“bcdef”未读。
子进程将“a”写入标准输出
子进程从文件 bar.txt 中读取“567”,没有任何未读内容
子进程将“567”写入标准输出
子进程退出
主进程继续运行
主进程从文件 foo.txt 中读取“b”,留下“cdef”未读
主进程将“b”写入标准输出
主进程退出
其结果是“a567b”被这两个合作进程写入标准输出。它们共享相同的两个文件,尽管 foo.txt 由两个不同的文件描述符访问,并且它们共享相同的标准输出。
推荐阅读
- laravel-5 - 如何模拟 PHPUnit 的 Guzzle 请求
- python - 错误 - 迭代列表列表
- javascript - 如何使用对象数组一般地创建表格单元格和行
- python - 多索引 Python 中的反连接
- javascript - 从 BrowserWindow 电子类扩展时出错
- javascript - Angular js 到 Angular 5 的迁移
- spring-boot - OAUTH2:从客户端 UI 访问 REST 端点以保护 URI 返回anonymousUser
- semantic-ui - 在 Semantic UI React 中附加到下拉列表的标签和按钮
- linux - 不能 docker pull - 连接被拒绝
- javascript - 登录时url中的问号