为了打印到终端,由 shell 启动的进程通过write()使用stdout从 shell(父进程)继承的已经打开的文件描述符进行系统调用来执行文件 I/O 。
对于管理这个程序的进程,包含“Hello world!”的数据如何?字符串从内存中的堆栈传输到shell的子进程?

这意味着如果希望打印到终端,则必须write()使用已打开的stdout文件描述符进行系统调用。
这是一个简单的示例程序,使事情更清楚:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
pid_t current_PID = getpid();
pid_t parent_PID = getppid();
printf("Current process ID: %d\n", current_PID);
printf("Parent process ID: %d\n", parent_PID);
return 0;
}
我们可以通过echo $$命令获取shell的进程ID (bash这里假设是shell)。
$ echo $$
29760
然后我们编译并运行示例程序(我简称为pid):
$ ./pid
Current process ID: 9071
Parent process ID: 29760
父进程 ID 是启动该pid进程的 shell 的 ID 。
要查看 CPU 级别发生的情况,这里是反汇编main()并解释相关操作的注释:
<main>:
push %ebp
mov %esp,%ebp
and $0xfffffff0,%esp
sub $0x20,%esp
call 8048340 <getpid@plt> # libc wrapper around getpid() system call
mov %eax,0x18(%esp) # write return value (PID) in register to stack
call 8048370 <getppid@plt> # libc wrapper around getppid() system call
mov %eax,0x1c(%esp) # write return value (PPID) in register to stack
mov 0x18(%esp),%eax # read from stack, write to register
mov %eax,0x4(%esp) # write register value to stack as 2nd arg to printf (PID)
movl $0x8048560,(%esp) # read format string from memory, write to stack as 1st arg to printf
call 8048330 <printf@plt> # libc wrapper around write() system call
mov 0x1c(%esp),%eax # read PPID saved on stack, write to register
mov %eax,0x4(%esp) # write PPID in register to stack as 2nd arg to printf
movl $0x8048578,(%esp) # read format string from memory, write to stack as 1st arg to printf
call 8048330 <printf@plt> # libc wrapper around write() system call
mov $0x0,%eax
leave
ret
这是strace示例程序的输出:
$ strace ./pid
execve("./pid", ["./pid"], [/* 53 vars */]) = 0
[ Process PID=10017 runs in 32 bit mode. ]
brk(0) = 0x943d000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7793000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=155012, ...}) = 0
mmap2(NULL, 155012, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfffffffff776d000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0P\234\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1763068, ...}) = 0
mmap2(NULL, 1772156, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfffffffff75bc000
mmap2(0xf7767000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1aa000) = 0xfffffffff7767000
mmap2(0xf776a000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfffffffff776a000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff75bb000
set_thread_area(0xffc17c00) = 0
mprotect(0xf7767000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0xf77b8000, 4096, PROT_READ) = 0
munmap(0xf776d000, 155012) = 0
getpid() = 10017
getppid() = 10014
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 13), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7792000
write(1, "Current process ID: 10017\n", 26Current process ID: 10017
) = 26
write(1, "Parent process ID: 10014\n", 25Parent process ID: 10014
) = 25
exit_group(0) = ?
+++ exited with 0 +++
注意execve()第一行的write()系统调用和底部的系统调用。
Linux 编程接口,2.2 外壳,pg。24
Linux 编程接口,4.1 概述,pg。69