linux并发服务器 —— 多进程并发(四)

news2025/1/14 0:51:12

进程概述

程序是包含一系列信息的文件,描述了如何在运行时创建一个进程;

进程是正在运行的程序的实例,可以用一个程序来创建多个进程;

用户内存空间包含程序代码以及代码所使用的变量,内核数据结构用于维护进程状态信息;

进程控制块(PCB):维护进程相关的信息,task_struct结构体

PCB内部成员:进程id、进程的状态、进程切换时需要保存和恢复的一些CPU寄存器、虚拟地址空间信息、控制终端信息等

进程可以使用的资源上线可以调用: ulimit -a 进行查询

 进程状态转换

三态模型:就绪、运行、阻塞

五态模型:新建、就绪、运行、阻塞、终止

阻塞态不能直接变为运行态,需要先变为就绪态;

新建态:进程刚被创建,还没有分配资源,尚未进入就绪队列;

终止态:进程完成任务到达正常结束点,或出现错误而异常终止,或被新操作系统以及有终止权的进程所终止;

查看进程:ps aux/ajx(不能动态显示)

a - 显示终端所有进程;

u - 显示进程详细信息

x - 显示没有控制终端的进程;

j - 列出与作业控制相关的信息

实时显示进程动态:top (-d 指定时间间隔)

按键排序:

M - 内存降序

P - CPU占有率降序

U - 根据用户名筛选

K - 杀死进程

T - 根据运行时长排序

杀死进程:kill PID

kill -9 PID(强制杀死进程)

killall name 根据进程名杀死进程;

进程号相关函数

进程号的范围 0~32767;

getpid(void)、getppid(void)、getpgid(pid_t pid)

进程创建

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

    pid_t fork(void);
        返回值:
            返回两次;一次在父进程中,一次在子进程中
            父进程中返回子进程的ID
            子进程中返回0
            如何区分父进程和子进程 - 通过fork返回值;   
            父进程中返回-1表示创建子进程失败,并设置errno
            失败的原因:
                1. 进程数上限
                2. 系统内存不足
*/
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main(){
    // 创建子进程
    pid_t pid = fork();

    // 判断父子进程
    if(pid>0){
        cout<<pid<<endl;
        cout<<"父进程 - 进程号:"<<getpid()<<endl;
    }
    else if(pid == 0){
        cout<<"子进程 - 进程号:"<<getpid()<<endl;
    }

    for(int i = 0 ; i<3 ; i++){
        cout<<"i: "<<i<<" "<<getpid()<<endl;
        sleep(1);
    }

    return 0;
}



父子进程虚拟地址空间的情况

子进程用户区数据和父进程一样,内核区也会拷贝,但pid不同;

fork()是通过写时拷贝实现的,资源的复制在需要写入时才进行,在此之前以只读方式进行共享;

父子进程的关系及GDB多线程调试

父子进程间的关系

区别:

        1. fork()返回值不同

        2. pcb中的一些数据 eg. 当前进程pid  ppid、信号集

共同点:

        子进程刚被创建,没执行任何写操作

                - 用户区数据

                - 文件描述符表          

父子进程对变量是不是共享的?

         - 读时共享,写时拷贝;

GDB多进程调试

GDB默认只能跟踪一个进程 默认跟踪父进程;

- 显示跟踪进程:show follow-fork-mode

- 设置调试父进程和子进程:set follow-fork-mode [parent(默认)|child]

- 显示调试模式:show detach-on-fork

- 设置调试模式:set detach-on-fork [on|off]

默认为on,表示调式当前进程时,其他进程继续运行;off表示调式当前进程,其它进程被GDB挂起,停在fork处;

- 查看调试的进程:info inferiors

- 切换调试进程:inferior 进程编号 后 c即可

- 使进程脱离GDB调试:detach inferior id

exec函数族(一系列函数)

作用:根据指定文件名找到可执行文件;用其取代调用进程的内容(在调用进程内部执行一个可执行文件);但它不会生成新的进程

exec函数族的函数执行成功不返回,调用失败会返回-1 , 从调用点接着往下执行;

execl函数
/*
    #include <unistd.h>
    int execl(const char *path, const char *arg, ...);
        参数:
            path - 需要指定的可执行文件路径/名称
                a.out   /home/nowcoder/a.out(推荐)
            arg - 可执行文件所需的参数列表
                1st - 一般没有作用,一般写执行程序名称
                参数列表必须以NULL结束(哨兵)
        返回值:出错返回-1 并设置errno
*/
#include <unistd.h>
#include <iostream>
using namespace std;

int main(){
    // 创建一个子进程 在子进程执行exec函数族中的函数
    pid_t pid = fork();
    if(pid>0){
        cout<<"我是你爹"<<" "<<getpid()<<endl;
        sleep(1);
    }
    else if(pid == 0){
        execl("hello" , "hello" , NULL);
        cout<<"我是你儿子"<<" "<<getpid()<<endl;
    }

    for(int i = 0 ; i<3 ; i++){
        cout<<i<<" "<<getpid()<<endl;
    }

    return 0;
}

execlp 从环境变量查可执行文件
/*
    #include <unistd.h>
    
    int execlp(const char *file, const char *arg,);
        -- 会到环境变量中查可执行文件 找不到执行失败
        参数:
            file - 需要指定的可执行文件的文件名
                a.out   /home/nowcoder/a.out(推荐)
            arg - 可执行文件所需的参数列表
                1st - 一般没有作用,一般写执行程序名称
                参数列表必须以NULL结束(哨兵)
        返回值:出错返回-1 并设置errno
*/
#include <unistd.h>
#include <iostream>
using namespace std;

int main(){
    // 创建一个子进程 在子进程执行exec函数族中的函数
    pid_t pid = fork();
    if(pid>0){
        cout<<"我是你爹"<<" "<<getpid()<<endl;
        sleep(1);
    }
    else if(pid == 0){
        execlp("ps" , "ps" , "aux" , NULL);
        cout<<"我是你儿子"<<" "<<getpid()<<endl;
    }

    for(int i = 0 ; i<3 ; i++){
        cout<<i<<" "<<getpid()<<endl;
    }

    return 0;
}

进程退出、孤儿进程、僵尸进程

进程退出:exit(标准C库)、_exit(linux系统函数)

/*
    #include <stdlib.h>
    void exit(int status);

    #include <unistd.h>
    void _exit(int status);

    status - 进程退出时的状态信息 父进程回收子进程资源时可以获取
*/
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main(){
    cout<<"hello"<<endl;
    cout<<"world";

    // exit(0); // hello world
    _exit(0); // hello

    return 0;
}

孤儿进程:父进程运行结束,子进程还在运行 -> 孤儿进程;

 - 内核会把孤儿进程的父进程设置为init , init进程会循环wait()退出的子进程;

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main(){
    // 创建子进程
    pid_t pid = fork();

    // 判断父子进程
    if(pid>0){
        cout<<pid<<endl;
        cout<<"父进程 - 进程号:"<<getpid()<<endl;
    }
    else if(pid == 0){
        sleep(1);
        cout<<"子进程 - 进程号:"<<getpid()<<endl;
        cout<<"子进程 - 父进程:"<<getppid()<<endl;
    }

    for(int i = 0 ; i<3 ; i++){
        cout<<"i: "<<i<<" "<<getpid()<<endl;
    }

    return 0;
}

 父进程死亡后切换到前台(出现上述现象);

僵尸进程:进程终止,可以释放用户区的数据,内核区的PCB没办法自己释放,需要父进程进行释放。如果父进程尚未回收,子进程残留资源存放于内核;变成僵尸进程;

不能被kill -9 杀死;

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main(){
    // 创建子进程
    pid_t pid = fork();

    // 判断父子进程
    if(pid>0){
        cout<<pid<<endl;
        while(1){
            cout<<"父进程 - 进程号:"<<getpid()<<endl;
            sleep(1);
        }
    }
    else if(pid == 0){
        cout<<"子进程 - 进程号:"<<getpid()<<endl;
        cout<<"子进程 - 父进程:"<<getppid()<<endl;
    }

    for(int i = 0 ; i<3 ; i++){
        cout<<"i: "<<i<<" "<<getpid()<<endl;
    }

    return 0;
}



 处理方法:

1. 父进程调wait()/waitpid()

2. 杀死父进程,让Init接管子进程进行释放处理;

wait/waitpid 函数

wait()函数会阻塞,waitpid()可以设置不阻塞,并且waitpid()可以指定等待哪个子进程结束;

一次wait/waitpid只能清理一个子进程,清理多个子进程应该使用循环;

wait()

调用wait的进程会被挂起,直到其一个子进程退出或遇到不可忽略的信号;

如果其没有子进程或者子进程都结束了会立刻返回-1;

/*
    #include <sys/types.h>
    #include <sys/wait.h>

    pid_t wait(int *wstatus);
        等待任一子进程结束 然后回收子进程资源;
        参数:
            wstatus - 进程退出时的状态信息(传出参数)
        返回值:
            成功 - 被回收的子进程id
            失败 - -1
                1. 所有的子进程都结束
                2. 调用函数失败
*/
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;

int main(){
    // 创建5个子进程
    pid_t pid;

    for(int i = 0 ; i<5 ; i++){
        pid = fork();
        if(pid == 0){
            break;
        }
    }

    if(pid>0){
        while(1){
            cout<<"我是你爹: "<<getpid()<<endl;
            
            int ret = wait(NULL);// NULL 不获取状态
            if(ret == -1){
                break;
            }
            cout<<"捕获到了子进程:"<<ret<<endl;
            
            sleep(2);
        }
    }
    else if(pid == 0){
        while(1){
            cout<<"我是你儿子: "<<getpid()<<endl;
            sleep(2);
        }
    }

    return 0;
}

waitpid()

/*
    #include <sys/types.h>
    #include <sys/wait.h>

    pid_t waitpid(pid_t pid, int *wstatus, int options);
        功能:回收指定进程号子进程 设置阻塞/非阻塞
        参数:
            pid
                <-1 - 回收某个进程组的子进程 组id == abs(pid)
                -1 - 回收所有子进程 相当于wait()
                0 - 回收当前进程组的所有子进程
                >0 - 回收指定子进程ID进程
            watatus - 同wait
            options
                0 - 阻塞
                WNOHANG - 非阻塞
            返回值:
                >0 - 子进程ID
                =0 - options=WNOHANG 表示还有子进程
                -1 - 错误/没有子进程
    waitpid(-1 , __ , 0) = wait(__);
*/

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

using namespace std;

int main(){
    // 创建5个子进程
    pid_t pid;

    for(int i = 0 ; i<5 ; i++){
        pid = fork();
        if(pid == 0){
            break;
        }
    }

    if(pid>0){
        while(1){
            cout<<"我是你爹: "<<getpid()<<endl;
            sleep(2);
            int st;
            // int ret = waitpid(-1 , &st , 0);
            int ret = waitpid(-1 , &st , WNOHANG);
            if(ret == -1){
                break;
            }

            if(ret == 0){
                cout<<"他妈的怎么还有子进程"<<endl;
                continue;
            }
            else{
                if(WIFEXITED(st)){
                    cout<<"退出的状态码:"<<WEXITSTATUS(st)<<endl;
                }

                if(WIFSIGNALED(st)){
                    cout<<"被哪个信号干掉:"<<WTERMSIG(st)<<endl;
                }

                cout<<"捕获到了子进程:"<<ret<<endl;
            }
        }
    }
    else if(pid == 0){
        while(1){
            cout<<"我是你儿子: "<<getpid()<<endl;
            sleep(2);
        }
    }

    return 0;
}

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

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

相关文章

大型医院影像信息系统(PACS)源码,自主研发,支持二开

影像信息系统&#xff08;PACS&#xff09;可接收和显示多种设备的不同影像&#xff0c;比如CT、DR、B超和彩超等&#xff0c;具有医学影像获取、存档、观片、处理、打印和多种管理功能。使资料保存更加完整&#xff0c;共享性更强。医生可以方便的对影像资料进行浏览、拷贝和光…

visual stdio 2015创建QDialog界面类

问题引入 在visual stdio 2015中创建的界面默认为QWidget&#xff0c;可手动修改为继承自QDialog界面类。操作方法 右键项目 -> 添加 -> Add Qt Class… 选择第二项&#xff0c;如下图

九、脉宽调制(pwm)与脉冲频率调制(pfm)

1、概述 pwm和pfm是两大类dc-dc转换器架构 每种类型的性能特征不一样 重载和轻载时的效率不一样 负载调节 设计复杂性能 EMI、噪声的考虑 集成式转换器解决方案可整合这两种操作 2、PWM控制架构 3、损耗部分 损耗组成部分&#xff1a; MOS管的开关损耗、栅极驱动器损耗&…

辉瑞乡村振兴战略下传统村落文化旅游设计小红书中美德少许

辉瑞乡村振兴战略下传统村落文化旅游设计小红书中美德少许

2021年06月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;数字变换 给定一个包含5个数字&#xff08;0-9&#xff09;的字符串&#xff0c;例如 “02943”&#xff0c;请将“12345”变换到它。 你可以采取3种操作进行变换 &#xff08;1&#xff09;交换相邻的两个数字 &#xff08;2&#xff09;将一个数字加1。如果…

表面计量封闭型高斯滤波器(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

按照json文件的值复制图片

按照json文件的值复制图片 文件格式处理当前JSON代码封装增加批处理 文件格式 0是不挑选&#xff0c;1是挑选 处理当前JSON # coding: utf-8 from PIL import Image, ImageDraw, ImageFont import os import shutil import cv2 as cv import numpy as np import jsondef read…

TDesign在按钮上加入图标组件

在实际开发中 我们经常会遇到例如 添加或者查询 我们需要在按钮上加入图标的操作 TDesign自然也有预备这样的操作 首先我们打开文档看到图标 例如 我们先用某些图标 就可以点开下面的代码 可以看到 我们的图标大部分都是直接用tdesign-icons-vue 导入他的组件就可以了 而我…

【【萌新的STM32学习20--按键输入实验】】

萌新的STM32学习20–按键输入实验 在了解完蜂鸣器的实验之后我们又进行一个小实验 又是正点原子没有上课讲解的实验 对于这种不平滑的情况我们更多的也会去选择方法进行消除抖动 软件消抖&#xff1a;方法很多&#xff0c;我们例程中使用最简单的延时消抖。检测到按键按下后&…

矿业配电柜监测,真的如此难以克服?

当今工业和商业领域中&#xff0c;电力作为生产和运营的基石&#xff0c;无可替代。在这个背景下&#xff0c;配电柜监控的重要性日益凸显。 配电柜作为电力系统的核心组成部分&#xff0c;其稳定运行直接关系到生产的连续性、安全性以及能源的高效利用。通过配电柜监控&#x…

小红书爆款种草笔记拆解:如何打造吸引眼球的内容!

小红书作为一个生活方式平台和消费决策入口&#xff0c;已成为众多年轻人所喜爱的种草圣地。在这里&#xff0c;用户可以通过分享美妆、穿搭、美食等各类生活方式&#xff0c;吸引大量的关注和点赞。然而&#xff0c;如何在小红书上打造一篇吸引眼球、引发用户种草的爆款笔记呢…

每日一题 823. 带因子的二叉树

每日一题 823. 带因子的二叉树 难度&#xff1a;中等 思路&#xff1a; 取乘积&#xff0c;那么两个叶子节点相乘一定会得到一个更大的数&#xff0c;所以先排序以父节点为根节点的数的数量 以右节点为根节点的数的数量 * 以左节点为根节点的数的数量初始化列表&#xff0c;…

vscode vue3自定义自动补全

敲代码多了&#xff0c;发现重发动作很多&#xff0c;于是还是定义自动补全代码吧——懒是第一生产力&#xff01; 1&#xff0c;Ctrl Shift P打开快捷命令行&#xff1a;找到下面这个 2&#xff0c;然后找到ts&#xff1a; 里面给了demo照着写就行 // "Print to conso…

【Qt QAxObject】使用 QAxObject 高效任意读写 Excel 表

1. 用什么操作 Excel 表 Qt 的官网库中是不包含 Microsoft Excel 的操作库&#xff0c;关于对 Microsoft Excel 的操作库可选的有很多&#xff0c;包含基于 Windows 系统本身的 ActiveX、Qt Xlsx、xlsLib、LibXL、qtXLS、BasicExcel、Number Duck。 库.xls.xlsx读写平台Qt Xls…

访问学者择校容易犯哪些错误?

在选择访问学者的学校时&#xff0c;很多人容易犯一些错误&#xff0c;这些错误可能会影响他们的学术和个人发展。下面就让知识人网小编带大家一起来探讨一下&#xff0c;访问学者在选择学校时容易犯的一些错误。 首先&#xff0c;许多访问学者容易陷入排名偏见。他们可能会过于…

什么是数字化工厂呢?为何MES管理系统是工业4.0的核心

随着互联网时代的来临&#xff0c;线上业务逐渐成为企业发展的新趋势。越来越多的企业开始将业务拓展到线上&#xff0c;开展数字化工厂转型。那么&#xff0c;什么是数字化工厂呢&#xff1f;它有哪些特点呢&#xff1f; 数字化工厂&#xff0c;是指以数字化技术&#xff0c;包…

React 的七个值得推荐的动画库

今天&#xff0c;Web 应用程序用户界面有许多吸引用户的元素&#xff0c;为了满足这些需求&#xff0c;开发人员不得不寻找新的方法来实现带有动画和过渡的 UI&#xff0c;因此&#xff0c;开发了专门的库和工具来处理 Web 应用程序中的动画。在本文中&#xff0c;将讨论一些优…

Ansible学习笔记2

Ansible是Python开发的自动化运维工具&#xff0c;集合了众多运维工具&#xff08;Puppet、cfengine、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置&#xff0c;批量程序部署、批量运行命令等功能。 特点&#xff1a; 1&#xff09;部署简单&#xff…

Ceph入门到精通-LVS基础知识

LB集群: &#xff08;Load Balancing&#xff09;即负载均衡集群,其目的是为了提高访问的并发量及提升服务器的性能&#xff0c;其 实现方式分为硬件方式和软件方式。 硬件实现方式&#xff1a; 常用的有 F5公司的BIG-IP系列、A10公司的AX系列、Citrix公司的 NetScaler系列…

延迟队列的理解与使用

目录 一、场景引入 二、延迟队列的三种场景 1、TTL对队列进行延迟 2、创建通用延时消息对消息延迟 3、使用rabbitmq的延时队列插件 x-delayed-message使用 父pom文件 pom文件 配置文件 config 生产者 消费者 结果 一、场景引入 我们知道可以通过TTL来对队列进行设…