进程控制
进程创建
fork函数
作用
fork函数的作用是用来创建一个新进程,新进程被称为子进程,而原来的进程称为父进程。
返回值
fork函数有两个返回值,给父进程返回子进程pid,给子进程返回0。
-
为什么有两个返回值?
在执行fork函数的时候,当fork函数的核心逻辑已经执行完成,准备
return
时,子进程就已经被创建了出来,此时就已经分为了两个执行流,所以return
就被执行了两次。 -
为什么给父进程返回子进程pid,给子进程返回0?
对于子进程来说,它的父进程是唯一的;而对于父进程来说,它可能会有多个子进程。为了更好的管理子进程,所以给父进程返回的是子进程的pid。
-
同一个
id
(存储fork的返回值),怎么会同时存储两个不同的值?当fork函数执行完之后,父子进程谁先返回,就谁先向变量
id
写入数据,由于进程具有独立性,会发生写时拷贝。因此,同一个id
,地址相同但内容却不同。
fork调用失败
当系统中的进程超过限制时,fork创建子进程会失败。下面是一个测试代码。
#include <stdio.h>
#include <unistd.h>
int main ()
{
int cnt = 1;
while(1)
{
pid_t id = fork();
if (id == 0)
{
while(1)
{
sleep(1);
}
}
else if(id < 0)
{
printf("fork error!!!\n");
break;
}
cnt++;
printf("%d\n", cnt);
}
return 0;
}
运行结果
进程终止
进程退出码
进程退出码是进程结束时返回的一个整数值,用来标定进程执行的结果是否正确。通常0表示进程正常结束,没有出现错误;非零表示出现了某种异常或错误情况,不同的非零值可能代表不同的错误类型或原因。
echo $?
:用于显示上一个命令的退出状态码。其中$?
是一个变量,叫做”退出状态变量“。
执行如下代码
#include <stdio.h>
int Add(int begin, int end)
{
int sum = 0;
for (int i = begin; i < end; i++) sum += i;
return sum;
}
int main()
{
int ret = Add(0, 100);
if (ret == 5050) return 0;
else return 1;
}
从上面的结果发现为什么只有第一次的退出码是1
,而之后都是0
呢?那是因为echo
本身就是一个进程,当第一次执行时,它显示的mytest
的退出码,当第二次执行时,它显示的是上一个echo
的退出码,由于上一个echo
进程是正常退出的,那么肯定显示的就是0
了。
常见的退出方法
- 从
main
返回 - 调用exit()
- _exit()
其中exit()是库函数,而_exit()是系统调用。exit()会在进程终止前刷新并关闭所有打开的缓冲区,将未输出的数据输出;_exit()
则不会刷新缓冲区,直接终止进程,可能导致未输出的数据丢失。
示例:
return
退出:return
是一种更常见的退出进程方法。执行return n
等同于执行exit(n)
,因为调用main
的运行时函数会将main
的返 回值当做exit
的参数。
进程等待
子进程退出,父进程如果不管,那么就有可能造成僵尸进程的问题,进而造成内存泄漏;另外,进程一旦变成僵尸进程,那么使用kill -9
命令都无法结束该进程。最后父进程派给子进程的任务完成的如何,父进程同样要知道,例如子进程是否正常退出,运行结果是否正确。因此父进程需要通过进程等待的方式回收子进程资源,获取子进程的退出信息。
进程等待方法
-
wait()
pid_t wait(int *status)
作用:用来暂停父进程的执行,获取子进程的终止状态,包括正常结束的退出码或异常终止的信号。
返回值:成功时返回子进程的pid,失败返回-1。
示例:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t id = fork();//创建子进程 //子进程运行5秒后退出 if (id == 0) { int cnt = 5; while(cnt) { printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt); cnt--; sleep(1); } exit(0); } //父进程 sleep(8); pid_t ret = wait(NULL); if (ret > 0) { printf("wait success:%d\n", ret); } }
-
waitpid()
pid_t waitpid(pid_t pid, int *status, int options)
参数:
- pid:
- pid = 1,等待任意一个子进程,与wait等效。
- pid > 0,等待指定pid的子进程。
- status:
- 是一个位图结构,用于获取子进程的终止状态信息。
- 可以通过宏(如
WIFEXITED
、WEXITSTATUS
等)来解析和提取具体的状态细节。
- options:
- WNOHANG: 非阻塞等待,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
status参数
在返回的
status
值中,低16位包含了子进程的终止信息。- 低 8 位(即 0 - 7 位):如果子进程是通过
exit
正常终止的,这 8 位保存了子进程的退出状态码。可以通过WEXITSTATUS(status)
宏来获取。如果WIFEXITED
宏对wait
或waitpid
返回的状态值进行判断后返回非零值,就表示子进程正常退出。 - 高 8 位(即 8 - 15 位):如果子进程是由于信号而终止的,这 8 位保存了导致子进程终止的信号编号。可以通过
WTERMSIG(status)
宏来获取。
可以使用
status & 0xFF
获取它的低八位,使用(status >> 8) & 0xFF
获取高八位。#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t id = fork();//创建子进程 //子进程运行5秒后退出 if (id == 0) { int cnt = 5; while(cnt) { printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt); cnt--; sleep(1); } exit(10);//退出码 } int status = 0; int ret = waitpid(id, &status, 0);//阻塞式等待 if (ret > 0) { if (WIFEXITED(status))//判断是否正常退出 { printf("exit code: %d\n", WEXITSTATUS(status));//获取退出状态码 } else printf("child exit not normal\n"); printf("wait success, exit code : %d, sig: %d\n", (status>>8) & 0xff, status & 0xff); }
- pid:
阻塞等待与非阻塞等待
阻塞等待:当子进程还在运行时,父进程一直在等待子进程结束运行,在等待的过程中父进程不会去执行其他任务。
非阻塞等待:当子进程还在运行时,父进程在等待的途中会去执行其他任务。
示例1:阻塞式等待
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();//创建子进程
//子进程运行5秒后退出
if (id == 0)
{
int cnt = 3;
while(cnt)
{
printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
cnt--;
sleep(1);
}
exit(10);
}
//父进程
sleep(5);
int status = 0;
pid_t ret = waitpid(id, &status, 0);//阻塞式等待
if (ret > 0)
{
printf("wait success:%d, sig number: %d, child exit code: %d\n", ret, (status & 0xff), (status >> 8) & 0xff);
}
sleep(5);
}
从上面的结果可以看出,在前3秒的时候子进程正在运行,而父进程在阻塞等待子进程执行结束,第4秒时,子进程执行结束,状态变为僵尸状态,第6秒的时候,父进程成功回收子进程,拿到了子进程的退出信号和退出码。
示例2:非阻塞轮询等待
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();//创建子进程
//子进程运行5秒后退出
if (id == 0)
{
int cnt = 5;
while(cnt)
{
printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
cnt--;
sleep(1);
}
exit(10);
}
//父进程
int status = 0;
while (1)
{
pid_t ret = waitpid(id, &status, WNOHANG);//非阻塞式等待
if (ret == 0)
{
printf("wait done, but child is running\n");
}
else if (ret > 0)
{
printf("wait success:%d, sig number: %d, child exit code: %d\n", ret, (status & 0xff), (status >> 8) & 0xff);
break;
}
else printf("wait failed\n");
sleep(1);
}
}
这段代码和上面的差不多,只不过子进程在执行任务的时候,父进程是以非阻塞的形式去等待子进程的。
标签:status,控制,cnt,int,pid,Linux,进程,include From: https://www.cnblogs.com/wzhiheng/p/18561298