回收内核空间资源 wait函数 waitid函数

摘要:本文主要讲述内核空间资源的收回,介绍wait和waitid函数的基本使用方法,以及它们之间的差异.

回收内核空间资源 wait和waitid函数

进程退出时释放了用户空间的资源,但是进程PCB并没有释放,这一工作显然不是自己完成,而是由当前进程的父进程完成的.

当一个进程正常退出或异常退出时,内核就向其父进程发送SIGCHLD信号.因为子进程终止是一个异步事件,所以这种信号也是内核向父进程发的异步通知.父进程可以选择忽略该信号(如果父进程设置了SA_NOCLDWAIT标志位(SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号),或者提供一个该信号发生时即被调用执行的函数。父进程可以显示地调用wait()函数和waitpid()函数来完成.

1. wait()和waitid()函数等待子进程结束

函数定义:

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

pid_t waitpid(pid_t pid, int *status, int options);

函数说明:

如果等待到任意一个子进程结束,将返回当前结束的子进程PID,同时将子进程退出时的状态存储在“_stat_loc"变量中.如果出错返回-1,错误原因存储在errno中.

调用wait()函数的父进程,如果其所有的子进程都还在运行,则父进程会阻塞,等待任意一个子进程结束,回收该子进程的内核进程资源.如果父进程没有任何子进程,则会出错.

如果进程由于接收到SIGCHLD信号而调用wait(),则可期望wait()会立即返回.但是如果在任意时刻调用wait(),则进程可能会阻塞.

参数status:

参数status是一个整型指针.如果status不是空指针,则终止进程的终止状态就放在它所指向的内存单元内.如果不关心终止进程的状态,则可将参数指定为空指针.

2. wait()和waitid()函数的区别

(1)在一个子进程终止前,wait()使其调用者阻塞,而waitpid()有一个选项,可使调用者不阻塞.

(2)waitpid()并不等待其在调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程(下文会详细介绍).

例子1:演示wait()函数的基本使用方式

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>

int main()
{
        pid_t pid,wait_pid;
        int status;
        pid = fork();    //调用fork函数
        if(pid==-1)
        {
                printf("fork errno:%m\n");
        }
        if(pid==0)
        {
                printf("my pid is :%d\n",getpid());
                sleep(5);
                exit(EXIT_SUCCESS);   //正常退出
        }
        else
        {
                wait_pid = wait(&status);   //等待子进程结束
                if(WIFEXITED(status))       //调用WIFEXITED宏
                {
                        printf("wait on pid:%d,normal exit ,return value is:%4x\n",wait_pid,WEXITSTATUS(status));
                }
                else if(WIFSIGNALED(status))
                {
                        printf("wait on pid:%d,recive signal,return value is:%4x\n",wait_pid,WIFSIGNALED(status));
                }
        }
        return 0;
}

程序正常运行退出,输出:

:my pid is :2573

:wait on pid:2573,normal exit ,return value is:   0

如果在程序运行后,向子进程发送一个信号,则输出:

:my pid is :2573

:wait on pid:2573,normal exit ,return value is:   1

在程序中还使用了WIFEXITED和WIFSIGNALED宏,其中宏WIFEXITED是用来判断进程是否是正常退出,如果是,此宏值为1.对于这种情况,可执行WEXITSTATUS(status),取子进程传送给exit,_exit或_Exit参数的低8位.

而宏WIFSIGNALED是用来判断进程是否是因为收到信号后而退出的,如果是,此宏值为1. 对于这种情况,可执行WTERMSIG(status),取使子进程终止的信号编号.

另外还有两个宏与wait和waitpid所返回的终止状态相关的.WIFSTOPPED和WIFCONTINUED.

3. waitpid()函数等待指定的子进程

如果一个进程有几个子进程,那么只要有一个子进程结束终止,wait()函数就会返回.但是现在我们想等待某一个子进程而已,其他子进程结束我们不关心,该怎么办?用户可以使用waitpid()函数来等待指定子进程结束.

定义:pid_t waitpid(pid_t pid, int *status, int options);

其中,第一个参数为进程PID值,对该值有一下的解析:

(1)pid==-1 等待任一子进程结束,此时waitpid()函数等价于wait()函数;

(2)pid>0   等待其进程ID与pid相等的子进程;

(3)pid==0  等待其组ID等于调用进程组ID的任一子进程;

(4)pid<-1  等待其组ID等于pid绝对值得任一子进程.

第二个参数为调用它的函数中某个变量地址,如果执行成功,则用来存储结束进程的结束状态.

第三个参数为等待选项,可以设置为0,亦可为WNOHANG(不阻塞等待)和WUNTRACED(报告状态信息).如果options设置为WNOHANG,而此时没有子进程退出,将立即返回0,不会像wait()那样永远等待下去.否则返回子进程PID,并在参数STAT_LOC中获取子进程的状态.

这是两个常数,可以用"|"运算符把它们连接起来使用,比如:

ret = waitpid(-1,NULL, WNOHANG|WUNTRACED);

如果我们不想使用它们,也可以把options设为0,比如:

ret = waitpid(-1, NULL, 0);

waitpid的返回值

由于waitpid函数参数较多,返回值也是根据参数的情况而定,综合分析一共有3种情况:

(1)如果执行成功正常返回,waitpid()返回子进程的PID;

(2)如果参数options设置了选项WNOHANG,而waitpid在执行时没有已退出的子进程,则返回0;

(3)如果调用出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程则出错.

4. waipid()函数提供了wait()函数没有的三个功能

(1)waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态.

(2)waitpid提供了一个wait的非阻塞版本.又是用户希望取得子进程的状态,但不想阻塞.

(3)waitpid支持作业控制.

例子2:等待指定的子进程

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t chlid_pid, wait_pid;
    chlid_pid=fork();
    if(chlid_pid<0)
        printf("Error occured on forking.\n");
    else if(chlid_pid==0)
    {
        sleep(5);
        exit(0);
    }
    do
    {
        wait_pid=waitpid(chlid_pid, NULL, WNOHANG);   //不阻塞,直接返回
        if(wait_pid==0)
        {
            printf("No child exited\n");
            sleep(1);
        }
    }while(wait_pid==0);    

    if(wait_pid==chlid_pid)
        printf("successfully release child %d\n", wait_pid);
    else
        printf("some error occured\n");
    return 0;
}

输出:

:No child exited

:No child exited

:No child exited

:No child exited

:No child exited

:successfully release child 2538

每隔一秒钟,父进程就检查一次当前是否有子进程退出.在前5秒钟,由于没有子进程退出,父进程调用waitpid(chlid_pid, NULL, WNOHANG)直接返回.子进程退出后,便可以处理其后事.

笔者:个人能力有限,只是学习参考...读者若发现文中错误,敬请提出.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

时间: 2024-10-27 16:40:36

回收内核空间资源 wait函数 waitid函数的相关文章

回收进程用户空间资源 exit()函数 _exit()函数 atexit()函数 on_exit()函数

摘要:本文主要讲述进程的终止方式,以及如何使用exit()函数来终止进程,回收进程用户空间资源:分析了exit()函数与_exit()函数,return关键字的差异.同时详细解读了如何使用atexit()和on_exit()函数来注册终止处理程序. 进程终止.回收资源 1.进程终止方式 在内核中,程序执行的唯一方法是调用一个exec函数.而进程自愿终止的唯一方法是显示或隐式地调用_exit()或_Exit(). 进程有5种正常终止方式: (1)常见的一种是,在main函数中执行return语句,

Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc的区别【转】

转自:http://www.th7.cn/system/lin/201606/167750.shtml 我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的,对应的用户空间内存释放函数是 free().注意:动态申请的内存使用完后必须要释放,否则会造成内存泄漏,如果内存泄漏发生在内核空间,则会造成系统崩溃. 那么,在内核空间中如何申请内存呢?一般我们会用到 kmalloc().kzalloc().vmalloc() 等,下面我们介绍一下这些函数的使

内核空间内存申请函数kmalloc kzalloc vmalloc的区别

我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的,对应的用户空间内存释放函数是 free().注意:动态申请的内存使用完后必须要释放,否则会造成内存泄漏,如果内存泄漏发生在内核空间,则会造成系统崩溃.  那么,在内核空间中如何申请内存呢?一般我们会用到 kmalloc().kzalloc().vmalloc() 等,下面我们介绍一下这些函数的使用以及它们之间的区别. kmalloc: void *kmalloc(size_t size, gfp

第3阶段——内核启动分析之start_kernel初始化函数(5)

内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数真正理解需要对linux相关体系有很深的了解后才能明白 代码如下: asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param[], __sto

(笔记)Linux内核中内存相关的操作函数

linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) 内核空间申请指定大小的内存区域,返回内核空间虚拟地址.在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间.有关buddy和slab,请参见<linux内核之内存管理.doc> gfp_t flags 的选项

linux 内核移植(七)——rest_init函数分析

代码在start_kernel函数运行的最后到了rest_init()函数中 1:rest_init()函数分析 (1)rest_init中调用kernel_thread函数启动了2个内核线程,分别是:kernel_init和kthreadd (2)调用schedule函数开启了内核的调度系统,从此linux系统开始转起来了. (3)rest_init最终调用cpu_idle函数结束了整个内核的启动.也就是说linux内核最终结束了一个函数cpu_idle.这个函数里面肯定是死循环. (4)简单

线程同步——内核对象实现线程同步——等待函数

1 对于内核对象实现线程同步,不得不提三点: 2 1)大多数内核对象既有触发也有未触发两个状态 3 比如:进程.线程.作业.文件流.事件.可等待的计时器.信号量.互斥量 4 2)等待函数:等待函数使线程自愿进入等待状态,直到指定的内核对象变为触发状态为止, 5 说道等待我们最喜欢不过了,因为这样不会浪费我们宝贵的CPU时间. 6 3)对于自动重置对象来说,当对象被触发时,函数会自动检测到(手动重置对象为触发是,函数也能检测到), 7 并开始执行,但是在函数会在返回之前使事件变为非触发状态. 8

函数对象,函数的嵌套,名称空间与作用域,闭包,装饰器,迭代器,内置函数

一,函数对象 函数对象:函数是第一类对象,即函数可以当做数据传递 1,可以被引用 2,可以做参数的传递 3,返回值可以是函数 4,可以当作容器类型的元素 二,函数嵌套 1,函数的嵌套调用 2,函数的嵌套定义 三,名称空间与作用域 名称空间:存放名字的地方叫名称空间,存放这些值与名字的绑定关系 查看内置名称的两种方法: 三种名称空间 1,内置名称空间:随着python解释器的启动而产生 2,全局名称空间:文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间 3,局部名称空间:调用函

Mac内核XNU的mach_vm子系统某个函数的代码逻辑

Mac内核XNU的mach_vm子系统某个函数的代码逻辑 mach子系统包括了很多内核功能的实现,比如VM子系统(内存管理).host子系统(主机硬件信息的处理).thread子系统(thread相关实现).exc子系统(异常处理相关),下面跟踪一下mach_vm子系统的mach_vm_allocate函数. (1)最顶层函数mach_vm_allocate: 它的实现,调用了两个函数(要么这个,要么另一个): ------ xnu/libsyscall/mach/mach_vm.c -----