Linux复习 / 进程控制QA梳理

news/2024/11/29 8:56:06/

文章目录

    • 前言
    • Q&A
      • 进程终止
        • Q:exit和_exit的区别?
        • Q:内核是如何终止进程的?
      • 进程等待
        • Q:为什么要等待子进程?
        • Q:如何等待子进程(wait/waitpid的区别)?
      • 进程替换
        • Q:为什么要进程替换?
        • Q:进程替换的原理是什么?
        • Q:如何替换进程?
      • 环境变量
        • Q:为什么执行自己的程序需要"./",而执行系统指令不需要"./"?

前言

本篇博客梳理关于Linux进程控制相关Q&A,若读者也在复习这块知识,或者正在学习这块知识,可以通过这些Q&A检测自己的知识掌握情况。此外,思维导图已经更新至我的gitee,Q&A之外的体系梳理还请移步思维导图。

Q&A


进程终止

Q:exit和_exit的区别?

A:

  • _eixt是一个系统调用函数,可以在man的2号手册中找到,而exit是一个C语言库函数,是语言级别的调用,可以在man的3号手册中找到
  • _exit只进行内核级别的清理工作:关闭文件描述符、释放内存、发送信号。而exit进行内核+语言级别的清理工作:关闭打开的流、刷新缓冲区、删除临时文件。比如
    • _exit不清理语言缓冲区,进程退出后不会将其中的内容刷新出来
    • 而exit会清理语言缓冲区,进程退出后会将其中的内容刷新出来
  • exit在底层调用了_exit,在清理完语言级别的数据后,再清理系统级别的数据

Q:内核是如何终止进程的?

A:一般情况下,内核会释放进程的数据结构和占用的内存资源。对于频繁使用的对象,内核会假释放该对象,将其放入到一个内存池中,当相同对象再次创建时,从内存池中取出一个对象并重置它的数据,给新进程使用。这有些类似STL的空间配置器。


进程等待

Q:为什么要等待子进程?

A:子进程退出,处于僵尸态,若父进程不回收子进程,过多僵尸进程占用PID资源,可能导致系统无法创建新进程。同时,父进程回收子进程也是为了获取子进程的运行情况、退出码等信息。

Q:如何等待子进程(wait/waitpid的区别)?

A:使用wait/waitpid等待子进程。两者的函数原型

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

关于两者的输出型参数:status
image.png

  • 0 ~ 6:若进程被信号终止,该字段表示进程的退出信号。若没有被信号终止,该字段为全0。可以使用status & 0x7F获取该字段,或者使用EIFEXITED(status)判断进程是否正常退出
  • 7 :core dump标志位,进程运行出错时的调试开关
  • 8 ~ 15:若进程正常退出,该字段表示其退出码。可以使用(status >> 8) & 0xFF或者WEXITSTATUS获取该字段

关于两函数的区别:

  • wait默认阻塞,且等待的是任意子进程
  • waitpid的参数options为0表示阻塞,为WNOHANG表示非阻塞
    • 非阻塞状态下,如果子进程没有退出,waitpid返回0。有子进程退出返回其pid
    • 且pid参数可以指定需要等待的子进程pid,pid为-1表示等待任意子进程

进程替换

Q:为什么要进程替换?

A:fork创建子进程后,父子进程数据和代码共享,此时子进程执行的代码和父进程是相似的。如果我们想让子进程执行全新的代码,完成其他的功能,就需要用到进程替换。替换后的程序可以是任何语言编写的,这也是不同语言的程序进行耦合的方式。

Q:进程替换的原理是什么?

A:fork创建子进程之后,若子进程没有进行数据修改,那么父子进程共享同样的页表,其映射的数据和代码完全一样。当进程替换发生,操作系统从磁盘加载新程序到内存中,然后修改子进程的页表与虚拟地址空间,使页表映射新程序的物理空间,不再与父进程共享同样的空间。要注意的是:进程替换没有产生新的进程,因为操作系统只是将新程序加载到内存,并没有为其创建进程控制块(task_struct)。

也可以这么理解,没有发生进程替换时,子进程的数据修改会触发数据的写时拷贝。发生进程替换时,子进程直接触发了数据和代码的写时拷贝。一般情况下,子进程的代码区数据和父进程相同,不会触发写时拷贝。而进程替换就是一个特殊情况,它会触发代码区的写时拷贝。

Q:如何替换进程?

A:关于一个程序,操作系统要执行它就要知道:1.程序所在的位置(路径),2.需要执行的程序名与执行该程序需要携带的选项。所以替换一个程序时,同样需要告知操作系统这两个信息:where + what。通常,我们使用系统调用,exec系列函数进行进程替换。

关于exec系列函数:

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

execl的函数参数:

  • path:表示程序所在的路径,最好使用绝对路径
  • arg:表示要替换的程序名称
  • …: 可变参数列表,表示执行需要需要携带的选项,以NULL结尾
    而execv使用指针数组argv[]代替arg和可变参数列表,指针数组具体使用见下图
    image.png

execlp和execvp函数涉及到PATH环境变量,使用echo $PATH可以查看PATH环境变量存储的路径,如果要替换的程序路径在这些路径下,就不需要写完整的绝对路径,只要写存在PATH路径下的文件名即可。比如

execl("/usr/bin/pwd", "pwd", NULL);
execlp("pwd", "pwd", NULL);

execle和execvep可以携带环境变量,需要传入envp[]数组,具体的使用方式和argv[]数组一样,但环境变量的设置需要满足name = value的格式,比如
image.png

直接设置环境变量会覆盖子进程从父进程继承下来的环境变量,若想在继承的环境变量上添加变量,可以使用environ指针,该指针指向一个字符串数组,比如

extern char* environ[]; // 声明环境变量
execle("/usr/bin/pwd", "pwd", NULL, environ);

此时替换的进程就会继承父进程的环境变量。


环境变量

Q:为什么执行自己的程序需要"./“,而执行系统指令不需要”./"?

A:这个问题的本质是:为什么执行自己的程序需要带上路径,而执行系统指令只需要程序名?这是因为:当shell解析指令时,将指令分成指令名和选项之后会进行判断,如果指令名没有携带路径,shell会去PATH环境变量下存储的路径中查找该程序。PATH保存了多条路径,大多都是系统指令所在的路径,若将自己程序所在的路径添加到PATH中,执行该路径下的程序也不用携带路径名。但是这样会污染PATH环境变量。设置环境变量的方法

export PATH=$PATH:要添加的路径

不同路径间用":"分割,export会覆盖原来的环境变量,$PATH的值是原来的PATH值,若不添加这条语句,原来的PATH值会被覆盖,不推荐这么做。


http://www.ppmy.cn/news/39190.html

相关文章

基于模型预测控制(MPC)的微电网调度优化的研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Vue插槽理解

Vue插槽理解插槽插槽 slot又名插槽,vue内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口 插槽slot是子组件的一个模板标签元素,而这一个元素是否显示,以及怎么显示是由父组件决定的 slot分为三类:默…

Leetcode.559 N 叉树的最大深度

题目链接 Leetcode.559 N 叉树的最大深度 easy 题目描述 给定一个 N 叉树,找到其最大深度。 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。…

ubuntu的neo4j启动问题

一、权限 首先赋予改文件可执行权限,赋予完会从白色变成绿色。 cd neo4j/bin目录下 sudo chmod x neo4j 二、sudo找不到java的问题 java -version正常但是sudo java -version不正常。 要么将jdk的path加入到secure_path 要么干脆直接注释掉 sudo visudo #注意&#…

[c++17] --- [[nodiscard]]和[[maybe_unused]]

1 [[nodiscard]] 介绍和应用示例 [[nodiscard]] 是C++17引入的一个属性(Attribute),它用于向编译器提示一个函数的返回值应该被检查,避免其被忽略或误用。它可以被用于函数、结构体、类、枚举和 typedef 等声明上,表示如果函数返回值未被使用,或者结构体、类、枚举和 type…

【Python机器学习】——标准差

Python机器学习——标准差 文章目录 Python机器学习——标准差一、Python 标准差一、Python 标准差 什么是标准差? 标准差(Standard Deviation,又常称均方差)是一个数字,描述值的离散程度。 低标准偏差表示大多数数字接近均值(平均值)。 高标准偏差表示这些值分布在更宽…

判断月份天数

1 问题 定义一个方法,把输入的月份判断月份一共有几天。 2 方法 使用java.util.Scanner,手动输入月份 并定义这个变量是一个整数型。 import java.util.Scanner; public class years { public static void main(String[] args) { Scanner putn…

Solon v2.2.7 发布,支持 Java 20

Solon 是一个高效的 Java 应用开发框架:更快、更小、更简单。也是一个有自己接口标准规范的开放生态。 150来个生态插件,覆盖各种不同的应用开发场景: 相对于 Spring Boot 和 Spring Cloud 的项目: 启动快 5 ~ 10 倍…