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

Linux下的系统编程——进程的执行与回收(八)

前言:

前面我们对进程已经有了一个初步的了解与认识,现在让我们学习一下进程中一些函数的具体使用,比如exec可以执行一些指定的程序,wait / waitpid可以回收子进程,什么是孤儿进程,什么是僵尸进程,下面让我们一起对这些进行中的操作进行学习吧

目录

一、exec函数族

1.execlp:

  2.execl:

3.execvp

4.exec函数族的一般规律:

二、回收子进程

1.孤儿进程:

2 .僵尸进程:

​编辑

3.wait:

4.waitpid

 5.waitpid回收多个子进程


一、exec函数族

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

1.execlp:

    int execlp(const char *file, const char *arg, ...);        借助 PATH 环境变量找寻待执行程序

        参1: 程序名

        参2: argv0

        参3: argv1

        ...: argvN

        哨兵:NULL

该函数通常用来调用系统程序。如: ls、date、cp、cat等命令。
 

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main(int argc,char *argv[])
{int i;pid_t pid;        //创建子进程if(pid == -1){perror("fork error");exit(1);}else if(pid == 0){        //子进程//execlp("ls","-l","-d","-h",NULL);//错误写法/************************************/execlp("ls","ls","-l","-h",NULL);    /************************************/perror("exec error");exit(1);}else if(pid > 0){        //父进程sleep(1);printf("I'm parent : %d\n",getpid());}return 0;
}

 

 date命令的实现:

execlp("date","date",NULL);

 

  2.execl:

 int execl(const char *path, const char *arg, ...);    自己指定待执行程序路径。(路径+程序名)

#include <stdio.h>int main(int argc, char **argv)
{printf("Hello, %s!\n", argv[1]);printf("Hello, world!\n");return 0;
}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main(int argc,char *argv[])
{int i;pid_t pid;     //创建子进程if(pid == -1){perror("fork error");exit(1);}else if(pid == 0){    //子进程//execlp("ls","-l","-d","-h",NULL);//execlp("date","date",NULL);/************************************/execl("./a.out","./a.out","linux",NULL);/************************************/perror("exec error");exit(1);}else if(pid > 0){    //父进程sleep(1);printf("I'm parent : %d\n",getpid());}return 0;
}

3.execvp

加载一个进程,使用自定义环境变量env


int execvp(const char*file, const char *argv[]);

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main(int argc,char *argv[])
{int i;pid_t pid;            创建子进程if(pid == -1){perror("fork error");exit(1);}else if(pid == 0){        //子进程//execlp("ls","-l","-d","-h",NULL);//execlp("date","date",NULL);//execl("./a.out","./a.out","linux",NULL);/************************************/char *argv[] = {"date",NULL};execvp("date",argv);/************************************/perror("exec error");exit(1);}else if(pid > 0){         //父进程sleep(1);printf("I'm parent : %d\n",getpid());}return 0;
}

4.exec函数族的一般规律:

        l:命令行参数列表

        p:使用PATH环境变量

        v:使用命令行参数数组

        exec函数一旦调试成功即执行新的程序,不返回。只要失败才返回,错误值-1。所以通常我们直接在exec函数调用后调用 perror()和exit()。无需if判断。· 

二、回收子进程

1.孤儿进程:

父进程死亡子进程进孤儿院

        孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
模拟孤儿进程:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{pid_t pid;pid = fork();if (pid == 0) {while (1) {printf("I am child, my parent pid = %d\n", getppid());sleep(1);}} else if (pid > 0) {printf("I am parent, my pid is = %d\n", getpid());sleep(9);printf("------------parent going to die------------\n");} else {perror("fork");return 1;}return 0;
}

 查看进程状态:ps ajx

 进程孤儿院:

  1   2035   2035   2035 ?            -1 Ss    1001   0:00 /lib/systemd/systemd --user

 解决方法:

                 杀死子进程:     kill -9 4871

2 .僵尸进程:

子进程死亡,父进程一直不管 

        僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(zombie)进程。(死亡以后没有回收)

        特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

模拟僵尸进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{pid_t pid;pid = fork();if (pid == 0) {printf("---child, my parent= %d, going to sleep 10s\n", getppid());sleep(10);printf("-------------child die--------------\n");} else if (pid > 0) {while (1) {printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);sleep(1);}} else {perror("fork");return 1;}return 0;
}

  查看进程状态:ps ajx

 解决方法:

        杀死父进程:   kill -9 4770

*3.wait:

    wait函数:    回收子进程退出资源, 阻塞回收任意一个。

    pid_t wait(int *status)

    

    参数:(传出) 回收进程的状态。

    返回值:成功: 回收进程的pid

                   失败: -1, errno

    函数作用1:    阻塞等待子进程退出

    函数作用2:    清理子进程残留在内核的 pcb 资源

    函数作用3:    通过传出参数,得到子进程结束状态

    
    获取子进程正常终止值

        WIFEXITED(status) --》 为真 --》调用 WEXITSTATUS(status) --》 得到 子进程 退出值。

    获取导致子进程异常终止信号

        WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常终止的信号编号。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{pid_t pid, wpid;int status;pid = fork();if (pid == 0) {printf("---child, my id= %d, going to sleep 10s\n", getpid());sleep(10);printf("-------------child die--------------\n");return 73;} else if (pid > 0) {//wpid = wait(NULL);          // 不关心子进程结束原因wpid = wait(&status);       // 如果子进程未终止,父进程阻塞在这个函数上if (wpid == -1) {perror("wait error");exit(1);}if (WIFEXITED(status)) {        //为真,说明子进程正常终止. printf("child exit with %d\n", WEXITSTATUS(status));}if (WIFSIGNALED(status)) {      //为真,说明子进程是被信号终止.printf("child kill with signal %d\n", WTERMSIG(status));}printf("------------parent wait finish: %d\n", wpid);} else {perror("fork");return 1;}return 0;
}

正常终止:

被信号终止:

*4.waitpid

waitpid函数:    指定某一个进程进行回收。可以设置非阻塞。          

    waitpid(-1, &status, 0) == wait(&status);

    pid_t waitpid(pid_t pid, int *status, int options)

    

参数:
        pid:指定回收某一个子进程pid

            > 0: 待回收的子进程pid

            -1:任意子进程

            0:同组的子进程。

        status:(传出) 回收进程的状态。

        options:WNOHANG 指定回收方式为,非阻塞。

  

 返回值:

        > 0 : 表成功回收的子进程 pid

        0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。

        -1: 失败。errno

回收任意子进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>int main(int argc,char *argv[])
{int i;pid_t pid,wpid;for(i = 0;i < 5;i++){if(fork()==0)       //循环期间,子进程不forkbreak;}if(i == 5){           //父进程//wait(NULL);//一次wait/waitpid函数调用,只能回收一个子进程/*****************************************/wpid = waitpid(-1,NULL,WNOHANG);//回收任意子进程,没有结束的子进程,父进程直接返回0/****************************************/if(wpid == -1){perror("waitpid error");exit(1);}printf("I'm parent ,wait a child finish :%d\n",wpid);}else{            //子进程,从break跳出sleep(i);printf("I'm %dth child\n",i+1);}return 0;
}


回收指定进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>int main(int argc,char *argv[])
{int i;pid_t pid,wpid,tmpid;for(i = 0;i < 5;i++){pid = fork();if(pid == 0){       //循环期间,子进程不forkbreak;}if(i == 2){tmpid = pid;printf("*************pid= %d***************\n",pid);}}if(i == 5){           //父进程,从表达式2跳出sleep(5);		  //设置睡眠,等所有子进程结束后再回收//wait(NULL);	   //一次wait/waitpid函数调用,只能回收一个子进程//wpid = waitpid(-1,NULL,WNOHANG);     //回收任意子进程,没有结束的子进程,父进程直接返回0printf("I am parent , before waitpid , pid = %d\n",tmpid);/********将前面sleep(5)屏蔽***************///wpid = waitpid(tmpid,NULL,0);         //指定一个进程回收,阻塞回收/****************************************//*****************************************/wpid = waitpid(tmpid,NULL,WNOHANG);     //指定一个进程回收,不阻塞/****************************************/if(wpid == -1){perror("waitpid error");exit(1);}printf("I'm parent ,wait a child finish :%d\n",wpid);	//wpid回收的是真正的子进程id}else{            //子进程,从break跳出sleep(i);printf("I'm %dth child,pid = %d\n",i+1,getpid());}return 0;
}

注意:

        一次wait/waitpid调用只能回收一个子进程,无法回收他孙子辈的进程,多次清理需要while

 5.waitpid回收多个子进程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>int main(int argc,char *argv[])
{int i;pid_t pid,wpid;for(i = 0;i < 5;i++){pid = fork();if(pid == 0){       //循环期间,子进程不forkbreak;}}if(i == 5){    //父进程/**********使用阻塞回收子进程********/while((wpid = waitpid(-1,NULL,0))){printf("wait child %d\n",wpid);}/***********************************/}else{        //子进程sleep(i);printf("I'm %dth child ,pid =%d\n",i+1,getpid());}return 0;
}

结束一个回收一个

之后返回-1,表示没有失败了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>int main(int argc,char *argv[])
{int i;pid_t pid,wpid;for(i = 0;i < 5;i++){pid = fork();if(pid == 0){       //循环期间,子进程不forkbreak;}}if(i == 5){/*********使用阻塞回收子进程***********//*while((wpid = waitpid(-1,NULL,0))){printf("wait child %d\n",wpid);}*//***********************************//*******使用非阻塞方式回收子进程******/while((wpid = waitpid(-1,NULL,WNOHANG)) != -1){if(wpid > 0){	printf("wait child %d\n",wpid);}else if(wpid == 0){sleep(1);continue;}/************************************/}}else{sleep(i);printf("I'm %dth child ,pid =%d\n",i+1,getpid());}return 0;
}

相关文章:

Linux下的系统编程——进程的执行与回收(八)

前言&#xff1a; 前面我们对进程已经有了一个初步的了解与认识&#xff0c;现在让我们学习一下进程中一些函数的具体使用&#xff0c;比如exec可以执行一些指定的程序&#xff0c;wait / waitpid可以回收子进程&#xff0c;什么是孤儿进程&#xff0c;什么是僵尸进程&#xf…...

第十九章 ObjectScript - 执行例程

文章目录 第十九章 ObjectScript - 执行例程执行例程New 命令 第十九章 ObjectScript - 执行例程 执行例程 执行例程时&#xff0c;使用DO命令&#xff0c;如下所示: do ^routinename要执行一个过程、函数或子程序(不访问其返回值)&#xff0c;可以使用以下命令: do label^ro…...

Podman安装与使用

1.Podman简介 Podman是一个无守护进程的容器引擎&#xff0c;用于在Linux系统上开发、管理和运行OCI容器。 Podman的主要功能包括&#xff1a; 创建和管理容器&#xff1a;Podman可以创建、启动、停止和删除容器&#xff0c;以及管理容器的生命周期。容器镜像管理&#xff1…...

C++ 嵌套循环

一个循环内可以嵌套另一个循环。C 允许至少 256 个嵌套层次。 语法 C 中 嵌套 for 循环 语句的语法&#xff1a; for ( init; condition; increment ) {for ( init; condition; increment ){statement(s);}statement(s); // 可以放置更多的语句 }C 中 嵌套 while 循环 语句的…...

锁( ReentrantLock,Synchronized)

1.lock和synchronized 语法层面 synchronized 是关键字&#xff0c;源码在 jvm 中&#xff0c;用 c 语言实现&#xff1b; Lock 是接口&#xff0c;源码由 jdk 提供&#xff0c;用 java 语言实现&#xff1b; 使用 synchronized 时&#xff0c;退出同步代码块锁会自动释放&…...

主频计算-架构真题(二十三)

某文件系统采用多级索引结构&#xff0c;若磁块大小为4K字节&#xff0c;每个块号需占4个字节&#xff0c;那么采用二级索引结构时的文件最大长度可占用&#xff08;&#xff09;个物理块。 1、1024 2、1024*1024 3、2048*2048 4、4096*4096 答案&#xff1a;B 解析&…...

docker安装redis实操记录

1.Docker拉取镜像 docker pull redis2.Docker挂载配置文件 创建挂载文件夹 mkdir -p /home/redis/data下载默认配置文件 redis.conf 3.启动redis 容器 docker run --restartalways --log-opt max-size100m --log-opt max-file2 -p 6379:6379 --name redis -v /home/redi…...

MobaXterm 突破14个session限制

通常情况下&#xff1a;随着工作时间的增长&#xff0c;我们会保存许许多多的linux到本地的mobastream&#xff0c;然后当超过14个&#xff0c;就会被被限制&#xff0c;这个会让人很头疼。 1. 安装python&#xff0c;配置好环境变量 测试安装成功&#xff1a; 2. 基于项目进行…...

使用Redisson实现高并发抢红包

一、概述 1、简介 在传统的抢红包场景中&#xff0c;如果面临高并发请求&#xff0c;通常需要考虑加锁来保证数据的一致性。而在分布式环境下&#xff0c;为了解决分布式锁的问题&#xff0c;我们可以使用Redisson这样的分布式Java对象和服务框架来实现。 本篇博客将演示如何…...

【网络编程】TCP/IP协议(互联网的基石)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…...

【VS Code插件开发】自定义侧边栏、视图(六)

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域优质作者、阿里云专家博主&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4e2; 资料领取&#xff1a;前端…...

lv3 嵌入式开发-8 linux shell脚本函数

目录 1 函数的定义 2 函数的调用 3 变量的作用域 4 练习 1 函数的定义 基本语法&#xff1a; function name() {statements[return value] }function是 Shell 中的关键字&#xff0c;专门用来定义函数&#xff1b; name是函数名&#xff1b; statements是函数要执行…...

国际版阿里云腾讯云免费开户:服务器怎样转移

阿里云服务器怎样转移 阿里云服务器作为云核算范畴的领军企业之一&#xff0c;为用户供应了高性能、可靠、安全的云服务器服务。随着业务的发展和需求的改动&#xff0c;或许会有需求将阿里云服务器进行转移的情况。本文将介绍阿里云服务器转移的步骤和注意事项&#xff0c;帮…...

区块链实验室(20) - FISCO控制台连接到指定的节点

在FISCO技术文档中&#xff0c;控制台默认采用config.toml作为配置文件&#xff0c;并指定了连接的节点地址和商品&#xff0c;如下所示。 [network] peers["127.0.0.1:20200", "127.0.0.1:20201"] # The peer list to connect在该案例中&#xff0c;控…...

网络渗透day10-工具和技术

以下为网络渗透的工具和技术。 让我更详细地描述网络渗透测试的各个阶段以及使用的工具。 1. 信息收集阶段&#xff1a; 目标识别&#xff1a; 在这一阶段&#xff0c;渗透测试人员确定测试的目标&#xff0c;例如特定的服务器、应用程序或网络。 开放源情报&#xff08;OSIN…...

SSE 和 WebSocket 应用

SSE 和 WebSocket 应用 一.SSE 和 WebSocket 对比二.SSE 和 WebSocket 调试SpringBoot 下 SSE 应用1.依赖2.启动类3.接口类4.Html 测试5.测试结果 SpringBoot 下 WebSocket 应用1.依赖2.启动类3.WS 切点配置4.WS连接类配置5.WS Html 测试6.测试结果 一.SSE 和 WebSocket 对比 …...

mac帧 arp

1.分片 2.MSS max segment size 3.跨网络的本质 就是经历很多的子网或者局域网 4.将数据从A主机跨网络送到B主机的能力 IP和mac IP解决的是路径选择的问题 5.数据链路层 用于两个设备&#xff08;同一种数据链路节点&#xff09;之间进行传递 6.以太网ether 7.局域网通…...

java面试题-Redis相关面试题

Redis相关面试题 面试官&#xff1a;什么是缓存穿透 ? 怎么解决 ? 候选人&#xff1a; 嗯~~&#xff0c;我想一下 缓存穿透是指查询一个一定不存在的数据&#xff0c;如果从存储层查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到 DB 去查询&…...

你用过 Maven Shade 插件吗?

文章首发地址 Maven Shade插件是Maven构建工具的一个插件&#xff0c;用于构建可执行的、可独立运行的JAR包。它解决了依赖冲突的问题&#xff0c;将项目及其所有依赖&#xff08;包括传递依赖&#xff09;合并到一个JAR文件中。 下面是对Maven Shade插件的一些详解&#xff…...

Android 后台启动Activity适配

在Android 9及以下版本&#xff0c;后台启动Activity相对自由&#xff0c;但是如果在Activity上下文之外启动Activity会有限制。 Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag所以此时需要给intent添加flag&#x…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...