[Linux]进程控制详解

news/2024/9/19 14:38:01 标签: linux, 运维, 服务器

1.创建进程

在这里插入图片描述
进程调用fork,当控制转移到内核中的fork代码后,内核做:
● 分配新的内存块和内核数据结构给子进程
● 将父进程部分数据结构内容拷贝至子进程
● 添加子进程到系统进程列表当中
● fork返回,开始调度器调度

这个前面提到过,就不多说了

2.写时拷贝

引言:当程序变成二进制后,所有的变量名都会变成地址。
fork之后代码共享,就是子进程被创建时,会以父进程为模板,子进程和父进程默认拥有一份相同的代码和数据,当然页表中存储的地址也是一样的。
在这里插入图片描述
在这里插入图片描述
两个问题:
创建子进程时,为什么不直接把父进程的数据给一份?
因为操作系统按需分配,节省空间。

为什么要拷贝?开新空间就好了啊
这里的写:包括了增删查改,不是全被都被覆盖。可能只修改一部分的数据

上图中页表部分括号里有个只读权限,接下来我们分析一下页表

(i)页表

页表不仅仅有虚拟地址和物理地址,物理地址还包括了权限
在这里插入图片描述
举例:字符常量无法被修改,就是我们在写入时,由虚拟地址到物理地址的转化中,权限中没有写入权限。
写代码时const的意义:把运行时产生的错误提前到编译时(防御性编程)

3.终止进程

进程退出情况

我们写代码时,最后都会return 0,main函数的返回值,就是进程的退出码
一般0表示成功,非0表示失败。
在进程执行结束之后,如果进程失败了,我们最想知道的是他的错误原因

echo $?
这个可以查看进程的退出码

当然错误码我们是无法理解的,所以就有错误码转化为错误描述
strerror()
在这里插入图片描述
我们可以看看linux中的错误码

int main()
{
    for(int i=0;i<200;i++)
    {
        printf("%d: %s\n",i,strerror(i));
    }
    return 0;
}

在这里插入图片描述

函数退出情况

除了进程有退出,函数也是有退出的。
函数也是有返回值的,函数的返回值就是错误码,这个和进程的一样
在这里插入图片描述

总结

总结:
任何进程的最终执行情况我们用两个数字表示==(异常)信号编号+进程退出码

如何让进程退出?
1.main函数return
2.exit(退出码)——终止进程,status(进程退出码)
这个进程退出码status会在进程等待中详细的说

4.进程等待

我们提到的父进程如果不等待子进程,就会使子进程变成僵尸进程,会引发内存泄漏
在这里插入图片描述
所以父进程通过wait的方式去回收子进程的资源,这个是必须要做的事情。
在这里插入图片描述
wait会默认进行阻塞等待,等待任意一个子进程;
返回值大于0,等待成功,等待子进程的pid;返回值小于0,等待失败

int main()
{

    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt)
        {
            printf("I am child,pid: %d,ppid: %d\n", getpid(), getppid());
            cnt--;
            sleep(1);
        }
        printf("马上变僵尸\n");
        exit(0);
    }
    sleep(10);
    // father
    pid_t rid = wait(NULL);
    if (rid > 0)
    {
        printf("wait success child_id:%d\n", rid);
    }
    return 0;
}

在这里插入图片描述
wait比较简单,我们来看看waitpid()
在这里插入图片描述
status:局部使用的位图形式在这里插入图片描述
在这里插入图片描述
status的8到15位时退出码,0到7时退出信号。

提取status的信号:status&0x7F
这样就把他的前7位的数字取出来了;
提取status的退出码:(status>>8)&0xFF
这样就把他的次低8位的数字取出来了.
当然这种方式太麻烦了,我们有专门的宏来完成这件事
在这里插入图片描述

if (WIFEXITED(status))
    {
        printf("wait success,rid: %d,status: %d,exit code: %d\n",rid,status,WEXITSTATUS(status));
    }

非阻塞等待才是重点
在这里插入图片描述
这里当options设位0,就是阻塞等待,设为WNOHANG就是非阻塞等待。
非阻塞:当在等待子进程退出的时候,父进程还可以去做别的事情

waitpid的返回值:
● 大于0:系统调用成功
● 等于0:系统调用成功,但子进程还没退出
● 小于0:调用失败

我们用代码来看看非阻塞等待的优势

const static int NUM=3;
typedef void(*func_t)();

func_t task[NUM];


void printfNAME()
{
    printf("this is print name\n");
}
void printfNPC()
{
    printf("this is print npc\n");
}
void printfAGE()
{
    printf("this is print age\n");
}

void InitLog()
{
    task[0]=printfNAME;
    task[1]=printfNPC;
    task[2]=printfAGE;
    task[3]=NULL;
}
void Exe()
{
    for(int i=0;task[i];i++)
    {
        task[i]();
    }
}
int main()
{
    InitLog();
    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt)
        {
            printf("I am child,pid: %d,ppid: %d\n", getpid(), getppid());
            cnt--;
            sleep(1);
        }
        sleep(12);
        exit(111);
    }
    // father
    int status=0;
    pid_t rid = waitpid(id,&status,WNOHANG);
    while(1)
    {
        if(rid>0)
        {
            printf("wait sucess,rid: %d,status: %d,exit code: %d\n",rid,status,WEXITSTATUS(status));
            break;
        }
        else if(rid==0)
        {
            printf("child is running,do other thing\n");
            //开始任务
            printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
            Exe();
        }
        else
        {
            perror("waitpid");
            break;
        }
        sleep(1);
    }

    return 0;
}

当子进程还在运行的时候,父进程不是傻傻的等,等待的时候可以去完成别的工作。


http://www.niftyadmin.cn/n/5665715.html

相关文章

pybind11 学习笔记

pybind11 学习笔记 0. 一个例子1. 官方文档1.1 Installing the Library1.1.1 Include as A Submodule1.1.2 Include with PyPI1.1.3 Include with Conda-forge 1.2 First Steps1.2.1 Separate Files1.2.2 PYBIND11_MODULE() 宏1.2.3 example.cpython-38-x86_64-linux-gnu.so 的…

JAVA-集合相关

HashMap如何解决哈希冲突的&#xff1f; 计算hash值&#xff0c;基于hashCode计算冲突之后&#xff0c;先是使用链式寻址法当链表长度大于8&#xff0c;且hash表的容量大于60的时候&#xff0c;再添加元素则转化成红黑树 为什么计算hash值是&#xff0c;是将hash地址的值右移1…

Vue+nodejs+express汽车配件商城销售管理系统 i9cgz

目录 技术栈具体实现截图系统设计思路技术可行性nodejs类核心代码部分展示可行性论证研究方法解决的思路Express框架介绍源码获取/联系我 技术栈 该系统将采用B/S结构模式&#xff0c;开发软件有很多种可以用&#xff0c;本次开发用到的软件是vscode&#xff0c;用到的数据库是…

谈谈你对线程池的了解

一、什么是线程池 线程池是一种创建和管理线程的技术。 二、怎么创建线程池 通过Executors工具类的静态方法&#xff0c;创建线程池。创建ThreadPoolExecutor对象&#xff0c;按照业务需要&#xff0c;自定义线程参数&#xff0c;创建线程池。 三、线程池的状态有哪些 线程池的…

ESP32-S3百度文心一言大模型AI语音聊天助手(支持自定义唤醒词训练)【手把手非常详细】【万字教程】

简介 此项目主要使用ESP32-S3实现一个AI语音聊天助手&#xff0c;可以通过该项目熟悉ESP32-S3 arduino的开发&#xff0c;百度语音识别&#xff0c;语音合成API调用&#xff0c;百度文心一言大模型API的调用方法&#xff0c;音频的录制及播放&#xff0c;SD卡的读写&#xff0c…

API 接入前的安全防线:注意事项全梳理

在当今数字化的商业环境中&#xff0c;API&#xff08;Application Programming Interface&#xff09;的广泛应用为企业带来了诸多便利&#xff0c;但同时也伴随着潜在的安全风险。在接入 API 之前&#xff0c;构建坚实的安全防线至关重要。以下是对 API 接入前安全注意事项的…

5分钟部署Prometheus+Grafana批量监控Linux服务器

给你送挂来了&#xff0c;宝~~ 详细讲解如何在 Linux 服务器上使用 Docker 容器快速部署 Prometheus 和 Grafana 监控系统&#xff0c;同时通过 node_exporter 采集全面的系统性能数据。整个流程涵盖了从环境配置到搭建一个全面监控平台的每个步骤 1&#xff0c;一键安装Node…

自监督的主要学习方法

自监督学习是一种机器学习方法&#xff0c;其中模型从未标注的数据中学习生成标签&#xff0c;通常通过构造预训练任务或预测任务来从数据的内部结构中提取信息。它的核心目标是利用无监督的数据进行学习&#xff0c;从而在下游任务中更好地利用监督信号。自监督学习的主要方法…