1. 进程创建
- 代码测试
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4
5 int main()
6 {
7 pid_t pid = fork();
8 if (pid < 0)
9 {
10 perror("fork error\n");
11 return -1;
12 }
13
14 else if (pid == 0)
15 {
16 printf("子进程创建成功[%d]\n", getpid());
17 // 只有子进程才会进入
18 }
19 else if (pid > 0)
20 {
21 printf("父进程创建成功[%d]\n", getpid());
22 // 只有父进程才能进入
23 }
24 printf("父子进程都能进入[%d]\n", getpid());
25 return 0;
26 }
- 结果
[test@localhost ForkTest]$ make
gcc fork.c -o fork
[test@localhost ForkTest]$ ./fork
父进程创建成功[7368]
父子进程都能进入[7368]
子进程创建成功[7369]
父子进程都能进入[7369]
2. 进程终止
3. 进程等待
- 当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。僵尸进程会导致资源泄露.
- 当父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
- 进程等待的意义: 等待一个子进程的退出, 获取退出子进程的返回值, 并且释放子进程资源, 防止出现僵尸进程
- 一旦出现僵尸进程, 那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程
- 僵尸进程示例代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
sleep(5);
exit(0);
}
while(1)
{
printf("打LOL~~~\n");
sleep(1);
}
return 0;
}
- 运行结果
运行后立刻查看当前信息
[test@localhost ~]$ ps -aux|grep wait
test 8272 0.0 0.0 4216 352 pts/0 S+ 18:17 0:00 ./wait
test 8273 0.0 0.0 4212 84 pts/0 S+ 18:17 0:00 ./wait
5秒钟后查看当前信息
[test@localhost ~]$ ps -aux|grep wait
test 8272 0.0 0.0 4216 352 pts/0 S+ 18:17 0:00 ./wait
test 8273 0.0 0.0 0 0 pts/0 Z+ 18:17 0:00 [wait] <defunct>
- 通过进程等待的操作即可防止僵尸进程
- 使用wait()来进行进程等待
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
sleep(5);
exit(0);
}
// 添加wait();
wait(NULL);
while(1)
{
printf("打LOL~~~\n");
sleep(1);
}
return 0;
}
-
进程等待的两个操作
-
使用waitpid()先不对status进行探讨;
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
sleep(5);
exit(0);
}
// wait(NULL);
// int ret = waitpid(-1, NULL, 0);
// 使用非阻塞
// int ret = waitpid(-1, NULL, WNOHANG);
// 使用非阻塞此时还会产生僵尸进程, 故非阻塞的操作一般需要循环操作
while(waitpid(-1, NULL, WNOHANG) == 0)
{
printf("再等等\n");
sleep(1);
}
// 由于之前的循环操作子进程已经释放了
// 那么下面得到的ret就应该是为负数, 没有子进程出错
int ret = waitpid(-1, NULL, WNOHANG);
if (ret < 0)
{
perror("waitpid error");
}
else if(ret == 0)
{
printf("have no child exit\n");
}
else
{
printf("pid:%d child exited\n", getpid());
}
while(1)
{
printf("打LOL~~~\n");
sleep(1);
}
return 0;
}
- 结果
gcc wait.c -o wait
[test@localhost ForkTest]$ ./wait
再等等
再等等
再等等
再等等
再等等
waitpid error: No child processes
打LOL~~~
打LOL~~~
打LOL~~~
打LOL~~~
打LOL~~~
- 使用status保存信息
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(-1);
}
else if (pid == 0)
{
sleep(5);
exit(99);
}
// wait(NULL);
// int ret = waitpid(-1, NULL, 0);
// 使用非阻塞
// int ret = waitpid(-1, NULL, WNOHANG);
// 使用非阻塞此时还会产生僵尸进程, 故非阻塞的操作一般需要循环操作
int status = -1;
while(waitpid(-1, &status, WNOHANG) == 0)
{
printf("再等等\n");
sleep(1);
}
// 操作系统提供了WIFEXITED和WEXITSTATUS两个宏来对status进行操作
// 使用宏来判断和查看
if (WIFEXITED(status))
{
printf("child exit retval:%d\n", WEXITSTATUS(status));
}
// 和上边的宏功能相同
if (!(status & 0x7f))
{
printf("child exit retval:%d\n", (status) >> 8 & 0x0ff);
}
else
{
printf("process occured exception\n");
}
// 由于之前的循环操作子进程已经释放了
// 那么下面得到的ret就应该是为负数, 没有子进程出错
int ret = waitpid(-1, NULL, WNOHANG);
if (ret < 0)
{
perror("waitpid error");
}
else if(ret == 0)
{
printf("have no child exit\n");
}
else
{
printf("pid:%d child exited\n", getpid());
}
while(1)
{
printf("打LOL~~~\n");
sleep(1);
}
return 0;
}
- 执行结果
[test@localhost ForkTest]$ ./wait
再等等
再等等
再等等
再等等
再等等
child exit retval:99
child exit retval:99
waitpid error: No child processes
打LOL~~~
打LOL~~~
打LOL~~~
^C
4. 进程程序替换
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
- 程序替换功能十分强大
- 一般是让父进程创建子进程, 让子进程来替换
5. 实现minishell
- 通过进程控制的学习, 做一个minishell
- 博客链接:
https://blog.csdn.net/new_bee_01/article/details/103882136