基于strace分析x86 Linux可执行文件行为的作业疑问
问题背景
我有一项作业,给定一个x86 Linux可执行文件./asg-myst,在VirtualBox的Ubuntu 16.04.5中执行strace -f得到如下输出:
execve("./asg-myst", ["./asg-myst"], [/* 67 vars */]) = 0 uname({sysname="Linux", nodename="student-VirtualBox", ...}) = 0 brk(NULL) = 0x1191000 brk(0x11921c0) = 0x11921c0 arch_prctl(ARCH_SET_FS, 0x1191880) = 0 readlink("/proc/self/exe", "/home/student/Desktop/asgUBUNTU"..., 4096) = 42 brk(0x11b31c0) = 0x11b31c0 brk(0x11b4000) = 0x11b4000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/etc/passwd", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=2246, ...}) = 0 read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 2246 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0 write(1, "csdae+", 6) = 6 getpid() = 3794 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x1191b50) = 3795 wait4(-1, strace: Process 3795 attached <unfinished ...> [pid 3795] write(1, "795794=", 7) = 7 [pid 3795] lseek(3, -2167, SEEK_CUR) = 79 [pid 3795] exit_group(42) = ? [pid 3795] +++ exited with 42 +++ <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 42}], 0, NULL) = 3795 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3795, si_uid=1000, si_status=42, si_utime=0, si_stime=0} --- write(1, "42\n", 3) = 3 lseek(3, -2167, SEEK_CUR) = -1 EINVAL (Invalid argument) exit_group(0) = ? +++ exited with 0 +++
我的初步分析如下:
父进程调用getpid()系统调用,随后调用clone()。这一操作将父进程的getpid值存储到子进程中,子进程无需调用getpid()即可获取父进程的PID值。这一现象在添加-f参数运行的strace输出中可见。由于程序每次运行都会作为新进程启动,PID值始终变化。输出的6位数字模式与PID值相关:最右侧3位对应父进程getpid()的最后3位,最左侧3位则是该值加1。例如,若PID为3287,最后3位是287,则输出为288287。子进程调用exit_group(42)会修改si_status,父进程在正常退出前打印该si_status。
想请教下我的分析是否存在遗漏?
你的分析核心正确,补充几个关键遗漏细节
首先要肯定你的核心思路没问题,你抓住了父子进程PID的关联以及退出状态传递的关键逻辑,但还有几个容易被忽略的细节需要补充:
1. 文件描述符继承与lseek失败的原因
父进程在clone前已经打开了/etc/passwd(文件描述符3),并且执行了read(3, ..., 4096) = 2246——这说明已经读到了文件末尾(文件大小正好是2246字节),此时文件的偏移量停在2246。
子进程会继承父进程的所有打开文件描述符,而且文件表项(包括偏移量)是父子进程共享的。所以子进程执行lseek(3, -2167, SEEK_CUR)时,计算得2246-2167=79,这是合法的,偏移量被修改为79。
但父进程在子进程退出后执行同样的lseek(3, -2167, SEEK_CUR)时,当前偏移量已经是79,79-2167=-2088——文件偏移量不能为负数,所以返回EINVAL错误。这是你分析中遗漏的关键关联点。
2. 子进程PID的来源细节
你提到子进程无需调用getpid就能拿到父进程PID,这点没错。另外,子进程自己的PID其实是父进程PID加1(在系统没有其他进程并发创建的情况下,Linux的PID通常是递增分配的),所以子进程直接用父进程PID加1得到自己的PID,这就是输出795794=的原因:795是子进程PID(3795)的最后三位,794是父进程PID(3794)的最后三位。
3. 固定输出"csdae+"的意义
你没提到初始的write(1, "csdae+", 6),这部分是程序硬编码的固定输出,和PID逻辑无关,只是程序的前置输出内容。
总结
你的核心分析方向完全正确,只是遗漏了文件描述符共享导致的lseek失败、固定前置输出这两个细节,以及子进程PID与父进程PID的递增关联逻辑。整体推导已经覆盖了大部分关键流程~
内容的提问来源于stack exchange,提问作者Prashin Jeevaganth

