Linux多进程(一)创建进程与进程控制

news2025/1/16 3:52:44

一、进程状态

进程一共有五种状态分别为:创建态就绪态运行态阻塞态(挂起态)退出态(终止态)其中创建态和退出态维持的时间是非常短的,稍纵即逝。主要是就绪态, 运行态, 挂起态三者之间的状态切换。

293193229657eb472

  • 就绪态:万事俱备,只欠东风(CPU资源
  • 运行态:获取到CPU资源的进程,正在运行
  • 阻塞态:进程被强制放弃CPU
  • 退出态:进程被销毁,占用的资源被释放

二、进程命令

2.1、查看进程

$ ps aux
	- a: 查看所有终端的信息
	- u: 查看用户相关的信息
	- x: 显示和终端无关的进程信息

image-20240422203146656

2.2、杀死进程

kill命令可以发送某个信号到对应的进程,进程收到某些信号之后默认的处理动作就是退出进程。

查看Linux中有哪些标准信号:

kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

9号信号(SIGKILL)的行为是无条件杀死进程,想要杀死哪个进程就可以把这个信号发送给这个进程,操作 kill -9 进程ID

三、创建进程

3.1、进程相关函数

3.1.1、获取当前进程的进程ID(PID)
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
3.1.2、获取当前进程父进程ID(PPID)
#include <sys/types.h>
#include <unistd.h>

pid_t getppid(void);
3.1.3、创建一个新的进程
#include <unistd.h>
pid_t fork(void);

fork调用会获得一份完整的系统程序镜像,子进程执行fork调用后返回的pid为0,父进程执行fork调用后返回的pid为子进程的PID。

3.2、fork剖析

fork调用后,父子进程的相同点:

  • 代码区:默认情况下父子进程地址空间中的源代码始终相同。
  • 全局数据区:父进程中的全局变量和变量值全部被拷贝一份放到了子进程地址空间中
  • 堆区:父进程中的堆区变量和变量值全部被拷贝一份放到了子进程地址空间中
  • 动态库加载区(内存映射区):父进程中数据信息被拷贝一份放到了子进程地址空间中
  • 栈区:父进程中的栈区变量和变量值全部被拷贝一份放到了子进程地址空间中
  • 环境变量:默认情况下,父子进程地址空间中的环境变量始终相同。
  • 文件描述符表: 父进程中被分配的文件描述符都会拷贝到子进程中,在子进程中可以使用它们打开对应的文件。

区别:

  • 父子进程各自的虚拟地址空间是相互独立的,不会互相干扰和影响。
  • 父子进程地址空间中代码区代码虽然相同,但是父子进程执行的代码逻辑可能是不同的。
  • 由于每个进都有自己的进程ID,因此内核区存储的父子进程ID是不同的。
  • fork成功之后,会返回两个值,父子进程的返回值是不同的。根据两个不同的值区分父进程子进程,并执行不同的逻辑。

四、exec族函数

在项目开发过程中,有时候有这种需求,需要通过现在运行的进程启动磁盘上的另一个可执行程序,也就是通过一个进程启动另一个进程,这种情况下我们可以使用exec族函数,函数原型如下:

#include <unistd.h>

extern char **environ;
int execl(const char *path, const char *arg, ... /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

这些函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代(也就是说用户区数据基本全部被替换掉了),只留下进程ID等一些表面上的信息仍保持原样。只有调用失败了,它们才会返回一个 -1。

exec族函数中最常用的有两个execl()execlp()

4.1、execl

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
  • path: 要启动的可执行程序的路径, 推荐使用绝对路径
  • arg: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同
  • ... : 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。
  • 函数执行成功, 没有返回值,如果执行失败, 返回 -1。

4.2、execlp

int execlp(const char *file, const char *arg, ...);
  • file : 可执行程序的名字

    • 在环境变量PATH中,可执行程序可以不加路径
    • 没有在环境变量PATH中, 可执行程序需要指定绝对路径
  • arg: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同

  • ... : 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。

  • 函数执行成功, 没有返回值,如果执行失败, 返回 -1

4.3、仿真

#include <stdio.h>
#include <unistd.h>

int main() {
    execl("/bin/ps", "title", "aux", NULL);
    return 0;
}

image-20240422205951687

五、进程控制

5.1、结束进程

#include <stdlib.h>
void exit(int status);
  • status退出码, 如果参数值为 0 程序退出之后的状态码就是0, 如果是100退出的状态码就是100。

在 main 函数中直接使用 return也可以退出进程, 假如是在一个普通函数中调用 return 只能返回到调用者的位置,而不能退出进程。

5.2、孤儿进程

在一个启动的进程中创建子进程,这时候父子进程同时运行,但是父进程由于某种原因先退出了,子进程还在运行,这时候这个子进程就可以被称之为孤儿进程(父亲死了,成孤儿了)。

操作系统是非常关爱运行的每一个进程的,当检测到某一个进程变成了孤儿进程,这时候系统中就会有一个固定的进程领养这个孤儿进程(有干爹了)。如果使用Linux没有桌面终端,这个领养孤儿进程的进程就是 init 进程(PID=1)也就是第一个进程,如果有桌面终端,这个领养孤儿进程就是桌面进程。

系统为什么要领养这个孤儿进程呢?在子进程退出的时候, 进程中的用户区可以自己释放, 但是进程内核区的pcb资源自己无法释放,必须要由父进程来释放子进程的pcb资源,孤儿进程被领养之后,这件事儿干爹就可以代劳了,这样可以避免系统资源的浪费。

下面这段代码可以得到一个僵尸进程

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

int main()
{
    // 创建子进程
    pid_t pid = fork();
    // 父进程
    if(pid > 0) {
        printf("我是父进程, pid=%d\n", getpid());
    }
    else if(pid == 0) {   
        // 休眠,等待父进程退出
        sleep(1);
        // 子进程
        printf("我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());
    }
    return 0;
}

image-20240422211007521

领养子进程的是桌面进程,ID号为1449

5.3、僵尸进程

在一个启动的进程中创建子进程,这时候就有了父子两个进程,父进程正常运行, 子进程先与父进程结束, 子进程无法释放自己的PCB资源, 需要父进程来做这个件事儿, 但是如果父进程也不管, 这时候子进程就变成了僵尸进程。

僵尸进程不能将它看成是一个正常的进程,这个进程已经死亡了,用户区资源已经被释放了,只是还占用着一些内核资源(PCB)。

僵尸进程的出现是由于这个已死亡的进程的父进程不作为造成的。

创建五个僵尸进程

int main()
{
    pid_t pid;
    // 创建子进程
    for(int i=0; i<5; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    // 父进程
    if(pid > 0)
    {
        // 需要保证父进程一直在运行
        // 一直运行不退出, 并且也做回收, 就会出现僵尸进程
        while(1)
        {
            printf("我是父进程, pid=%d\n", getpid());
            sleep(1);
        }
    }
    else if(pid == 0)
    {
        // 子进程, 执行这句代码之后, 子进程退出了
        printf("我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());
    }
    return 0;
}

image-20240422211524727

得到了五个僵尸进程

消灭僵尸进程的方法是,杀死这个僵尸进程的父进程,这样僵尸进程的资源就被系统回收了。通过kill -9 僵尸进程PID的方式是不能消灭僵尸进程的,这个命令只对活着的进程有效,僵尸进程已经死了,鞭尸是不能解决问题的。

5.4、进程回收

为了避免僵尸进程的产生,一般我们会在父进程中进行子进程的资源回收,回收方式有两种,一种是阻塞方式wait(),一种是非阻塞方式waitpid()

5.4.1、wait

wait是阻塞函数,如果没有子进程退出, 函数会一直阻塞等待, 当检测到子进程退出了, 该函数阻塞解除回收子进程资源。这个函数被调用一次, 只能回收一个子进程的资源,如果有多个子进程需要资源回收, 函数需要被调用多次。

#include <sys/wait.h>

pid_t wait(int *status);
  • status 是一个指向整数的指针,用于存储子进程的终止状态。如果不关心子进程的终止状态,可以将该参数设置为 NULL

  • wait 函数返回已终止子进程的进程ID(PID),如果出错,则返回 -1

<sys/wait.h>头文件中定义了几个宏来帮助解释子进程的退出状态信息:

  • WIFEXITED(status):如果子进程正常退出(通过调用 exit()_exit()),则此宏返回非零值。
  • WEXITSTATUS(status):如果 WIFEXITED(status) 为真,这个宏返回子进程的退出状态码。
  • WIFSIGNALED(status):如果子进程因为信号而终止,此宏返回非零值。
  • WTERMSIG(status):如果 WIFSIGNALED(status) 为真,这个宏返回导致子进程终止的信号编号。
  • WIFSTOPPED(status):如果子进程当前是停止的,并且没有被恢复,此宏返回非零值(仅对使用 waitpid() 并指定 WUNTRACED 选项时有效)。
  • WSTOPSIG(status):如果 WIFSTOPPED(status) 为真,这个宏返回导致子进程停止的信号编号。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    pid_t pid;
    int status;

    // 创建子进程
    pid = fork();

    if (pid < 0) {
        // 错误处理
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程执行的代码
        printf("Child process: My PID is %d\n", getpid());
        exit(EXIT_SUCCESS);
    } else {
        // 父进程等待子进程的退出
        printf("Parent process: Waiting for child process to exit...\n");
        wait(&status);
        printf("Parent process: Child process with PID %d has exited\n", pid);
    }

    return 0;
}
5.4.2、waitpid

waitpid() 函数可以看做是 wait() 函数的升级版,通过该函数可以控制回收子进程资源的方式是阻塞还是非阻塞,另外还可以通过该函数进行精准打击,可以精确指定回收某个或者某一类或者是全部子进程资源。

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
  • 参数:
    • pid:
      • -1:回收所有的子进程资源, 和wait()是一样的, 无差别回收,并不是一次性就可以回收多个, 也是需要循环回收的
      • 大于0:指定回收某一个进程的资源 ,pid是要回收的子进程的进程ID
      • 0:回收当前进程组的所有子进程ID
      • 小于 -1:pid 的绝对值代表进程组ID,表示要回收这个进程组的所有子进程资源
    • status: NULL, 和wait的参数是一样的
    • options: 控制函数是阻塞还是非阻塞
      • 0: 函数是行为是阻塞的 ==> 和wait一样
      • WNOHANG: 函数是行为是非阻塞的
  • 返回值:
    • 如果函数是非阻塞的, 并且子进程还在运行, 返回0
    • 成功: 得到子进程的进程ID
    • 失败: -1
      • 没有子进程资源可以回收了, 函数如果是阻塞的, 阻塞会解除, 直接返回-1
      • 回收子进程资源的时候出现了异常
#include <sys/wait.h>

int main() {
    pid_t pid;
    // 创建子进程
    for(int i=0; i<5; ++i) {
        pid = fork();
        if(pid == 0) {
            break;
        }
    }

    // 父进程
    if(pid > 0) {
        while(1) {
            int status;
            pid_t ret = waitpid(-1, &status, WNOHANG);  // 非阻塞
            if(ret > 0)
            {
                printf("成功回收了子进程资源, 子进程PID: %d\n", ret);
                if(WIFEXITED(status)) {
                    printf("子进程退出时候的状态码: %d\n", WEXITSTATUS(status));
                }
                if(WIFSIGNALED(status)) {
                    printf("子进程是被这个信号杀死的: %d\n", WTERMSIG(status));
                }
            }
            else if(ret == 0) {
                printf("子进程还没有退出, 不做任何处理...\n");
            }
            else {
                printf("回收失败, 或者是已经没有子进程了...\n");
                break;
            }
            printf("我是父进程, pid=%d\n", getpid());
        }
    }
    // 子进程
    else if(pid == 0) {
        printf("===我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());
    }
    return 0;
}

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

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

相关文章

视频不够清晰怎么办?教你几种有效方法

在我们日常生活中&#xff0c;有时候我们会遇到不清晰的视频&#xff0c;这给我们带来了很多不便。那么&#xff0c;怎么将不清晰的视频变清晰呢&#xff1f;本文将为您介绍一些常用的软件工具&#xff0c;帮助您提升视频的清晰度。 方法一&#xff1a;使用AI技术 AI技术可以通…

低视力者出行升级:适配服务助力双手解放与环境感知

作为一名资深记者&#xff0c;我有幸深入了解并记录低视力者在日常出行中所面临的挑战与解决方案。近年来&#xff0c;低视力者辅助设备适配服务提供领域的创新成果&#xff0c;尤其是结合手机应用的辅助设备&#xff0c;正在以人性化、智能化的方式&#xff0c;帮助低视力者实…

Scala 05 —— 函数式编程底层逻辑

Scala 05 —— 函数式编程底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 Scala 05 —— 函数式编程底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函…

ELK创建仪表盘

创建仪表盘步骤&#xff1a; 一、保存search二、生成饼图三、创建仪表盘 一、保存search 首先保存一段时间内的search&#xff0c;可以添加想要的字段&#xff0c;并保存这个search方便下次直接打开该search&#xff0c;并方便在可视化和仪表盘中使用该search. 二、生成饼图…

基于JAVA高考志愿辅助填报系统

当今社会已经步入了科学技术进步和经济社会快速发展的新时期&#xff0c;国际信息和学术交流也不断加强&#xff0c;计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统高考志愿辅助填报采取了人工的管理方法&#xf…

销帮帮CRM与电商运营增效的关系?

在电商运营中&#xff0c;不同部门之间往往存在信息壁垒&#xff0c;导致客户体验的不连贯。销帮帮CRM通过提供跨职能管理客户关系的共享平台和一体化工作流引擎&#xff0c;使员工能够使用正确的工具和数据更有效地管理跨业务线的客户关系&#xff0c;实现更互联的客户体验。这…

相亲平台app小程序

相亲平台app小程序是一种基于手机应用的微型程序&#xff0c;专为在线相亲交友活动设计。它提供了一系列的功能&#xff0c;旨在帮助用户更方便、更高效地找到心仪的伴侣。 首先&#xff0c;用户可以在个人资料部分上传照片、填写个人资料、设置兴趣爱好等信息&#xff0c;以便…

前端-vue项目debugger调试

一、前言 有的时候接受同事一个项目&#xff0c;用框架不一样&#xff0c;写的也不太规范&#xff0c;那么就需要打断点去学习改项目的流程了。 那么vue项目是如何debugger调试呢&#xff1f; 二、操作 大概理解一下&#xff0c;vue项目启动&#xff0c;大概是先启动框架&am…

【Web】DASCTF X GFCTF 2024|四月开启第一局 题解(全)

目录 EasySignin cool_index SuiteCRM web1234 法一、条件竞争(没成功) 法二、session反序列化 EasySignin 先随便注册个账号登录&#xff0c;然后拿bp抓包改密码(username改成admin) 然后admin / 1234567登录 康好康的图片功能可以打SSRF&#xff0c;不能直接读本地文…

T2T时代的基因组组装-文献精读-9

Genome assembly in the telomere-to-telomere era T2T时代的基因组组装&#xff0c;李恒大神的综述&#xff0c;昨天刚出刊&#xff0c;李恒也是samtools、seqtk等的核心作者。 seqtk安装与使用-seqtk-1.4&#xff08;bioinfomatics tools-012&#xff09; Samtools安装与…

DMAIC助力企业迅速响应客户诉求——客诉回复周期大缩减

近年来&#xff0c;客户的声音宛如企业的风向标&#xff0c;不容忽视。迅速而精准地回应客户的投诉&#xff0c;不仅是对品牌形象的捍卫&#xff0c;更是滋养客户满意度和忠诚度的甘泉。那么&#xff0c;我们如何借助DMAIC这一久经考验的质量管理法宝&#xff0c;来缩短客户投诉…

jdk版本冲突,java.lang.UnsupportedClassVersionError: JVMCFRE003

主要是编辑器所用的jdk版本和项目用的不一致导致的&#xff0c;虽然编译通过了&#xff0c;但是运行是会报错 选好后点击Apply点击ok&#xff0c;然后重新编译一遍项目就可以了

Python 全栈体系【四阶】(三十一)

第五章 深度学习 五、PaddlePaddle 基础 1. PaddlePaddle 简介 1.1 什么是 PaddlePaddle PaddlePaddle&#xff08;Parallel Distributed Deep Learning&#xff0c;中文名飞桨&#xff09;是百度公司推出的开源、易学习、易使用的分布式深度学习平台 源于产业实践&#xf…

Java基本语法(基础部分)

Java基本语法 文章目录 Java基本语法前言一、准备工作1.1 计算机软件与硬件1.2 计算机编程语言1.3 Java语言概述&程序分析1.4 Java环境搭建&Java API1.5 Java核心机制JVM 二、变量2.1 关键字&标识符2.2 变量2.3 数据类型(基本数据类型)2.3.1 基本数据类型2.3.2 基本…

【ONE·基础算法 || 递归 二叉树深搜】

总言 主要内容&#xff1a;编程题举例&#xff0c;熟悉理解递归类题型&#xff0c;进一步加深理解深搜&#xff0c;引入回溯和剪枝相关概念。       1、递归 1&#xff09;、递归   实际在学习语言中我们对其有一定涉及。这里&#xff0c;若从宏观视角看待递归的过程&am…

30个数据工程中最常用的Python库(下)

六、数据解析和ETL库 库&#xff1a;beautifulsoup4 数据工程并不总是意味着从数据存储和仓库中获取数据。通常&#xff0c;数据需要从非结构化的来源&#xff08;如网络或文档等&#xff09;中提取出来。Beautiful Soup是一个库&#xff0c;它可以轻松地从网页上抓取信息。它…

互联网营销两大宗师:周鸿祎和雷军做个人IP有什么不同?

前几天周鸿祎说要把自己的迈巴赫卖了,准备换国产新能源,还喊话让各个车企给他送车去体验。不少车企都送去了自己的最新车型,只有雷军直接回答,“等SUV出”。我们是在吃瓜,作者却是从中看到了新老营销宗师的手法不同。 最近,在纪念互联网30周年的座谈会上,发生了一件趣事…

高级控件4:Spinner

Spinner下拉列表组件 主要集合ArrayAdapter、SimpleAdapter以及自定义的Adapter&#xff08;继承自BaseAdapter&#xff09;配合使用实现下拉选择或者对话框中选择某一条目。下拉使用的更多&#xff0c;所以&#xff0c;接下来的案例也会重在演示下拉效果。 本次基本就是上代…

亚马逊运营干货:多账号多店铺防关联技巧

随着亚马逊识别技术的提升&#xff0c;以及政策的加强&#xff0c;不少跨境电商的卖家都面临着多账号多店铺被关联的风险&#xff0c;这个时候&#xff0c;卖家就需要做好相关的防关联措施了&#xff0c;下面这些方法很有效&#xff0c;可以去参考&#xff01; 亚马逊多账号多…

如何在Windows服务做性能测试(CPU、磁盘、内存)

目录 前言1. 基本知识2. 参数说明 前言 由于需要做一些接口测试&#xff0c;测试是否有真的优化 1. 基本知识 该基本知识主要用来用到Performance Monitor&#xff0c;以下着重介绍下这方面的知识 性能监视器&#xff08;Performance Monitor&#xff09;&#xff1a;Windo…