"echo "Hello, World!" > output.txt"背后重定向的系统调用分析
Linux下打开终端, 运行命令 echo "Hello, World!" > output.txt
, 你会发现当前目录下多了一个名为 output.txt
的文件, 其内容就是 Hello, World!
.
今天,我们就来研究一下他背后的原理.
strace 查看命令的系统调用
我们不能直接strace echo "Hello, World!" > output.txt
,这样打印的系统调用是不完整的.
我们先创建一个包含 echo "Hello, World!" > output.txt
的脚本 test.sh
#!/bin/bash
echo "Hello, World!" > output.txt
接着我们执行脚本并使用strace
进行追踪
chmod +x test.sh
strace -o strace_output.txt ./test.sh
解析统计的系统调用
现在,我们开始对strace_output.txt进行分析,我可能会省略部分系统调用 第一步:执行脚本
execve("./test.sh", ["./test.sh"], 0x7ffc7fe04830 /* 19 vars */) = 0
第二步:打开输出文件
#打开或创建 output.txt 文件,并返回文件描述符 3。
openat(AT_FDCWD, "output.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
第三步:保存原标准输出的文件描述符
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10 #将文件描述符 1(标准输出)复制到 10,以保存原始的标准输出。
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
第四步:重定向标准输出到 output.txt 文件
dup2(3, 1) = 1 #将文件描述符 3(output.txt 文件)复制到 1,即将标准输出重定向到 output.txt 文件。
close(3) = 0
newfstatat(1, "", {st_mode=S_IFREG|0644, st_size=0, ...}, AT_EMPTY_PATH) = 0
第五步:写入数据到重定向的标准输出
write(1, "Hello, World!\n", 14) = 14 #将字符串 "Hello, World!\n" 写入到文件描述符 1,此时 1 被重定向到 output.txt 文件。
第六步:恢复原标准输出的文件描述符
dup2(10, 1) = 1 #将保存的文件描述符 10 复制回 1,恢复原始的标准输出。
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
文件描述符
我们都知道, linux系统下, 有一个系统级的打开文件表 (Open File Table).而每个进程的 PCB (Process Control Block) 中都有一个文件描述符表 (File Descriptor Table), 进程就是通过文件描述符表中的文件描述符对文件进行操作的. 接下来,我们再来看看这些系统调用执行后,OFT和FDT的变化: 初始状态 进程的 FDT:包含标准输入(fd=0),标准输出(fd=1),标准错误(fd=2)。 系统的 OFT:初始状态为空。
第一步:执行脚本
execve("./test.sh", ["./test.sh"], 0x7ffc7fe04830 /* 19 vars */) = 0
第二步:打开输出文件
#打开或创建 output.txt 文件,并返回文件描述符 3。
openat(AT_FDCWD, "output.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
- 系统的 OFT:添加一项指向 output.txt 的文件条目,记录文件状态、文件偏移量等信息。
- 进程的 FDT:增加一项 fd=3,指向 OFT 中的 output.txt 条目。
第三步:保存原标准输出的文件描述符
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10 #将文件描述符 1(标准输出)复制到 10,以保存原始的标准输出。
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
- 系统的 OFT:无变化。
- 进程的 FDT:增加一项 fd=10,指向 OFT 中原标准输出的条目。
第四步:重定向标准输出到 output.txt 文件
dup2(3, 1) = 1 #将文件描述符 3(output.txt 文件)复制到 1,即将标准输出重定向到 output.txt 文件。
close(3) = 0
newfstatat(1, "", {st_mode=S_IFREG|0644, st_size=0, ...}, AT_EMPTY_PATH) = 0
- 系统的 OFT:无变化。
- 进程的 FDT:fd=1 现在指向 OFT 中 output.txt 的条目,而不再指向原标准输出的条目。
第五步:写入数据到重定向的标准输出
write(1, "Hello, World!\n", 14) = 14 #将字符串 "Hello, World!\n" 写入到文件描述符 1,此时 1 被重定向到 output.txt 文件。
- 系统的 OFT:更新 output.txt 条目的文件偏移量和文件内容。
- 进程的 FDT:无变化。
第六步:恢复原标准输出的文件描述符
dup2(10, 1) = 1 #将保存的文件描述符 10 复制回 1,恢复原始的标准输出。
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
- 系统的 OFT:无变化。
- 进程的 FDT:fd=1 恢复指向 OFT 中原标准输出的条目,fd=10 被关闭。
License:
CC BY 4.0