函数简介篇——进程同步函数

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  Linux网络编程、linux系统编程

前提概念

僵死(zombie)进程

  1️⃣目的:父进程可获取终止的子进程信息
  2️⃣概念:父进程未终止,子进程先终止,将终结的子进程设置为僵死进程。
  3️⃣补充:只保留最小的概要信息——一些保存着有用信息的内核数据结构。
      僵死进程等待父进程查询自己信息,若父进程获取子进程信息,则子进程消失,否则保持僵死状态

一 、wait()函数

1、函数介绍:

(1)函数功能

  用于父进程与子进程的同步。
  暂停父进程进入睡眠状态,一旦子进程终止(正常/异常)时【异步事件(在任意时刻都可发生)】,父进程会获取子进程终止状态后重新执行(结束wait系统调用)。
    ①若有多个子进程在执行,那么父进程中的wait()在第一个子进程结束时,恢复父进程。
    ②若所有子进程都还在运行,则阻塞。
    ③若无任何子进程,则立即出错返回。

项目 说明
函数原型 pid_t wait(int *wstatus);
头文件 sys/types.h、sys/wait.h
参数说明 wstatus:整数指针或null指针
返回值 ①返回结束的子进程的进程标识符
②返回-1,表示没有子进程结束。errno中含有出错代码ECHILD
注意 参数`wstatus`用于获取子进程exit系统调用的参数值(只读取最低的1字节),若不关心则可设为`NULL`。
(2)errno错误可能值:
项目 说明
ECHILD 调用进程没有任何子进程
EINTR 等待子进程结束时,收到了一个信号, wait()提前返回了。
(3)检查子进程返回状态的宏:

  位于:stdlib.hsys/wait.h

宏名 说明
WIFEXITED(status) 若进程通过系统调用_exit()或exit()正常退出,该宏返回为真(非零值)
WEXITSTATUS(status) 若 WIFEXITED (status)返回为真,该宏返回由子进程调用_exit( status)或exit (status)时设置的调用参数status值
WIFSIGNALED(status) 若进程是由信号引起的进程终止,该宏返回为真(非零值)
WTERMSIG(status) 若WIFSIGNALED (status)返回为真,该宏返回导致子进程退出的信号编号
WCOREDUMP(status) 若收到信号发生主存信息转储,则该宏返回真(非零值)
WIFSTOPPED(status) 若子进程暂停(停止并可继续执行),则该宏返回非零值。可通过ptrace()跟踪(只有当实现一个调试器才有用)
WSTOPSIG(status) 如果 WIFSTOPPED (status)返回非0,该宏返回导致子进程停止的信号编号
WIFCONTINUED(status) 若子进程暂停(停止并可继续执行),则该宏返回非零值。可通过ptrace()跟踪(只有当实现一个调试器才有用)
新标准将它定义为waitpid()

  PS:
    ①WIFEXITED(status)常与WEXITSTATUS(status)搭配使用。【正常退出结束】
    ②WIFSIGNALED(status)常与WTERMSIG(status)搭配使用。【信号引起结束】
    

(4)wait系列其他函数

  后面的数字表示对应的参数个数
  ①wait3():等待任何一个子进程改变状态(结束)
  ②wait4():等待某个特定子进程改变状态(结束)
  PS:
  wait3()wait4()不是POSIX定义,最好不使用,虽然几乎所有UNIX系统都支持。

#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3 (int *status, int options, structrusage *rusage);
pid_t wait4 (pid_t pid, int *status, int options,struct rusage *rusage);
(5)与waitpid()函数对比

  1️⃣子进程终止前,wait()使调用者阻塞,waitpid()可通过选项使调用者不阻塞。
  2️⃣waiitpid()并不等待调用后的第一个终止子进程,有若干选项控制等待进程。

2、示例实践:

(1) 源程序:wait.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main (int argc,char* argv[])
{
    int status = 0;
    pid_t pid = 0;                                       //子进程PID
    pid_t wpid =0;                                       //第一个结束的子进程PID

    /**
     *  Step 1:成功创建子进程并立即执行不同形态退出动作
     */
    pid = fork();
    if (pid < 0)
    {
        perror("fork");
    }
    else if(pid > 0){
        printf("I am parent process %d\n",getpid());
    }
    else if(pid == 0)
    {
        printf("I am child process %d\n",getpid());
        /* 情形一:子进程没有返回 (wait返回-1)*/
        //空
       
        /* 情形二: 子进程正常退出 (WIFEXITED)*/
        // exit(1);                                        // 等效于 return 1;  
        // exit(0);                                        // 等效于 return 0;   

        /* 情形三:  子进程得到信号没被捕捉退出 (WIFSIGNALED)*/
        /* WTERMSIG 用该宏返回导致子进程退出的信号值 */
        // abort();                                        //向自己发送SIGABRT信号

        /* 情形四: 多子进程 */
        sleep(10); 
    }

    /* 情形四: 多子进程 */
    if(!fork())         
    {
        printf("I am sibling process %d\n",getpid());
        exit(0);                                           //直接退出
    }

    /**
     *  Step 2:父进程调用wait系统调用,进入睡眠状态
     *      wait()的返回值:
     *          1.返回已终止子进程PID 
     *          2.返回-1(出错)
     */
    wpid = wait(&status);                                  //wpid表示第一个子进程退出的PID
    if (wpid == -1)
    {
        perror ("wait");
    }
    printf ("wpid=%d\n", wpid);                            //输出PID

    /**
     *  Step 3:根据子进程不同退出状态 输出语句
     */
    if (WIFEXITED (status))
    {
        printf("PID = %d\n",getpid());
        printf ("Normal termination with exit status=%d\n", WEXITSTATUS (status));
    }
    else if (WIFSIGNALED (status))
    {
        printf("PID = %d\n",getpid());
        printf ("Killed by signal=%d%s\n",WTERMSIG(status),WCOREDUMP(status)?"(dumpedcore)":"");
    }
    else if (WIFSTOPPED (status))
    {
        printf("PID = %d\n",getpid());
        printf ("Stopped by signal=%d\n", WSTOPSIG(status));
    }
    else if (WIFCONTINUED (status))
    {
        printf("PID = %d\n",getpid());
        printf ("Continued\n");
    }
   
    return 0;
}
(2)编译运行及结果

  编译:gcc wait.c -o wait
  运行:./wait
  j结果:

I am parent process 8501
I am sibling process 8503
wpid=8503
PID = 8501
Normal termination with exit status=0
I am child process 8502
I am sibling process 8507
wpid=8507
PID = 8502
Normal termination with exit status=0
(3)总结

  <1>进程结构

xx─┬─ 8501
   └─ 8502 ─┬─ 8503
            └─ 8507

  <2>解释
    父进程创建两个进程 8502 与 8503,其中 8503 创建后直接退出,8502 延时10s。
    父进程调用wait()函数进入睡眠状态,接收到 8503 的结束信号,输出语句:Normal termination with exit status=0
    子进程 8503 也创建进程 8507 ,并调用wait()函数进入睡眠状态,等待进程 8507结束后退出,并输出:Normal termination with exit status=0
  <3>结论
    验证了调用wait()进入父进程进入睡眠状态,若多子进程中某子进程先行结束,则返回该子进程PID,父进程进入运行状态。

二 、 waitpid ()函数

1、函数介绍

(1)函数功能:

  等待特定进程(可通过额外参数进行微调)。
  情景
    若一个进程有多个子进程,但无需等待所有子进程结束,父进程只想等待其中一个特定的子进程。
  解决方法
    ①多次调用wait(),每次根据返回值判断是不是特定进程。
    ②调用waitpid()系统调用。

项目 说明
函数原型 pid_t waitpid(pid_t pid, int *wstatus, int options);
头文件 sys/types.h、sys/wait.h
参数说明 1.pid:需等待的pid(一个或多个),可能情况:
①< -1 ,等待其组ID是参数pid的绝对值的任一子进程。
②== -1 ,等待任一子进程,与`wait()`等效。
③== 0 ,等待与调用进程处于同一进程组的任一进程 。
④ > 0 ,等待进程ID等于实参进程ID。
2.wstatus:整数指针或null指针
存放进程结束状态的存储空间
3.options:0或多个选项按二进制“或”运算结果
返回值 ①返回终止进程ID,并将该子进程终止状态存放__stat_loc指向的存储单元。
②若指定进程或进程组不存在或参数pid指定进程不是调用进程的子进程都将出错。
注意
(2)参数__options常量:

  参数可为0,或以下表格中常量按或运算的结果。

常量 说明
WCONTINUED 若实现支持作业控制。那么由pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态(POSIX.1的XSI扩展)
WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0
WUNTRACED 若某实现支持作业控制,而由pid指定的任一子进程已处于暂停状态,并且其状态自暂停以来还未报告过、则返回其状态。WIFSTOPPED宏确定返回值是否对应于一个暂停子进程
(3)与wait()函数对比

  1️⃣waitpid()可等待一个特定的进程,而wait则返回任一终止子进程的状态。在讨论popen()函数时会再说明这一功能。
  2️⃣waitpid()提供了一个wait()的非阻塞版本。有时用户希望取得一个子进程的状态,但不想阻塞。
  3️⃣waitpid()支持作业控制(利用WUNTRACEDWCONTINUED选项)。

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

int main (int argc,char* argv[])
{
    int status;
    pid_t pid;
    pid = waitpid (1742, &status, WNOHANG);
    if (pid == -1){
        perror ("waitpid");
    }
    else {
        printf ("pid=%d\n", pid);
        if (WIFEXITED (status))
        {
            printf ("Normal termination with exit status=%d\n", WEXITSTATUS (status));
        }
        if (WIFSIGNALED (status))
        {
        printf ("Killed by signal=%d%s\n", WTERMSIG(status), WCOREDUMP(status)?"(dumped core)":"");
        }
    }
}

  wait (&status);waitpid (-1, &status, 0);两者等效。

三 、waitid()函数

1、函数介绍:

(1)函数功能:

  与wait()、waitpid()一样,等待子进程结束,了解子进程状态改变的信息(终止、停止或继续运行),但具有更多选项。
  使用单独的参数表示要等待的子进程类型,而不是将此与进程ID或进程组合成一个参数、

项目 说明
函数原型 int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
头文件 sys/types.h、sys/wait.h
参数说明 1.idtype:等待PID类别:下表三种中的一种
2.id:通用ID号
3.infop:必须指向一个有效的siginfo_t类。
成功调用会保证以下成员被填充
4.options:0或多个选项按二进制“或”运算结果
返回值 成功返回0,错误返回-1,并设置errno值
注意 ①使用__idtype与__id指定要等待的子进程
(2)参数idtype的值(可能扩充)
常量 说明
P_PID 等待pid值是id的子进程。
P_GID 等待进程组ID是id那些子进程
P_ALL 等待所有子进程,参数id被忽略。
(3)参数options常量:

  以下表格中一个常量或多个常量按或运算的结果。

常量 说明
WEXITED 调用进程会等待结束的子进程(由id 和 idtyp指定)。
WSTOPPED 调用进程会等待收到了信号而停止了执行的子进程。
WCONTINUED 调用进程会等待收到了信号而继续执行的子进程。
WNOHANG 调用进程不会阻塞,如果没有子进程结束(停止或者继续执行),它就立刻返回。
WNOWAIT 调用进程不会移除满足条件的子进程的僵死状态。调用进程可能会在将来继续等待。
(4)参数infop:
成员 说明
si_pid 子进程的pid
si_uid 子进程的uid
si_code 根据子进程的状态是被信号所终止、杀死、停止或者继续执行而分别设置为CLDEXITED、CLD_KILLED、CLD_STOPPED或者CLD_CONTINUED中的一个。
si_signo 设置为SIGCHLD。
si_status 如果si.code是CLDEXITED,它是子进程的退出值。否则,它引起状态改变的那个信号的编码。
(5)errno值
常量 说明
ECHLD 由id和idtype确定的进程不存在。
EINTR 一个信号打断了子进程的执行,但是在options里没有设置WNOHANG。
EINVAL options参数不合法,或者id和 idtyp 的组合不合法。

四、sleep()

一、函数介绍

函数功能:

  使进程休眠SECONDS秒,或直到信号到达且不被忽略。

项目 说明
函数原型 extern unsigned int sleep (unsigned int __seconds);
头文件 unistd.h
参数说明 1.__seconds:秒数
返回值 返回小于实际休眠seconds的秒数(因此,如果它完全休眠,则返回0)。
注意 若一个信号处理程序在' sleep'调用中执行' longjmp'或修改对SIGALRM信号的处理,则之后对SIGALRM信号的处理是未定义的。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值