大家好,这里是小缺,一名对嵌入式软件开发充满热情的探索者。这一篇文章主要内容是带大家了解进程的挂起,等待,退出等操作。
1.1 进程的挂起
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
功能:进程在一定的时间内没有任何动作,称为进程的挂起(进程处于等待态)
参数:
seconds:指定要挂起的秒数
返回值:
若进程挂起到sec指定的时间则返回0,若有信号中断则返回剩余秒数
注意:
进程挂起指定的秒数后程序并不会立即执行,系统只是将此进程切换到就绪态
案例
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
while(1)
{
printf("hello world\n");
//当运行到sleep函数后,程序会在此位置等待设定的秒数,当秒数到大后,代
码会接着执行
// sleep运行时进程为等待态,时间到达后会先切换到就绪态,如果代码继续运
行,再切换到运行态
sleep(2);
}
return 0;
}
现象就是每隔两秒打印一遍hello world
执行结果
1.2 程序的等待
wait
和 waitpid
是Unix和类Unix操作系统中的系统调用,它们用于让一个进程等待其子进程的结束,并回收子进程的资源。
1.2.1 wait 函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
调用wait函数的进程会挂起,直到它的一个子进程退出或收到一个不能被忽视的信号时才
被唤醒。
若调用进程没有子进程或它的子进程已经结束,该函数立即返回。
参数:
status:函数返回时,参数status中包含子进程退出时的状态信息。
子进程的退出信息在一个int中包含了多个字段,
用宏定义可以取出其中的每个字段
子进程可以通过exit或者_exit函数发送退出状态
返回值:
成功:子进程的进程号
失败:-1
取出子进程的退出信息
WIFEXITED(status)
如果子进程是正常终止的,取出的字段值非零。
WEXITSTATUS(status)
返回子进程的退出状态,退出状态保存在status变量的8~16位。 在用此宏前应先用宏WIFEXITED判断子进程是否正常退出,正常退出才可以使用此宏。
注意: 此status是个wait的参数指向的整型变量。
案例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid=fork();
if(pid<0)
{
perror("fail to fork");
return ‐1;
}
if(pid == 0)
{
int i = 0;
for(i=0;i<5;i++)
{
printf("this is son process\n");
}
//使用exit退出当前进程并设置退出状态
exit(2);
}
else
{
//使用wait在父进程中阻塞等待子进程的退出
//不接收子进程的退出状态
//wait(NULL);
//接收子进程的退出状态,子进程中必须使用exit或者_exit函数退出进程是发送退出状态
int status = 0;
wait(&status);
if(WIFEXITED(status) != 0)
{
printf("The son process return status: %d\n", WEXITSTATUS(status));
}
printf("this is father process\n");
}
return 0;
}
执行结果
1.2.2 waitpid 函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status,int options)
功能:等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
pid:指定的进程或者进程组
pid>0:等待进程ID等于pid的子进程。
pid=0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid
不会等待它。
pid=‐1:等待任一子进程,此时waitpid和wait作用一样。
pid<‐1:等待指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值
status:保存子进程退出时的状态信息
options:选项
0:同wait,阻塞父进程,等待子进程退出。
WNOHANG:没有任何已经结束的子进程,则立即返回。
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态
返回值:
成功:返回状态改变了的子进程的进程号;如果设置了选项WNOHANG并且pid指定的进程存
在则返回0。
失败:返回‐1。当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进
程,waitpid就会出错返回,这时errno被设置为ECHILD。
wait(status) <==> waitpid(‐1, status, 0)
案例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid=fork();
if(pid < 0)
{
perror("fail to fork");
return ‐1;
}
if(pid == 0)
{
int i = 0;
for(i=0;i<5;i++)
{
printf("this is son process\n");
sleep(1);
}
exit(0);
}
else
{
waitpid(pid, NULL, 0);
printf("this is father process\n");
}
return 0;
}
运行结果
如果没有 waitpid()
,子进程的退出状态不会被父进程捕获,这可能会导致僵尸进程的产生。
僵尸进程是指已经结束但其退出状态还没有被父进程读取的进程。
这些僵尸进程在进程表中占据空间,直到父进程使用 wait()
或 waitpid()
读取它们的退出状态。
1.3 进程的终止
exit
和 _exit
是在Unix和类Unix操作系统中的两个不同的函数,它们都用于终止当前进程的执行
1.3.1 _exit函数
#include <unistd.h>
void _exit(int status);
功能:退出当前进程
参数:
status:退出状态,由父进程通过wait函数接收这个状态
一般失败退出设置为非0
一般成功退出设置为0
返回值:无
_exit
函数定义在<unistd.h>
头文件中,是操作系统提供的一个低级终止函数。当调用
_exit
时,它会立即终止当前进程,不执行任何高级清理操作,例如不调用退出处理函数或不刷新文件流。因此,使用
_exit
函数可以实现更快的进程退出。
1.3.2 exit函数
#include <stdlib.h>
void exit(int status);
功能:退出当前进程
参数:
status:
退出状态,由父进程通过wait函数接收这个状态
一般失败退出设置为非0
一般成功退出设置为0
返回值:无
exit
函数定义在<stdlib.h>
头文件中,是标准C库提供的一个高级终止函数。当调用
exit
时,它会执行以下操作:
- 调用所有已注册的退出处理函数(通过
atexit
或on_exit
注册)。- 关闭所有打开的文件流(如
fclose
)。- 刷新所有输出缓冲区。
- 调用
_exit
函数来终止进程,并传递给_exit
一个退出状态(exit status)。
exit和_exit函数的区别:
exit为库函数,而_exit为系统调用
exit会刷新缓冲区,但是_exit不会刷新缓冲区
一般会使用exit
1.4 退出清理函数
#include <stdlib.h>
int atexit(void (*function)(void));
功能:注册进程正常结束前调用的函数,进程退出执行注册函数
参数:
function:进程结束前,调用函数的入口地址。
一个进程中可以多次调用atexit函数注册清理函数,
正常结束前调用函数的顺序和注册时的顺序相反
返回值:
成功:0
失败:非0
注意事项
atexit
只能注册返回类型为void
、无参数的函数。atexit
注册的函数不会接收任何参数。- 如果在
atexit
调用成功之后发生了错误(例如,程序异常退出),这些注册的函数可能不会被调用。- 在程序退出时,如果注册的函数执行失败,可能会影响到程序的退出状态。在Linux系统中,非零退出状态意味着程序异常终止。
atexit
函数是在程序终止时被调用的最后手段,它们是清理程序资源的最后机会。
案例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void clear_fun1(void)
{
printf("perform clear fun1 \n");
}
void clear_fun2(void)
{
printf("perform clear fun2 \n");
}
void clear_fun3(void)
{
printf("perform clear fun3 \n");
}
int main(int argc, char *argv[])
{
//atexit函数在进程结束时才会去执行参数对应的回调函数
//atexit多次调用后,执行顺序与调用顺序相反
atexit(clear_fun1);
atexit(clear_fun2);
atexit(clear_fun3);
printf("process exit 3 sec later!!!\n");
sleep(3);
return 0;
}
执行结果