当前位置: 首页 > news >正文

Linux高并发服务器开发 第十六天(execlp/execl 进程回收/孤儿进程/僵尸进程 wait/waitpid回收 进程间的通信)

目录

1.exec函数族

1.1execlp

1.2execl

1.3练习

2.进程回收                                                                          

3.孤儿进程

4.僵尸进程

5.wait回收

6.waitpid回收

7.进程间通信 IPC

7.1 进程间通信的方法

7.2管道pipe


1.exec函数族

- 工作原理:

    - 将当前进程的 .text、.data ... 替换为所要加载的程序的 .text、.data ... ,然后让进程从新的 .text 第一条指令开始执行。但 进程 ID 不变

- 工作特性:

    - exec函数族函数,一旦调用成功执行新程序,不会返回!只有调用失败才返回, 错误值 -1, errno
    - 通常使用时,我们只需在 execxxx() 函数后,调用 perror 和 exit, 无需 if 判断。

1.1execlp

- p: PATH 。 该函数在使用时,自动借助 环境变量 PATH,找寻可执行程序。
    - 可以用来调用,系统的程序。

// 该函数,通常用来执行系统程序:ls、data、cp、cat ... 命令

int execlp(const char *file, const char *arg, .../* (char  *) NULL */);


参数:
    参1:带加载的程序名字。需要配合 PATH 使用。
    参2:argv0 -- 可执行文件名
    参3:argv1
    参4:argv2
    ...:argvN
    哨兵:NULL,最后必须传入一个 NULL 指针来标识参数列表的结束
返回值:
    成功:不返回
    失败:-1, errrno

- 示例:

int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == 0) {//execlp("ls", "-l", "-F", "-a", NULL);        这样传参错误!!!execlp("ls", "ls", "-l", "-F", "-a", NULL);perror("/bin/ls exec error");exit(1);} else if (pid > 0) {sleep(1);printf("parent\n");}return 0;
}

1.2execl

- 直接指定要加载的程序绝对访问路径。可以是系统可以执行文件,也用户自定义可执行文件。

int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
参数:
    参1:带加载的带有路径的程序名字。
    参2:argv0 -- 可执行文件名
    参3:argv1
    参4:argv2
    ...:argvN
    哨兵:NULL

示例:

int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == 0) {//execl("/bin/ls", "ls", "-l", "-F", "-a", NULL);execl("./while", "while", "aa", "bb", "cc", "dd", NULL);perror("/bin/ls exec error");exit(1);} else if (pid > 0) {sleep(1);printf("parent\n");}return 0;
}

1.3练习

- 编写程序创建子进程,子进程使用 exec族函数,获取当前系统中的进程详细信息,打印到一个文件中。
- 实现命令:ps  aux > out

int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == 0) {int fd = open("out", O_WRONLY|O_CREAT|O_TRUNC, 0644);if (fd == -1)sys_err("open error");// 重定向dup2(fd, STDOUT_FILENO);execlp("ps", "ps", "a", "u", "x", NULL);perror("execlp error");exit(1);} else if (pid > 0) {sleep(1);printf("I am parent\n");}return 0;
}

2.进程回收                                                                          

- fork后的子进程,其父进程有义务在子进程结束时,回收该子进程pcb。隔辈进程无回收关系。
- 进程终止:
    1. 关闭所有文件描述符
    2. 释放用户空间分配的内存。
    3. 进程的 pcb 残留在内核。保存进程结束的状态(正常:退出值。异常:终止其运行的信号编号)

3.孤儿进程

父进程,先于子进程终止。子进程沦为 “孤儿进程”。会被 init 进程领养。

使用命令:ps ajx 

ppid(父进程id)pid(进程id)gid(进程组id)sid(会话id)

4.僵尸进程

子进程终止,父进程未终止,但尚未对子进程回收。在此期间,子进程为 “僵尸进程”。

杀死进程命令:kill -9  进程id 。 只能杀死活跃的进程,对僵尸进程无效!!!

5.wait回收

- 只有 “父、子” 进程之间存在,回收关系。 爷孙进程、兄弟进程、叔侄进程... 不存回收关系!

#include <sys/wait.h>

pid_t wait(int *wstatus);
参:    
    传出参数。回收进程的状态。传 NULL,只回收进程的pcb,不获取退出状态。
返回值:
    成功:回收的进程pid
    失败:-1, errno

- 函数的作用:
    1. 阻塞等待子进程退出(终止)。
    2. 回收子进程残留在内核的 pcb。
    3. 获取子进程的退出状态(正常、异常)。—— 传出参数 :wstatus
- 回收子进程退出状态:
    - 正常退出:
        - 判断 WIFEXITED(status) 为真。
        - 进一步使用  WEXITSTATUS(status) 获取退出值。
    - 异常退出:
        - 判断 WIFSIGNALED(status) 为真。
        - 进一步 使用 WTERMSIG(status) 获取杀死子进程的信号的编号。
- 回收示例:

int main(int argc, char *argv[])
{int status = 0;pid_t wpid = 0;pid_t pid = fork();if (pid == -1)sys_err("fork err");else if (pid == 0) {printf("I'm child pid = %d\n", getpid());
#if 1execl("./abnor", "abnor", NULL);perror("execl err");exit(1);
#endifsleep(1);exit(73);} else {wpid = wait(&status);  // 保存子进程退出的状态.if (wpid == -1)sys_err("wait err");if (WIFEXITED(status)) {            // 宏函数为真,说明子进程正常终止.// 获取退出码printf("I'm parent, pid = %d child, exit code = %d\n", wpid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {  // 宏函数为真, 说明子进程被信号终止.// 获取信号编号printf("I'm parent, pid = %d child, killed by %d signal\n", wpid, WTERMSIG(status));}}

6.waitpid回收

pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
    pid:
        >0: 通过pid指定 回收某一个子进程。
        -1: 回收任意子进程。
        0: 回收 与父进程属于同一个进程组的 子进程。
                —— 子进程创建成功后,默认,会自动加入父进程进程组。
                
    wstatus:(传出)回收子进程状态。 --- 类似于 wait() 的参数。
    options:WNOHANG —— 指定回收方式为 “非阻塞”。
             0 —— 指定回收方式为 “阻塞”。等同于 wait()
返回值:
    > 0: 表成功回收的进程pid
    0: 函数调用时参3指定了 WNOHANG,子进程没有结束。
    -1:失败。 errno   

示例:waitpid 回收 N 个子进程。

int main(int argc, char *argv[])
{int i = 0;pid_t pid, wpid;for(i = 0; i < 5; i++) {pid = fork();if (pid == 0)break;}if (5 == i) {        // 父进程/*while ((wpid = wait(NULL))!=-1) {  // 阻塞等待子进程结束,回收printf("wait child %d\n", wpid);} *//*    while ((wpid = waitpid(-1, NULL, 0))!=-1) {  // 使用 waitpid 阻塞等待子进程结束,回收printf("wait child %d\n", wpid);} *///while ((wpid = waitpid(-1, NULL, WNOHANG))!=-1) {  // 使用 waitpid 非 阻塞回收子进程while ((wpid = waitpid(0, NULL, WNOHANG))!=-1) {  // 使用 waitpid 非 阻塞回收子进程if (wpid > 0) {printf("wait child %d\n", wpid);        // 正常回收一个子进程} else if (wpid == 0) {sleep(1);continue;}}printf("catch All child  finish\n");} else {sleep(i);printf("%dth child, pid = %d\n", i+1, getpid());}return 0;
}

总结:

==**一次wait、waitpid 调用,只能回收一个子进程!!!!**==

想回收 N 个子进程,需要将 wait、waitpid 调用 放于 循环中。

7.进程间通信 IPC

- 进程间通信的原理,借助 多个进程使用同一个 内核,借助内核,传递数据。

7.1 进程间通信的方法

1.  管道:最简单。
2.  信号:开销小。
3.  mmap映射:速度快,非血缘关系间。
4.  socket(本地套接字):稳定性好!

7.2管道pipe

- 实现原理:Linux 内核 使用环形队列机制,借助缓冲区(4k)实现。
- 特质:
    1. 本质:伪文件(实为内核缓冲区)
    2. 用于进程间通信,用两个文件描述符引用,一个读端,一个写端。
    3. 规定,数据从管道写端流入,从读端流出。
- 局限性:
    1. 自己写,不能自己读。
    2. 管道中的数据,读走没!不能反复读取!
    3. 半双工通信。(对讲机)
    4. 应用于血缘关系进程间。

使用的函数:

// 函数,调用成功,自动创建匿名管道,返回两个文件描述符,无需open,但需手动 close
int pipe(int pipefd[2]);
参:
    fd[0]:管道读端。r
    fd[1]:管道写端。w
返回值:
    成功:0
    失败:-1, errno

- 父子进程 管道通信 IPC。


http://www.mrgr.cn/news/89852.html

相关文章:

  • 第七天 开始学习ArkTS基础,理解声明式UI编程思想
  • 采用idea中的HTTP Client插件测试
  • OpenBMC:通过qemu-system-arm运行编译好的image
  • 【Pandas】pandas Series skew
  • 三星OEM版SSD固态硬盘Model码对应关系
  • 解决对axios请求返回对象进行json化时报“TypeError Converting circular structure to JSON“错误的问题
  • VIVADO生成DCP和EDF指南
  • 【漫话机器学习系列】084.偏差和方差的权衡(Bias-Variance Tradeoff)
  • C++开发(软件开发)常见面试题
  • Mysql知识梳理(数据库的锁梳理,Mysql优化)
  • Android13-系统服务大管家-ServiceManager进程-启动篇
  • 【从零开始系列】DeepSeek-R1:(本地部署使用)思维链推理大模型,开源的神!——Windows / Linux本地环境测试 + vLLM / SGLang远程部署服务
  • C# winforms 使用菜单和右键菜单
  • TaskBuilder低代码开发项目实战:项目简介
  • 2、k8s的cni网络插件和基本操作命令
  • 使用git commit时‘“node“‘ 不是内部或外部命令,也不是可运行的程序
  • JAVA安全—FastJson反序列化利用链跟踪autoType绕过
  • Android原生开发问题汇总
  • VMware下Linux和macOS遇到的一些问题总结
  • π0开源了且推出自回归版π0-FAST——打造机器人动作专用的高效Tokenizer:比扩散π0的训练速度快5倍但效果相当
  • idea——IDEA2024版本创建Sping项目无法选择Java 8
  • aliyun 的 ip 设置方法
  • 定时任务单线程消费 redis 中数据导致消费能力不足
  • DeepSeek本地化部署
  • mongodb 使用内存过大分析
  • 学习笔记:机器学习中的数学原理(一)