Linux编程基础 3.3:进程同步

Linux 系统中提供了 wait() 函数和 waitpid() 函数来获取进程状态,实现进程同步。

wait() 函数

wait() 函数存在于系统库函数 sys/wait.h 中,形式如下:

#include <sys/wait.h>
pid_t wait(int *status);

调用 wait() 的进程会被挂起,进入阻塞状态,直到子进程变为僵尸态,如果捕获到子进程的退出信息就会转为运行态,然后回收子进程资源并返回;若没有变为僵尸态的子进程,wait 函数就会让进程一直阻塞。若当前进程有多个子进程,只要捕获到一个变为僵尸态的子进程,wait 函数就会恢复执行态。

参数说明:

  • status:是一个 int * 类型的指针,用来保存子进程退出时的状态信息。通常情况下该参数设为NULL,表示不关心进程时如何终止的。

返回值:

  • 调用成功,wait() 会返回子进程的进程 id;
  • 调用失败,wait() 返回-1,errno 被设置为 ECHILD。

案例 6-4(书P139):

若子进程 p1 是其父进程 p 的先决进程,基于 wait() 函数使得进程同步。

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

int main(){
    pid_t pid,w;
    pid = fork();

    if( pid == -1){
        perror("fork error");
        exit(1);
    }
    else if( pid == 0){
        sleep(3);
        printf("Child proccess: pid =%d\\n",getpid());
    }
    else if( pid > 0){
        w = wait(NULL);
        printf("Catched a child process,pid = %d\\n",w);
    }

    return 0;
}

若 wait 函数的参数不为空,可以获取子进程的退出状态,退出状态存放在参数 status 的低八位中。Linux定义了一组判断进程退出状态的宏函数,其中最基础的两个是:

#include <sys/wait.h>
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值(即正常退出),则使用该宏提取子进程的返回值。

案例 6-5(书P140):

使用 wait() 函数同步进程,并使用宏获取子进程的返回值。

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

int main(){
    pid_t pid,w;
    pid = fork();

    if(pid == -1){
        perror("fork error");
        exit(1);
    }
    else if( pid == 0){
        sleep(3);
        printf("Child proccess: pid =%d\n",getpid());
        exit(5);
    }
    else if( pid > 0){
        int status;
        w = wait(&status);
        if(WIFEXITED(status)){
            printf("Child process pid = %d exit normally.\n",w);
            printf("Return Code:%d\n",WEXITSTATUS(status));
        }
        else{
            printf("Child process pid = %d exit abnormally.\n",w);
        }
    }
     return 0;
}

 

waitpid() 函数

wait 函数的缺点:当前进程有很多个子进程,wait 函数无法确保作为先决条件的子进程在父进程之前执行。waitpid 函数可以应对 wait 函数面临的缺点,可以等待指定的子进程,也可以在父进程不阻塞的情况下获取子进程的状态。

waitpid() 存在于系统库函数 sys/wait.h ,形式如下:

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

参数:

waitpid() 函数比 wait() 函数多两个参数:pidoptions

  • pid:一般是进程的pid,也有可能是其他取值。进一步说明如下:
    • pid > 0:只等待 pid 与该参数相同的子程序,若等待子进程(编号为pid)退出,函数返回;若未结束,则函数一直等待该进程;
    • pid = 0:等待同一进程组的所有子进程退出,若某子进程加入了其他进程组,则 waitpid() 不再关心它的状态;
    • pid = -1:waitpid() 函数退化为 wait() 函数,阻塞等待并回收一个子进程;
    • pid < -1:等待指定进程组中的任何子进程,进程组的 id 等于 pid 的绝对值。
  • options: 提供控制 waitpid() 选项,可以是一个常量,也可以是 | 连接的两个常量,选项如下:
    • WNOHANG:如果子进程没有终止,waitpid 不会阻塞父进程,会立即返回;
    • WUNTRACED:如果子进程暂停执行,waitpid 立即返回;
    • 0:不使用选项。

返回值:

  • 正常返回时,返回捕捉到的子进程的 pid;
  • options = WNOHANG, waitpid 发现没有已退出的子进程可回收,则返回0;
  • 若调用过程出错,则返回-1。errno 会被设置成为相应的值以指示错误位置。

案例 6-6(书P141):

使父进程等待进程组中某个指定的进程,若该进程不退出,则父进程一直阻塞。

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


int main(){
    pid_t pid,p,w;
    pid = fork();//创建第一个子进程

    if(pid == -1){//第一个子进程创建后父子进程的执行内容
        perror("fork error");
        exit(1);
    }
    else if(pid == 0){//子进程沉睡
        sleep(5);
        printf("First child process: pid =%d\n",getpid());
    }
    else if(pid > 0){//父进程继续创建进程
        int i;
        p = pid;
        for(i = 0; i<3 ; i++){//由父进程创建3个子进程
            if( (pid=fork()) == 0){
                break;
            }
        }
        if( pid == -1){//出错
            perror("fork error");
            exit(2);
        }
        else if(pid == 0){//子进程
            printf("Child process: pid =%d\n",getpid());
            exit(0);
        }
        else if(pid > 0){//父进程
            w =waitpid(p,NULL,0);//等待第一个子程序执行
            if(w == p){
                printf("Catch a child Process : pid = %d\n",w);
            }
            else{
                printf("waitpid error\n");
            }
        }
    }
    return 0;
}

 

特殊进程的危害

僵尸进程不能再次被运行,而且会占用一定的内存空间,并占据进程编号(id),当僵尸进程较多时,将会消耗系统内存,新进程可能因内存不足或无法获取pid而无法创建。

父进程通过 wait()waitpid() 函数可以有效防止僵尸进程的产生,对于已存在的僵尸进程,则可通过杀死其父进程的方法解决。

当僵尸进程的父进程被终止后,僵尸进程将作为孤儿进程被 init 进程接收,init 进程会不断调用wait() 函数获取子进程状态,对已处于僵尸态的进程进行处理。

孤儿进程永远不会成为僵尸进程。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值