// usr/src/bin/forkexec.rs
pub fn main() -> i32 {
println!("pid {}: parent start forking ...", getpid());
let pid = fork(); // 创建子进程
if pid == 0 {
// 子进程
println!("pid {}: forked child start execing hello_world app ... ", getpid());
exec("hello_world"); // 执行hello_world程序
100
} else {
// 父进程
let mut exit_code: i32 = 0;
println!("pid {}: ready waiting child ...", getpid());
assert_eq!(pid, wait(&mut exit_code)); //确认等待的子进程PID
assert_eq!(exit_code, 0); //确认退出码是0
println!("pid {}: got child info:: pid {}, exit code: {}", getpid() , pid, exit_code);
0
}
}
执行结果
Rust user shell
>> forkexec
pid 2: parent start forking ...
pid 2: ready waiting child ...
pid 3: forked child start execing hello_world app ...
pid 3: Hello world from user mode program!
pid 2: got child info:: pid 3, exit code: 0
Shell: Process 2 exited with code 0
>> QEMU: Terminated
进程切换
进程切换的要求
进程切换
进程生命周期的信息
CreateProcess(filename, CLOSE_FD)
CreateProcess(filename, CLOSE_FD, new_envp)
Unix进程创建/复制系统调用: fork/exec
用fork和exec创建进程的示例
int pid = fork(); // 创建子进程
if(pid == 0) { // 子进程在这里继续
// Do anything (unmap memory, close net connections…)
exec(“program”, argc, argv0, argv1, …);
}
用fork和exec创建进程的示例
int pid = fork(); // 创建子进程
if(pid == 0) { // 子进程在这里继续
// Do anything (unmap memory, close net connections…)
exec(“program”, argc, argv0, argv1, …);
}
main()
…
int pid = fork(); // 创建子进程
if (pid == 0) { // 子进程在这里继续
exec_status = exec(“calc”, argc, argv0, argv1, …);
printf(“Why would I execute?”); // 这行代码能执行到吗???
} else { // 父进程在这里继续
printf(“Whose your daddy?”);
…
child_status = wait(pid);
}
main()
…
int pid = fork(); // 创建子进程
if (pid == 0) { // 子进程在这里继续
exec_status = exec(“calc”, argc, argv0, argv1, …);
printf(“Why would I execute?”);
} else { // 父进程在这里继续
printf(“Whose your daddy?”);
…
child_status = wait(pid);
}
if (pid < 0) { /* error occurred */
在shell中调用fork()后加载计算器的图示
在shell中调用fork()后加载计算器的图示
在shell中调用fork()后加载计算器的图示
在shell中调用fork()后加载计算器的图示
在shell中调用fork()后加载计算器的图示
在shell中调用fork()后加载计算器的图示
int main()
{
pid_t pid;
int i;
for (i=0; i<LOOP; i++)
{
/* fork another process */
pid = fork();
if (pid < 0) { /*error occurred */
fprintf(stderr, “Fork Failed”);
exit(-1);
}
else if (pid == 0) { /* child process */
fprintf(stdout, “i=%d, pid=%d, parent pid=%d\n”,I,
getpid() ,getppid());
}
}
wait(NULL);
exit(0);
}
wait()系统调用用于父进程等待子进程的结束
等待僵尸子进程时,wait()立即返回其中一个值
如果是孤儿子进程,则由root进程负责等待并回收
僵尸进程:已经执行sys_exit系统调用,但还没有被父进程通过sys_wait系统调用回收其进程控制块的子进程。
孤儿进程:其父进程先退出的子进程。
进程管理相关的系统调用可能会影响进程的状态
The fork system call is one of Unix's great ideas.
-- https://cs61.seas.harvard.edu/site/2018/WeensyOS/
但是!
但是!
For implementation expedience [Ritchie, 1979]
结论
Please, stop teaching students that fork is good design
https://www.infoq.cn/article/BYGiWI-fxHTNvSohEUNW 当 Unix 为 PDP-11 计算机(其带有内存转换硬件,允许多个进程保留驻留)重写时,只为了在 exec 中丢弃一个进程就复制进程的全部内存就已经很没效率了。我们怀疑在 Unix 的早期发展阶段,fork 之所以能幸存下来,主要是因为程序和内存都很小(PDP-11 上有只 8 个 8 KiB 页面),内存访问速度相对于指令执行速度较快,而且它提供了一个合理的抽象。这里有两点很重要: