Linux0.11任务0中fork和pause的内嵌问题
产生进程1的fork和pause为什么要用内联函数而不能使用函数掉用的原因,网上的很多回答都是创造一个干净的用户态堆栈,至于不干净的用户堆栈空间会带来什么影响,无人提及
在理解下面代码的前,我们需要理解一下fork函数的特殊性,
fork会“两次返回”,一次子进程返回0,一次父进程返回子进程的pid
fork返回可能执行子进程,也可能执行父进程,先后顺序不确定
/* * fork程序是一个系统调用,使用_syscall0进程展开生成,0表示没有参数 * * #define _syscall0(type,name) \ * type name(void) \ * { \ * long __res; \ * __asm__ volatile ("int $0x80" \ * : "=a" (__res) \ * : "0" (__NR_##name)); \ * if (__res >= 0) \ * return (type) __res; \ * errno = -__res; \ * return -1; \ * * 根据前面的定义static inline _syscall0(int,fork) 展开 * * int fork() { * register eax __ret; * eax= __NR_fork; * int 0x80 * if (eax >= 0) * return int __res; * error = - __res * return -1 * } * INT 0x80是软中断函数,其调用流程为: * CPU通过中断向量0x80找到对应的描述符,此描述符包含了段选择子和偏移地址和其DPL * CPU检查当前的DPL是否小于描述符的DPL,如果小于拒绝执行 * CPU会从当前TSS段中找到中断处理程序的栈选择子和栈指针作为新的栈地址(tss.ss0, tss.esp0) * 如果DPL发生变化则将当前的SS, ESP, EFLAGS, CS, EIP压入新的栈中 * 如果DPL没有发生变化则将EFLAGS, CS, EIP压如新的栈中 * CPU从中断描述符中取CS:EIP作为新的运行地址 * * 为什么fork和pause需要inline执行呢 * 假设不是内联执行,当掉用fork时会ESP为A,将CS:IP压入当前系统堆栈中也就是任务0的用户堆栈中stack_start,此时ESP为B * 然后执行INT 80, 此指令会将SS ESP EFLAGS CS EIP ... 压入任务0的内核堆栈 * 在系统掉用过程中会产生任务切换,因此从系统调用退出时就可能有两种情况,一种是task0,一种是task1 * 怎么区分task0还是task1,INT 80返回后通过eax寄存器判断当前是task0还是task1 * 如果是task1执行,fork退出时用户态ESP恢复返回到init()函数中则不会有影响 * 如果是task0执行,fork退出时用户态ESP恢复返回到for(;;) pause执行,pause执行将CS:IP压入堆栈, * 和fork()系统掉用一样,pause从内核中返回时可能进入task1(fork)进程中 * fork函数使用RET返回,会使用堆栈中的CS:IP恢复执行 * 我们设想一下fork退出时会使用CS:IP进行恢复(能恢复吗),此时CS:IP是task0(pause)压入堆栈的返回地址 * 也就是说task1执行完毕后会进入到pause中执行,系统出现问题 * */ if (!fork()) { /* we count on this going ok */ init(); }
写到这里应该理解了,其实可以深度思考一下,fork,pause只要两个其中一个为inline就可以了,通过代码实际测试可以验证我的想法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!