【Linux进程控制】进程等待

news2024/11/15 9:33:50

目录

进程等待

进程等待是什么?

为什么?

怎么办?

wait方法

获取子进程status

多进程的等待问题

waitpid方法

什么是阻塞等待?什么是非阻塞等待?

wait/waitpid获取子进程信息原理


进程等待

进程等待是什么?

任何子进程,在退出的情况下,一般必须要被父进程进行等待。在退出的时候,如果父进程不管不顾,退出进程,则子进程就变成僵尸状态(Z状态),kill -9 都无法杀死,因为谁也没有办法杀死一个已经死去的进程,最终会造成内存泄漏

子进程的僵尸只能由父进程解决,父进程通过进程等待的方式,回收子进程资源,获取子进程的退出信息(因为父进程需要知道自己派给子进程运行结果是否正确。子进程是否正常退出等)

为什么?

1.父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(一定要考虑的)

2.获取子进程的退出信息,要知道子进程是因为什么原因退出的(可选的功能)

正是因为这两点,我们需要进程等待

怎么办?

wait方法

当子进程等待成功的时候,会返回等待成功的子进程pid,失败则返回-1。而传入的参数status为输出型参数,如果需要回去子进程的退出状态,则需要传入status;若不关心,则可以填写NULL。

【示例1】

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

int main()
{
	pid_t id = fork();
	if(id == 0)
	{
		int cnt = 5;
		while(cnt)
		{
			printf("[%d]->I am child process! pid = %d, ppid = %d\n", cnt, getpid(), getppid());
			cnt--;
			sleep(1);
		}
		exit(0);
	}
	int status = 0;
	pid_t ret = wait(&status);
	if(ret > 0)
	{
		printf("wait %d success! status = %d\n", ret, status);
	}
	return 0;
}
[wuxu@Nanyi lesson17]$ gcc -o test wait1.c -std=c99
[wuxu@Nanyi lesson17]$ ./test
[5]->I am child process! pid = 4593, ppid = 4592
[4]->I am child process! pid = 4593, ppid = 4592
[3]->I am child process! pid = 4593, ppid = 4592
[2]->I am child process! pid = 4593, ppid = 4592
[1]->I am child process! pid = 4593, ppid = 4592
wait 4593 success! status = 0

从上面代码可以发现,当子进程在执行时,父进程并不会执行wait之后的if判断语句,而是阻塞在pid_t ret = wait(&status);处等待子进程退出。故wait是阻塞等待的。

获取子进程status

❍ wait 和 waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充

❍ 如果传递NULL,表示不关心子进程的退出状态信息

❍ 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程

❍ status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16位的比特位)

①正常终止时,第8到第15位保存进程的退出状态 ②异常终止时,第8到第15位保存进程的退出状态(但异常终止的退出状态不一定能正确标识进程的退出状态,这里的退出状态是无效的,直接忽略),第7位为coredump标志位,这里先忽略这一位,将于后面的文章中介绍,第0到7位存储的是当前进程收到的信号。

status参数中可能包含的信息:

  • 退出码:如果子进程是正常退出,status将包含子进程的退出码。

  • 信号编号:如果子进程是因信号而终止,status将包含导致子进程终止的信号编号。

  • 暂停状态:如果子进程被暂停(例如,由信号SIGSTOPSIGTSTPSIGTTIN暂停),status将包含导致子进程暂停的信号编号。

  • 退出状态:如果子进程是正常退出,status的低8位(WIFEXITED(status))将设置为1,并且WEXITSTATUS(status)将包含退出码。

  • 信号状态:如果子进程是因信号而终止,status的低8位(WIFSIGNALED(status))将设置为1,并且WTERMSIG(status)将包含信号编号。

  • 暂停状态:如果子进程被暂停,status的低8位(WIFSTOPPED(status))将设置为1,并且WSTOPSIG(status)将包含信号编号。

 

 如果我们想获取子进程的退出码和退出信号,则上面代码应修改成下面这样

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

int main()
{
	pid_t id = fork();
	if(id == 0)
	{
		int cnt = 5;
		while(cnt)
		{
			printf("[%d]->I am child process! pid = %d, ppid = %d\n", cnt, getpid(), getppid());
			cnt--;
			sleep(1);
		}
		exit(0);
	}
	int status = 0;
	pid_t ret = wait(&status);
	if(ret > 0)
	{
		printf("wait %d success! exitcode = %d sig = %d\n", ret, ((status >> 8) & 0xFF), (status & 0x7F));
	}
	return 0;
}
[wuxu@Nanyi lesson17]$ gcc -o test wait1.c -std=c99
[wuxu@Nanyi lesson17]$ ./test
[5]->I am child process! pid = 4928, ppid = 4927
[4]->I am child process! pid = 4928, ppid = 4927
[3]->I am child process! pid = 4928, ppid = 4927
[2]->I am child process! pid = 4928, ppid = 4927
[1]->I am child process! pid = 4928, ppid = 4927
wait 4928 success! exitcode = 0 sign = 0
多进程的等待问题
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	int i = 0;
	for(; i < 5; i++)
	{
		pid_t id = fork();
		if(id == 0)
		{
			int cnt = 5;
			while(cnt)
			{
				printf("[%d]->I am child process! pid = %d, ppid = %d\n", cnt, getpid(), getppid());
				cnt--;
				sleep(1);
			}
			exit(0);
		}
	}
	i = 0;
	for(; i < 5; i++)
	{
		int status = 0;
		pid_t ret = wait(&status);
		if(ret > 0)
		{
			printf("wait %d success! exitcode = %d sign = %d\n", ret, ((status >> 8) & 0xFF), (status & 0x7F));
		}
	}
	return 0;
}

waitpid方法

waitpid与wait方法相同,如果子进程等待成功,则返回该子进程的pid,如果返回失败则返回1;并且一样可以传入status获取子进程的退出信息

waitpid()函数比wait()函数多两个参数:pidoptions。这两个参数允许用户更加精确地控制进程的等待行为

pid参数

  • 含义pid参数是一个整数,它指定等待哪个子进程的结束。如果pid-1,则waitpid()会等待任意一个子进程的结束。如果pid为0,则waitpid()会等待调用fork()创建的所有子进程的结束。如果pid大于0,则waitpid()会等待指定PID的子进程的结束。

  • 返回值:如果pid-1waitpid()返回最后一个子进程的PID;如果pid为其他值,则返回指定PID的子进程的PID。

options参数

  • 含义:options参数是一个整数,它定义了waitpid()的行为。这个参数可以是WNOHANGWUNTRACED或它们的组合。

  • WNOHANG:如果指定的子进程尚未结束,waitpid()不会阻塞调用进程,而是立即返回。

  • WUNTRACED:如果指定的子进程暂停执行(如被信号暂停),waitpid()也会返回,并且不会返回WNOHANG

  • WNOHANGWUNTRACED不能同时使用。

status参数

  1. 如果子进程是正常终止的

    • WIFEXITED(status) 宏可以用来检查子进程是否正常退出。如果是,WEXITSTATUS(status) 宏可以提取子进程的退出状态(即 exit()_exit() 的参数)。

  2. 如果子进程是因信号而终止的

    • WIFSIGNALED(status) 宏可以用来检查子进程是否因为捕获到的信号而终止。如果是,WTERMSIG(status) 宏可以提取导致子进程终止的信号的编号。

    • WCOREDUMP(status) 宏可以用来检查子进程是否产生了核心转储文件。

  3. 如果子进程是暂停状态(由于接收到 SIGSTOP, SIGTSTP, SIGTTIN, 或 SIGTTOU 信号):

    • WIFSTOPPED(status) 宏可以用来检查子进程是否已经停止。如果是,WSTOPSIG(status) 宏可以提取导致子进程暂停的信号的编号。

  4. 如果子进程是从暂停状态继续运行的

    • WIFCONTINUED(status) 宏可以用来检查子进程是否继续执行。

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

int main() {
    pid_t cpid, w;
    int status;

    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {    /* 子进程 */
        printf("子进程: 我将退出并返回状态 %d\n", 42);
        _exit(42); // 子进程退出,返回42
    } else {            /* 父进程 */
        do {
            w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
            if (w == -1) {
                perror("waitpid");
                exit(EXIT_FAILURE);
            }

            if (WIFEXITED(status)) {
                printf("子进程正常终止,返回状态=%d\n", WEXITSTATUS(status));
            } else if (WIFSIGNALED(status)) {
                printf("子进程因信号 %d 终止\n", WTERMSIG(status));
            } else if (WIFSTOPPED(status)) {
                printf("子进程被信号 %d 暂停\n", WSTOPSIG(status));
            } else if (WIFCONTINUED(status)) {
                printf("子进程继续执行\n");
            }
        } while (!WIFEXITED(status) && !WIFSIGNALED(status));
        exit(EXIT_SUCCESS);
    }
}

【示例】

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

int main()
{
	pid_t id = fork();
	if(id == 0)
	{
		int cnt = 5;
		while(cnt)
		{
			printf("[%d]->child process, pid = %d\n", cnt, getpid());
			cnt--;
			sleep(1);
		}
	}
	int status = 0;
	pid_t ret = waitpid(id, &status, 0);
	if(ret == id)
	{
		printf("wait %d success! exitcode = %d, sig =%d\n", ret ,((status >> 8) & 0xFF), (status & 0x7F));
	}
	return 0;
}
[wuxu@Nanyi lesson17]$ gcc -o test wait3.c -std=c99
[wuxu@Nanyi lesson17]$ ./test
[5]->child process, pid = 5313
[4]->child process, pid = 5313
[3]->child process, pid = 5313
[2]->child process, pid = 5313
[1]->child process, pid = 5313
wait 5313 success! exitcode = 0, sign =0
什么是阻塞等待?什么是非阻塞等待?

【例子】阻塞等待

张三在大学是个学渣,上课时间天天出去玩,以致于明天就要期末考试了,于是找到学霸李四,要求李四帮忙辅导一下。于是张三打电话告诉李四“李四,你帮我辅导一下明天操作系统期末考试呗,顺便请你吃个饭,就当报答你了”,李四一听,说“可以,但是你要先等我一小时,我先把计算机组成原理看完”,于是张三就来到李四家楼下,等待着李四。张三什么事也不做,就在干巴巴的等着,每过一分钟就给李四打电话,问李四好了没,直到李四说好了,下楼了,他才停止

这种循环等待,其他事也不做,就在干巴巴等着等反馈,这就是阻塞式等待,上面的wait和waitpid代码均是阻塞等待,父进程在wait/waitpid函数上阻塞等待着子进程,而不做其他事,等子进程执行结束才执行下面的if判断语句。

阻塞式等待可以及时回收子进程,但父进程花费大量时间阻塞等待子进程,只要CPU时间片轮到父进程,父进程就一直循环等待子进程,这样效率明显不高

【例子】非阻塞等待

男女朋友约好一起去逛街,女朋友对男友说,你先等我1小时,我画个妆。于是男友开始等待女友,在等待的过程中,男友打打游戏,每打完一把游戏问女朋友好了没。像这种在等待时,顺便做一下其它任务,这种称为非阻塞等待

像这种再等待时,顺带做一下其他任务,这种称为非阻塞等待。非阻塞等待执行的其他任务不应花费过多时间,以免影响父进程对子进程的等待与回收(因为父进程等待子进程才是主要任务,执行其他代码只是顺带执行,不是重点)。

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

void PrintLog()
{
    printf("begin PrintLog...\n");
}
void Download()
{
    printf("begin Download...\n");
}
void MysqlDataSync()
{
    printf("begin MySQLDataSync...\n");
}

typedef void(*func_t)();

#define N 3
func_t tasks[N] = {NULL};

void LoadTask()
{
    tasks[0] = PrintLog;
    tasks[1] = Download;
    tasks[2] = MysqlDataSync;
}
void HandlerTask()
{
    for(int i = 0; i < N; i++)
    {
        tasks[i](); // 回调方式
    }
}

// fahter
void DoOtherThing()
{
    HandlerTask();
}


void ChildRun()
{
    //int *p = NULL;
    int cnt = 5;
    while(cnt)
    {
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
        sleep(1);
        cnt--;
        //*p = 100;
    }
}

int main()
{
    printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());

    pid_t id = fork();
    if(id == 0)
    {
        // child
        ChildRun();
        printf("child quit ...\n");
        exit(123);
    }
    LoadTask();
    // father
    while(1)
    {
        int status = 0;
        pid_t rid = waitpid(id, &status, WNOHANG); // non block
        if(rid == 0)
        {
            sleep(1);
            printf("child is running, father check next time!\n");
            DoOtherThing();
        }
        else if(rid > 0)
        {
            if(WIFEXITED(status))
            {
                printf("child quit success, child exit code : %d\n", WEXITSTATUS(status));
            }
            else
            {
                printf("child quit unnormal!\n");
            }
            break;
        }
        else
        {
            printf("waitpid failed!\n");
            break;
        }
    }
      return 0;
    }

wait/waitpid获取子进程信息原理

当子进程处于僵尸状态时,系统至少要保留子进程的PCB,PCB中保存着子进程的退出码、收到的信号等退出信息。

当父进程在CPU上执行waitpid时,则进入内核态。处于内核态的执行语句有操作系统的权限,因此它可以读取子进程的退出信息,并将该退出信息填写入status。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2140154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI学习】陶哲轩在 2024 年第 65 届国际数学奥林匹克(IMO)的演讲:AI 与数学

陶哲轩在 2024 年第 65 届国际数学奥林匹克关于AI 和数学的演讲&#xff0c;很有意思。陶哲轩的讲话语速太快了&#xff0c;足见其聪明&#xff01; AI用于数学的一些方面&#xff1a; 陶哲轩介绍到刚刚被数学家接受并开始普及的方法&#xff1a;形式化证明辅助工具。 形式化…

API:连接数字世界的隐形纽带

在这个智能手机和应用程序无处不在的时代&#xff0c;你可能听说过API这个术语&#xff0c;但你知道它究竟是什么吗&#xff1f;API&#xff0c;全称为应用程序编程接口&#xff08;Application Programming Interface&#xff09;&#xff0c;是一种让不同的软件和服务之间能够…

MySQL基础篇(黑马程序员2022-01-18)

1 MySQL数据库概述 1.1 MySQL数据库的下载,安装,启动停止 1.2 数据模型 (1)关系型数据库(RDBMS) 概念&#xff1a;建立在关系模型基础上&#xff0c;由多张相互连接的二维表组成的数据库。 特点&#xff1a; A. 使用表存储数据&#xff0c;格式统一&#xff0c;便于维护。…

C++11第五弹:线程库 | 互斥锁 | 原子操作

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

蓝牙耳机是入耳式的好还是开放式的好?2024开放式耳机推荐

个人推荐入开放式耳机&#xff0c;戴起来更舒服&#xff0c;主要有以下几方面原因&#xff1a; 减少对耳部的压迫&#xff1a; 不入耳设计&#xff1a;开放式耳机通常不需要插入耳道&#xff0c;避免了对耳道的直接压迫。入耳式耳机的耳塞长时间塞在耳道内&#xff0c;会对耳…

Linux基础---07文件传输

Linux文件传输地图如下&#xff0c;先选取你所需的场景&#xff0c;若你是需要Linux和Linux之间传输文件就查看SCP工具即可。 一.下载网站文件 前提是有网&#xff1a; 检查网络是否畅通命令&#xff1a;ping www.baidu.com&#xff0c;若有持续的返回值就说明网络畅通。Ctr…

前端基础知识(HTML+CSS+JavaScript)

文章目录 一、HTML1.1 HTML 基础&#xff1a;1.1.1 HTML 的概念&#xff1a;1.1.2 认识 HTML 标签&#xff1a;1.1.3 HTML 文件基本结构&#xff1a;1.1.4 标签层次结构&#xff1a; 1.2 HTML 快速入门&#xff1a;1.3 HTML常见标签&#xff1a;1.3.1 标题标签&#xff1a;h1-h…

数据结构-2.顺序表

1.线性表 线性是n个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见的线性表有: 顺序表 , 链表 , 栈 , 队列... 线性表在逻辑上是线性结构, 也就是连续的一条直线 . 但是在物理结构上并不是连续的, 线性表在物理上存储时, 通常以数组和链式结…

5-----RYZ维修工具 操作界面预览与功能操作解析 刷机 解锁 修复参数等等

以上是工具选项功能的界面预览 。通过预览可以看到很多功能选项。此类工具涵盖了很多操作区域。需要根据自己机型的实际需求来操作。根据开发者的描述。此工具有一下功能。包含mtk刷机 分区修复。9008刷机 备份基带efs等等。 高通操作区域 高通修复串码 高通修改写入基带qc…

石化盈科PMO总经理任志婷受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 石化盈科信息技术有限责任公司运营管理部总经理兼PMO总经理任志婷女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“激活关键的少数派——项目经理培养体系的设计实践”。大会…

无人机视角下落水救援检测数据集

无人机视角下落水救援检测数据集&#xff0c;利用无人机快速搜索落水者对增加受害者的生存机会至关重要&#xff0c;该数据集共收集12万帧视频图像&#xff0c;涵盖无人机高度从10m-60m高度&#xff0c;检测包括落水者&#xff08;11万标注量&#xff09;、流木&#xff08;900…

TCP/IP - ICMP

目录 1. 帧格式2. ICMPv4消息类型(Type = 0,Code = 0)回送应答 /(Type = 8,Code = 0)回送请求(Type = 3)目标不可达(Type = 5)重定向(Type = 11)ICMP超时(Type = 12)参数3. ICMPv6消息类型回见TCP/IP 对ICMP协议作介绍 ICMP(Internet Control Message Protocol…

什么是快充协议,最常见的快充协议有哪些

什么是快充协议 随着手机快充的出现大家都知道快充技术但很多人确不知道快充协议&#xff0c;在快充技术里快充协议是必不可少的&#xff0c;那么今天我们就来探讨一下什么是快充协议&#xff1f; 快充协议是一种通过提高充电效率来缩短设备充电时间的电池充电技术。它通过在充…

商淘云九周年 分账系统助力企业合规发展

从2015到2024年&#xff0c;商淘云电商服务品牌已走过整整九个春秋。这九年&#xff0c;是商淘云不断发展壮大&#xff0c;深化品牌建设服务&#xff0c;并取得显著成效的九年&#xff0c;也是见证中国电商迅速崛起的九年。我们回顾九年的风雨历程&#xff0c;感受到企业的成长…

Python计算机视觉 第9章-图像分割

Python计算机视觉 第9章-图像分割 图像分割是将一幅图像分割成有意义区域的过程。区域可以是图像的前景与背景或图像中一些单独的对象。这些区域可以利用一些诸如颜色、边界或近邻相似性等特征进行构建。 9.1 图割&#xff08;Graph Cut&#xff09; 图割&#xff08;Graph…

SOMEIP_ETS_111: SD_Empty_Entries_Array

测试目的&#xff1a; 验证DUT能够忽略声明了条目数组长度为零的SubscribeEventgroup消息。 描述 本测试用例旨在确保DUT在接收到一个Entries数组长度为零的SubscribeEventgroup消息时&#xff0c;能够正确地忽略该消息&#xff0c;不对其进行解释或响应。 测试拓扑&#x…

【机器学习-监督学习】朴素贝叶斯

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

数据中台建设(七)——数据体系建设

数据体系建设 数据中台是企业数据汇集地&#xff0c;但并不是简单的数据堆积&#xff0c;而是进行分层建模&#xff0c;数据体系建设最终呈现一套完整、规范、准确的数据。数据体系建设就是大数据中数据仓库建设。如下图&#xff1a; 贴源数据层ODS(Operational Data Store)…

python的数据类型详解

python基础 认识python基本类型python的注释风格有三种&#xff08;也可以说是两种&#xff09;python的对齐方式python的多行语句折断字符串类型的“计算”列表的常见用法元组的常见用法集合set的常见用法字典的常见用法bytes类型python的输入输出python中的引用 认识python基…