深剖 Linux 进程控制

这篇具有很好参考价值的文章主要介绍了深剖 Linux 进程控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

传统艺能😎

小编是双非本科大一菜鸟不赘述,欢迎大佬指点江山,QQ - 1319365055

🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,打码路上一路向北,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
诚邀各位有志之士加入!!
直达: 社区链接点我


wexitstatus,Linux,C++,linux,服务器,进程

pc 指针🤔

我们说进程调用 fork ,当控制转移到内核的 fork 代码后,内核会:

  1. 将父进程部分数据拷贝给子进程;
  2. 分配新的内存块和数据结构给子进程;
  3. 子进程添加到进程列表;
  4. fork 返回,开始调度

因此 fork 之前父进程独立执行,fork 后父子分别执行,但是谁先谁后完全取决于调度器。

fork 之后,共享的只有 fork 后的代码吗?一般情况下,是父子共享所有代码,虽然子进程执行后续代码,它是相当于共享了所有代码但是子进程只能从某个地方开始执行!什么原理呢?很简单,在 CPU 里面有一种寄存器能保存当前的执行进度,它叫 eip,普遍喜欢叫它程序计数器或者 pc 指针,通过保存当前正在执行指令的下一条指令,拷贝给子进程。

为什么要写时拷贝?🤔

我们创建子进程的时候,直接给父子进程单独拷一份把数据分开,这样不香吗?

首先,这种方法是肯定可行的,但是不是最优的。父进程的数据,子进程不一定全用,即使全用也不一定全部写入,这样会有浪费空间的嫌疑。那为什么不把要用的数据拷一份?先不说技术上有没有人能完美实现,就算我拷贝了要用的但是不进行写入或修改的话绕一圈还是在浪费嘛。第三如果 fork 时无脑给子进程拷贝还会增加 fork 的成本,不管是内存还是时间。

所以采取写时拷贝,只会拷贝父子修改的就是拷贝的最小成本,但是拷贝的成本是不可消除的,这时一种延迟拷贝策略,他最大的意义就是你想要但是不立马使用的会先给别人提高了使用效率。

退出码🤔

我们知道程序结束会 return 0,但是大部分情况我们都是在无脑返回 0,程序最终返回 0 说明得到了我想要的结果,不为 0 就是失败了,那么非 0 返回值就是在标识失败的原因:return x ,这个 x 就是进程退出码

我们可以查看当前程序的退出码:

echo $?

wexitstatus,Linux,C++,linux,服务器,进程

这个 $? 就是 bash 中最近一次执行完毕时对应的进程退出码。普通的 C++ 代码可以无脑搞个 return 0,但是系统编程时强烈建议搞清楚当前程序的退出码,一般来讲失败的这个非 0 值是可以自定义的,主要是不同返回值方便我们定位不同问题。

exit 与 _exit🤔

main 函数中 return 代表进程终止,但是其他函数为什么不能 return 呢?不代表,return 仅仅代表当前函数调用结束!但是在自己代码中任意位置调用 exit ,就可以直接终止进程,exit 是C库函数,exit(x)中这个 x 就是自义定的退出码。

exit 的底层原理就是利用 _exit 函数 ,功能上二者无异,exit 调用是终止进程并且刷新缓冲区关闭文件流,而 _exit 是直接终止进程,不会有任何其他动作。

slab 分配器🤔

创建对象的过程包括了开辟空间和初始化,对象创建了像 task_struct,mm_struct 这种内核数据结构可能并不会被系统释放,那么系统会维护一个废弃的链表,不要的数据结构就放在这里,所以系统不是去释放而是放在这里使其无效化,当我们再度开辟时,就进行迭代即可,这样节约了申请时间直接跳到初始化,这种结构叫做内核数据结构缓冲池,也叫他 slab 分配器。

wexitstatus,Linux,C++,linux,服务器,进程

进程等待🤔

这个概念是根据僵尸进程提出的,为什么进程结束后不直接终止(X 状态)而是要先进入僵尸进程(Z 状态)?

子进程退出后如果父进程不管就会造成所谓的僵尸进程,从而造成内存泄漏,另外一旦变成僵尸进程连 kill 指令也束手无策,因为逻辑上你无法杀死一个已经死去的进程。其次,父进程有义务知道子进程的完成情况且结果是否正确,我们说到的退出码这里也是这样,确认子进程的退出状态就是子进程的退出码需要被父进程所读取。

所以进程等待实际上是父进程回收子进程资源,获取子进程退出信息的重要渠道!

进程等待方法🤔

wait😎

函数原型以及所需头文件:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);

返回值:等待成功则返回等待进程的PID,等待失败,返回-1;参数为输出型参数,获取子进程退出状态,不关心则可以设置成为 NULL 即可。

因此我们不妨用 wait 来验证一下僵尸进程和进程等待机制,通过 fork 创建子进程实现打印,然后终止掉子进程,子进程便处于僵尸状态,此时父进程是处于等待状态的,在10秒后开始对子进程进行处理(也就是获取子进程的pid),处理结束子进程退出,父进程此时再次等待10秒后退出

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t id = fork();
    if(id == 0){
        int ret = 5;
        while(ret){
            printf("child[%d] is running:ret is %d\n", getpid(), ret);
            ret--;
            sleep(1);
        }
        exit(0);
    }
    sleep(10);
    printf("father wait begin..\n");
    pid_t cur = wait(NULL);
    if(cur > 0){
        printf("father wait:%d success\n", cur);
    }
    else{
        printf("father wait failed\n");
    }
    sleep(10);
}

程序结果:

wexitstatus,Linux,C++,linux,服务器,进程
我们在运行的同时可以再复制一个 SSH 渠道的 shell 脚本来进行监视功能:

while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "**********************"; done

监视窗口如下,我们结果可以明显分为 4 大部分:

  1. 进程开始运行,父子进程同时运行。这里应该有 5 个因为打印 5 次,监视窗口手慢了漏了一次大家就当无事发生。
    wexitstatus,Linux,C++,linux,服务器,进程
  2. 子进程结束,父进程继续,因此子进程变成僵尸进程,触发父进程等待机制

wexitstatus,Linux,C++,linux,服务器,进程

  1. 子进程开始向父进程返回自己 pid,父进程通过等待机制回收子进程:

wexitstatus,Linux,C++,linux,服务器,进程

  1. 父进程得到子进程 pid 和 退出码,父进程退出,程序终止:

wexitstatus,Linux,C++,linux,服务器,进程

waitpid😎

所需头文件:

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);

返回值:当正常返回的时候waitpid返回收集到的子进程的进程ID;如果设置了选项WNOHANG,而调用中 waitpid 发现没有已退出的子进程可收集,则返回0;如果调用中出错,则返回 -1,这时 errno 会被设置成相应的值以指示错误所在

三个参数:

pid
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的PID。
具体的实现

waitpid 函数两种情况:

  1. pid_t cur = waitpid(id, NULL, 0);//等待指定一个子进程
  2. pid_t cur = waitpid(-1, NULL, 0);//等待任意一个子进程

这里我们使用第二种情况,大体思路和 wait 是一样的,结果也是一样的:

  pid_t cur = waitpid(-1, NULL, 0);//等待任意一个子进程
    if(cur > 0){
        printf("father wait:%d success\n", cur);
    }
    else{
        printf("father wait failed\n");
    }

status 参数🤔

int* status 是一种输出型参数,其中 status 是由32个比特位构成的一个整数,目前阶段我们只使用低16个位来表示进程退出的结果,正常终止的话 8 比特位保存退出状态,7 比特位保存退出码 0;而如果是被信号 kill 了,他就会大有不同,在未使用空间后面的结尾空间用于保存终止信号,在未使用空间和终止信号之间还有一个比特位大小的core dump 标志

wexitstatus,Linux,C++,linux,服务器,进程

因此要获取 status 很简单,我们用次低8位表示进程退出时的退出状态,也就是退出码;用低7位表示进程终止时所对应的信号,我们想要拿到这个退出码和信号的值,只要拿到低 16 个比特位中的次低 8 位和低 7 位就可以了。

status >> 8 & 0xFF;//次低8位,退出码
status & 0x7F;//低7位,退出信号

注意不能简单理解 status 为一个单纯的整数,status 结果和子进程的退出方式强相关,因此具体情况具体分析

退出码与退出信号🤔

我们就用上面的代码就可以测试出一段代码的退出码和退出信号:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main()
{
    pid_t id = fork();
    if(id == 0){
        int ret = 3;
        while(ret){
            printf("child[%d] is running:ret is %d\n", getpid(), ret);
            ret--;
            sleep(1);
        }
        exit(10);
    }
    printf("father wait begin..\n");
    int status = 0;
    pid_t cur = waitpid(id, &status, 0);
    if(cur > 0){
        printf("father wait:%d success, status exit_code:%d, status exit_signal:%d\n", cur, (status >> 8)& 0xFF, status & 0x7F);
    }
    else
        printf("father wait failed\n");
    }

wexitstatus,Linux,C++,linux,服务器,进程

那么进程退出时先看退出码还是退出信号?

退出码对应了进程退出的两种情况:

  1. 代码跑完且结果正确;
  2. 代码跑完但结果不正确;

进程信号代表的是进程跑完还是没跑完,因此一个程序要能跑完才有资格谈退出码,进程异常只关心退出信号。

其实 Linux 也给我们提供一些宏供我们直接使用,懒得去敲神魔位运算:

WIFEXITTED(status):正常终止子进程返回的状态为真,用于查看是否正常退出
WEXITSTAUS(status):WIFEXITTED 非零时提取子进程退出码

aqa 芭蕾 eqe 亏内,代表着开心代表着快乐,ok 了家人们。文章来源地址https://www.toymoban.com/news/detail-812362.html

到了这里,关于深剖 Linux 进程控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux网络编程:多进程 多线程_并发服务器

    文章目录: 一:wrap常用函数封装 wrap.h  wrap.c server.c封装实现 client.c封装实现 二:多进程process并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 三:多线程thread并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 ​​​​   read 函数的返回值 wrap.h  wrap

    2024年02月12日
    浏览(44)
  • 蓝易云:Linux系统sshd命令 – openssh服务器守护进程

    sshd是Linux系统中的一个守护进程,它提供了远程登录服务和安全的文件传输功能。以下是sshd的详细教程。 1. 安装openssh-server 在Debian/Ubuntu上安装openssh-server命令如下: sudo apt-get update sudo apt-get install openssh-server 在CentOS/RHEL上安装openssh-server命令如下: sudo yum update sudo yum install

    2024年02月16日
    浏览(36)
  • Linux高并发服务器开发---笔记1(环境搭建、系统编程、多进程)

    0613 首先这整个系列笔记属于笔记①:牛客校招冲刺集训营—C++工程师中的 第四章 笔记。 视频课链接: 视频1:Linux高并发服务器开发(40h); 视频2:第4章 项目制作与技能提升(录播)(26h30min); 视频课3: 第5章 高频考点与真题精讲(录播)中的 5.10-5.13 项目回顾 有个学

    2024年02月15日
    浏览(45)
  • linux 服务器进程、端口查找,nginx 配置日志查找,lsof 命令详解

    1.1 使用查看端口号对应的进程信息 方式一 : 使用netstat命令 -t:显示TCP连接 -u:显示UDP连接 -l:仅显示监听状态的连接 -n:以数字形式显示端口号,而不是以服务名称显示 通过管道符号|将netstat的输出结果传递给grep命令,用于过滤出包含指定端口号的行。 执行命令后,终端

    2024年02月04日
    浏览(47)
  • Linux 常用操作命令(CentOS 7.0)- 故障定位:服务器负载、进程管理、日志分析

    系统经研发测试上线后,如果运行期间出现了BUG,需要对服务故障进行定位,一般会查看服务器负载、服务状态、进程管理、服务日志等。 本文以CentOS 7.0 操作系统上的命令操作作为示例进行记录。 #服务器负载 完整参见:http://www.laobingbiji.com/note/detail.html?note_id=20231115154337

    2024年01月17日
    浏览(52)
  • Linux中 socket编程中多进程/多线程TCP并发服务器模型

    一次只能处理一个客户端的请求,等这个客户端退出后,才能处理下一个客户端。 缺点:循环服务器所处理的客户端不能有耗时操作。 模型 源码 可以同时处理多个客户端请求 父进程 / 主线程专门用于负责连接,创建子进程 / 分支线程用来与客户端交互。 模型 源码 模型 源

    2024年02月12日
    浏览(31)
  • linux服务器远程控制安全配置

    一、设置linux服务器用户登录错误次数锁定配置         为防止遭受恶意暴力破解,设置账户登录尝试次数并进行锁定,有效保护账户的安全。         1、登录失败处理功能策略(服务器终端)                 注:用户锁定期间,无论在输入正确还是错误的密码

    2024年04月12日
    浏览(31)
  • [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单进程、多进程、多线程、线程池tcp服务器

    网络的上一篇文章, 我们介绍了网络变成的一些重要的概念, 以及 UDP套接字的编程演示. 还实现了一个简单更简陋的UDP公共聊天室. [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现…

    2024年02月16日
    浏览(52)
  • 【linux】服务器CPU占用50%,top/htop/ps却看不到异常进程?使用unhide可以查看!

    htop发现前32个核全被占满了,但是却找不到对应进程号 sysdig -c topprocs_cpu 发现CPU占用3143.28%,因为是32核,平均每核就是接近100% sysdig -c topprocs_cpu ,可以发现病毒进程号 检查 /etc/sysctl.conf 如果该文件只有一行 fs.file-max = =2097152 则大概率被隐藏 文件 / 进程 类病毒感染,注释掉

    2024年02月03日
    浏览(41)
  • Linux服务器上EMQX Dashboard控制台登录密码忘了

    1.登录阿里云服务器管理控制台,[ 远程连接 ] 接入阿里Linux服务器;  2.输入以下代码 cd /usr/lib/emqx ,表示去到库目录emqx下 ./bin/emqx_ctl admins, 回车会出现 第一条命令添加用户(输入新用户名+新密码),第二条命令重置密码(原用户名+新密码),第三条命令删除用户(删除

    2024年02月15日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包