2.2.1 进程管理,以及父子进程共享同一个文件资源时,文件的‘读写位置’会相互影响

本文主要是介绍2.2.1 进程管理,以及父子进程共享同一个文件资源时,文件的‘读写位置’会相互影响,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录
  1. 等待一个进程(父进程等待子进程终止)
  2. 僵尸进程(defunct / zombie)
  3. 父子进程共享同一个文件资源时,‘读写指针’ 设置

1. 等待一个进程
  • 当用 fork 函数调用启动一个子进程时,子进程就有了它自己的生命周期并将独立运行。可以通过在父进程中调用 wait 函数让父进程等待子进程结束。
  1. wait 函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *stat_loc);

头文件 ‘sys/wait.h’ 中定义了用来解释状态信息的宏(每两行为一组,共 3 组):

说明
WIFEXITED(stat_val)如果子进程正常结束,它就取一个非零值
WEXITSTATUS(stat_val)如果 WIFEXITED 非零,它返回子进程的退出码
--
WIFSIGNALED(stat_val)如果子进程因为一个未捕获的信号而终止,它就取一个非零值
WTERNSIG(stat_val)如果 WIFSIGNALED 非零,它返回一个信号代码
--
WIFSTOPPED(stat_val)如果子进程意外终止,它就取一个非零值
WSTOPSIG(stat_val)如果 WIFSTOPPED 非零,它返回一个信号代码
  1. 父进程等待子进程终止,并获取/打印子进程的退出状态码,代码如下:
/* test3.c */
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>void main(){printf("main running.\n");int res;pid_t pid_res;pid_t pid = fork();switch(pid){case 0:// 子进程printf("child process running.\n");// 模拟耗时操作sleep(5);printf("child process done.\n");// 指定退出码为 69(父进程将获取该退出码)exit(69);case -1:// errorexit(EXIT_FAILURE);default:// 父进程printf("parent process wait for child process.\n");// 父进程执行挂起(阻塞),等待子进程终止pid_res = wait(&res);if(pid_res == pid){if(WIFEXITED(res)){printf("cp exit code(WIFEXITED): %d\n", WEXITSTATUS(res));}else if(WIFSIGNALED(res)){printf("cp exit code(WIFSIGNALED): %d\n", WTERMSIG(res));}else if(WIFSTOPPED(res)){printf("cp exit code(WIFSTOPPED): %d\n", WSTOPSIG(res));}}else{printf("error: wait for child process.\n");}printf("parent process done.\n");exit(EXIT_SUCCESS);}
}

运行 test3,执行结果如下(父进程等待子进程终止,并成功获取子进程退出码):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test3 test3.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test3
main running.
parent process wait for child process.
child process running.
child process done.
cp exit code(WIFEXITED): 69 // 表明:子进程正常结束,并且正确获取退出码为 69
parent process done.

将 switch 语句中的子进程执行代码由

        case 0:// 子进程printf("child process running.\n");// 模拟耗时操作sleep(5);printf("child process done.\n");// 指定退出码为 69(父进程将获取该退出码)exit(69);

修改为(其中 故意导致子进程异常终止):

        case 0:// 子进程printf("child process running.\n");// 模拟耗时操作sleep(5);printf("child process done.\n");//// 故意导致子进程异常终止//res = 1 / 0;// 指定退出码为 69(父进程将获取该退出码)exit(69);

再次运行 test3,执行结果如下(可见编译器报告‘除零’警告,忽略警告,继续运行 test3,发现子进程因未捕获的信号而终止(WIFSIGNALED),并且信号代码为 8):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test3 test3.c
test3.c: In function ‘main’:
test3.c:18:21: warning: division by zero [-Wdiv-by-zero]res = 1 / 0;^
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test3
main running.
parent process wait for child process.
child process running.
child process done.
cp exit code(WIFSIGNALED): 8 // 子进程因未捕获的信号而终止(WIFSIGNALED),8(SIGFPE) 表示算术运算错误
parent process done.

2. 僵尸进程
  • 子进程终止时,它与父进程之间的关系还会保持,直到父进程也正常终止或父进程调用 wait 函数才告结束。因此,进程表中代表子进程的表项不会立即释放。虽然子进程不再运行,但它仍然存在于系统中,因为它的退出码还需要保存起来,以备父进程今后的 wait 调用使用。这时它将成为一个死(defunct)进程或僵尸(zombie)进程。
  • 如果父进程异常终止,子进程将自动把 PID 为 1 的进程(即 init )作为自己的父进程。子进程现在是一个不再运行的僵尸进程,但因为其父进程异常终止,所以它由 init 进程接管。僵尸进程将一直保留在进程表中直到被 init 进程发现并释放。进程表越大,这一过程就越慢。应该尽量避免产生僵尸进程,因为在 init 清理它们之前,它们将一直消耗系统的资源。
  1. 模拟僵尸进程,代码如下(子进程先于父进程终止):
/* test3.c */
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>void main(){printf("parent running.\n");int res;pid_t pid = fork();switch(pid){case 0:// child processprintf("child process running.\n");printf("child process done.\n");exit(0);case -1:// errorexit(EXIT_FAILURE);default:// 父进程等待子进程终止sleep(5);printf("parent process done.\n");exit(EXIT_SUCCESS);}
}

运行 test3 程序,执行结果如下(正如期待的那样,子进程先行终止,父进程随后才结束运行):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test3 test3.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test3
parent running.
child process running.
child process done. 	// 子进程终止
parent process done.	// 父进程终止

在子进程终止后,父进程终止之前,查询进程信息,如下(子进程 pid:11353 为僵尸进程):

ubuntu@cuname:~$ ps l
F   UID    PID   PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1000  11352   3665  20   0   4352   680 hrtime S+   pts/18     0:00 ./test3
1  1000  11353  11352  20   0      0     0 -      Z+   pts/18     0:00 [test3] <defunct>
  1. 怎样避免僵尸进程?除了在父进程中调用 wait 函数,还有可以使用信号处理,在父进程中处理 或 忽略子进程的 SIGCHLD 信号。修改代码如下(忽略子进程的 SIGCHLD 信号):
/* test3.c */
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void main(){printf("parent running.\n");// 忽略子进程的 SIGCHLD 信号signal(SIGCHLD, SIG_IGN);pid_t pid = fork();switch(pid){case 0:// child processprintf("child process running.\n");printf("child process done.\n");exit(0);case -1:// errorexit(EXIT_FAILURE);default:// 父进程等待子进程终止sleep(5);printf("parent process done.\n");exit(EXIT_SUCCESS);}
}

运行 test3:

ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test3
parent running.
child process running.
child process done.
parent process done.

并查看进程信息如下(已经没有僵尸进程了):

ubuntu@cuname:~$ ps l
F   UID    PID   PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0  1000  11455   3665  20   0   4352   640 hrtime S+   pts/18     0:00 ./test3

3. 父子进程共享同一个文件资源时,‘读写指针’ 设置
  • fork 或 exec 函数调用中的文件描述符(在原进程中已打开的文件描述符在新进程中仍将保持打开(这不是绝对的,这里暂不讨论))
  • 父子进程都有各自的文件描述符,它们都指向同一个文件资源,它们各自独立。但是父子进程它们对同一个文件资源进行读写时,文件的 ‘读写位置’ 是相互影响的。
  • 每个进程都有一些与之关联的文件描述符。这是一些小值整数,可以通过它们访问打开的文件或设备。每个进程持有的文件描述符个数是有限的,取决于系统的配置情况。当一个程序开始运行时,它一般会有 3 个已经打开的文件描述符
文件描述符说明
0标准输入
1标准输出
2标准错误

模拟程序:在父进程中打开文件,然后在子进程中执行写入操作,最后父进程读取文件内容。父进程读取之前,设置了 ‘读写指针’ 的位置,否则无法读取子进程写入的内容,因为 ‘读写指针’ 处于文件末尾。代码如下:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>void main(){printf("parent running.\n");int res;char buf_wr[] = "9ijghjkk9988";char buf_rd[sizeof(buf_wr)];buf_rd[0] = '\0';int fd = open("/tmp/data.txt", O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);if(fd < 0){printf("error: open file:/tmp/data.txt failed.\n");exit(EXIT_FAILURE);}pid_t pid = fork();switch(pid){case 0:// 子进程printf("child process running.\n");res = write(fd, buf_wr, sizeof(buf_wr));if(res != sizeof(buf_wr)){printf("error: child process write failed.\n");exit(EXIT_FAILURE);}close(fd); // 子进程先关闭自己的文件描述符printf("child process done.\n");exit(0);case -1:// errorexit(EXIT_FAILURE);default:// 父进程// 父进程等待子进程终止wait(0);//// 设置文件的‘读写位置’,使其偏移到子进程执行写入操作时的位置//lseek(fd, 0 - sizeof(buf_wr), SEEK_CUR);res = read(fd, buf_rd, sizeof(buf_wr));if(res == 0){// EOFprintf("EOF\n");}else if(res > 0){printf("parent process done. readed string is '%s'\n", buf_rd);}else{// errorprintf("error: parent process read failed.\n");exit(EXIT_FAILURE);}close(fd);exit(EXIT_SUCCESS);}
}

运行结果,如下:

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test3 test3.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test3
parent running.
child process running.
child process done.  	// 子进程终止
parent process done. readed string is '9ijghjkk9988'  // 随后父进程终止

这篇关于2.2.1 进程管理,以及父子进程共享同一个文件资源时,文件的‘读写位置’会相互影响的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/586878

相关文章

10. 文件的读写

10.1 文本文件 操作文件三大类: ofstream:写操作ifstream:读操作fstream:读写操作 打开方式解释ios::in为了读文件而打开文件ios::out为了写文件而打开文件,如果当前文件存在则清空当前文件在写入ios::app追加方式写文件ios::trunc如果文件存在先删除,在创建ios::ate打开文件之后令读写位置移至文件尾端ios::binary二进制方式

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应