699 words
3 minutes
Labyrinth
2026-07-23
理论
进程自身只看得到寄存器和内存里的内容,如果想获取其他自身的信息需要和OS交互。
一种是可以通过getpid之类的的syscalls。
一种是/proc目录下的文件,/proc是一个伪文件系统,挂载在内存里不落盘。
Unix通过fork和execve两个API来复制和复位状态机。
pid_t pid = fork();if (pid == 0) { // 子进程:我现在是父进程的完整克隆 // 但我可以用自己的系统调用"重新配置自己"
close(0); // 关掉继承的 stdin open("/dev/null", O_RDONLY); // 重定向到 /dev/null dup2(pipe_fd, 1); // stdout 重定向到管道 chdir("/some/dir"); // 切换工作目录 setenv("PATH", "...", 1); // 修改环境变量 setgid(new_gid); // 降权 setuid(new_uid); // 降权 // ... 任何你能想到的状态修改
execve("/bin/myprog", argv, envp); // 最后才加载新程序} else { ...}上面的代码创建了一个进程,在execve之前用代码自由的执行了降权之类的操作。
这体现了Unix的"机制而非策略"思想:内核提供 fork和 execve两个正交的机制,组合方式由用户态决定。
BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);相对地,Windows设计spawn这类API需要维护非常多参数来尽量满足创建不同种类进程需要的能力。
教科书上一个简化的进程地址空间如下。
┌─────────┐ 高地址│ stack │├─────────┤│ ↓ ││ mmap ││ ↑ │├─────────┤ ← break│ heap │├─────────┤│ bss ││ data ││ text │└─────────┘ 低地址可以通过brk系统调用设置break的位置来控制堆区大小。历史上C语言里有一个sbrk函数是更高级的封装,现在被废弃了。
mmap操纵内存映射区。
想要看一个Linux进程具体的进程地址空间信息,可以用下面的命令。
ubuntu@VM-0-9-ubuntu:~/os2026$ cat /proc/self/maps5647336d4000-5647336d6000 r--p 00000000 fc:02 926 /usr/bin/cat5647336d6000-5647336da000 r-xp 00002000 fc:02 926 /usr/bin/cat5647336da000-5647336dc000 r--p 00006000 fc:02 926 /usr/bin/cat5647336dc000-5647336dd000 r--p 00007000 fc:02 926 /usr/bin/cat5647336dd000-5647336de000 rw-p 00008000 fc:02 926 /usr/bin/cat56476abe5000-56476ac06000 rw-p 00000000 00:00 0 [heap]7f76c0218000-7f76c023a000 rw-p 00000000 00:00 07f76c023a000-7f76c0523000 r--p 00000000 fc:02 2694 /usr/lib/locale/locale-archive7f76c0523000-7f76c0526000 rw-p 00000000 00:00 07f76c0526000-7f76c054e000 r--p 00000000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c054e000-7f76c06e3000 r-xp 00028000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c06e3000-7f76c073b000 r--p 001bd000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c073b000-7f76c073c000 ---p 00215000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c073c000-7f76c0740000 r--p 00215000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c0740000-7f76c0742000 rw-p 00219000 fc:02 11781 /usr/lib/x86_64-linux-gnu/libc.so.67f76c0742000-7f76c074f000 rw-p 00000000 00:00 07f76c0758000-7f76c075a000 rw-p 00000000 00:00 07f76c075a000-7f76c075c000 r--p 00000000 fc:02 600 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.27f76c075c000-7f76c0786000 r-xp 00002000 fc:02 600 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.27f76c0786000-7f76c0791000 r--p 0002c000 fc:02 600 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.27f76c0792000-7f76c0794000 r--p 00037000 fc:02 600 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.27f76c0794000-7f76c0796000 rw-p 00039000 fc:02 600 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.27fffd6eb6000-7fffd6ed8000 rw-p 00000000 00:00 0 [stack]7fffd6f00000-7fffd6f04000 r--p 00000000 00:00 0 [vvar]7fffd6f04000-7fffd6f06000 r-xp 00000000 00:00 0 [vdso]ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]/proc/self是一个符号链接,指向"正在读它的那个进程"的PID目录。
所以上面的命令返回的是cat这个进程的地址空间映射信息。
实验
简单的模拟,热身实验。