父子进程
用系统调用fork()函数实现子进程的创建,熟悉进程创建的执行过程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 打印主进程的 PID
printf("hello, world (pid: %d)\n", (int)getpid());
// 创建子进程
int rc = fork();
// 错误处理:如果 fork() 失败
if (rc < 0) {
fprintf(stderr, "fork failed\n");
exit(1);
}
// 子进程执行的部分
else if (rc == 0) {
printf("hello, I am the child (pid: %d)\n", (int)getpid());
}
// 父进程执行的部分
else {
printf("hello, I am the parent of %d (pid: %d)\n", rc, (int)getpid());
}
return 0;
}
wait 等待可以等待子进程完毕,避免子进程结束后资源无法收回成为僵尸进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
// 打印主进程的 PID
printf("hello, world (pid: %d)\n", (int)getpid());
// 创建子进程
int rc = fork();
// 错误处理:如果 fork() 失败
if (rc < 0) {
fprintf(stderr, "fork failed\n");
exit(1);
}
// 子进程执行的部分
else if (rc == 0) {
printf("hello, I am the child (pid: %d)\n", (int)getpid());
}
// 父进程执行的部分
else {
int status;
wait(&status);
printf("hello, I am the parent of %d (pid: %d)\n", rc, (int)getpid());
printf("%d",status);
}
return 0;
}
到现在可以明确一个道理,else 只是为了区分子进程和父进程谁先执行,但是子进程和父进程的代码块就是他们一定会执行的一些逻辑操作。你可以在不同代码块加上exit或者return 。把他当作一个程序去写。
编写一个程序,在调用fork()之前,让主进程访问一个变量(例如x),并将其值设置为某个值(例如100)。请问子进程中的变量是什么值?当子进程和父进程都改变x的值时,该变量会发生什么?
具体来说,fork()
会创建一个子进程,但在最开始,父进程和子进程的内存内容是相同的,并不是完全独立的。它们共享同一块内存区域,直到其中一个进程修改了某个变量(比如 x
),操作系统才会为修改该变量的进程分配一块新的内存副本。
关键点:
- 初始共享:
fork()
后,父子进程在内存中是共享的,直到其中一个进程进行写操作。父子进程并不会立刻各自拥有独立的内存副本。 - 写时复制(COW):当父进程或子进程修改某个共享的内存区域时,操作系统会为该进程创建一个独立的副本(这就是写时复制),确保两个进程不互相影响。
举个例子:
- 假设在
fork()
之后,父进程和子进程都访问变量x
,初始时它们的x
值相同。 - 只有当父进程或子进程修改
x
时,操作系统才会为修改x
的进程分配新的内存副本。
所以,尽管在 fork()
之后父子进程的内存空间是独立的,实际操作中,它们初始时通过写时复制共享了内存,直到其中一个进程做出修改。
wait 前面的逻辑
如果在父进程调用 wait()
函数之前还有其他的逻辑,父进程会执行这些逻辑,并 不会立即 等待子进程。
wait()
函数的作用是使父进程等待其子进程的结束,直到子进程退出并收集到它的退出状态。如果在调用 wait()
之前执行了其他操作,父进程在这些操作完成后才会进入等待状态。所以,如果在 wait()
之前有逻辑执行,那么父进程会先执行这些逻辑,直到程序执行到 wait()
,才会开始等待子进程的结束。我好像突然懂了。一般来讲子进程和父进程并发执行,但是如果说子进程先执行,其实父进程wait函数就没作用,但是父进程先执行,执行到wait会自动等待
执行命令
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h> // open() 等相关头文件
#include <unistd.h> // read(), write() 等相关头文件
#include <string.h> // strlen() 用于获取字符串长度
int main() {
// 打印主进程的 PID
printf("hello, world (pid: %d)\n", (int)getpid());
// 创建子进程
int rc = fork();
// 错误处理:如果 fork() 失败
if (rc < 0) {
fprintf(stderr, "fork failed\n");
exit(1);
}
// 子进程执行的部分
else if (rc == 0) {
char *args[] = { "ps", "aux", NULL };
execvp(args[0], args); // execvp 用于执行外部命令
printf("hello, I am the child (pid: %d)\n", (int)getpid());
return 1;
}
// 父进程执行的部分
else {
int status;
wait(&status);
printf("hello, I am the parent of %d (pid: %d)\n", rc, (int)getpid());
printf("%d\n",status);
}
return 0;
}