本篇博客梳理关于Linux进程控制相关Q&A,若读者也在复习这块知识,或者正在学习这块知识,可以通过这些Q&A检测自己的知识掌握情况。此外,思维导图已经更新至我的gitee,Q&A之外的体系梳理还请移步思维导图。
A:
A:一般情况下,内核会释放进程的数据结构和占用的内存资源。对于频繁使用的对象,内核会假释放该对象,将其放入到一个内存池中,当相同对象再次创建时,从内存池中取出一个对象并重置它的数据,给新进程使用。这有些类似STL的空间配置器。
A:子进程退出,处于僵尸态,若父进程不回收子进程,过多僵尸进程占用PID资源,可能导致系统无法创建新进程。同时,父进程回收子进程也是为了获取子进程的运行情况、退出码等信息。
A:使用wait/waitpid等待子进程。两者的函数原型
pid_t wait(int* status);
pid_t waitpid(pid_t pid, int* status, int options);
关于两函数的区别:
A:fork创建子进程后,父子进程数据和代码共享,此时子进程执行的代码和父进程是相似的。如果我们想让子进程执行全新的代码,完成其他的功能,就需要用到进程替换。替换后的程序可以是任何语言编写的,这也是不同语言的程序进行耦合的方式。
A:fork创建子进程之后,若子进程没有进行数据修改,那么父子进程共享同样的页表,其映射的数据和代码完全一样。当进程替换发生,操作系统从磁盘加载新程序到内存中,然后修改子进程的页表与虚拟地址空间,使页表映射新程序的物理空间,不再与父进程共享同样的空间。要注意的是:进程替换没有产生新的进程,因为操作系统只是将新程序加载到内存,并没有为其创建进程控制块(task_struct)。
也可以这么理解,没有发生进程替换时,子进程的数据修改会触发数据的写时拷贝。发生进程替换时,子进程直接触发了数据和代码的写时拷贝。一般情况下,子进程的代码区数据和父进程相同,不会触发写时拷贝。而进程替换就是一个特殊情况,它会触发代码区的写时拷贝。
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的函数参数:
execlp和execvp函数涉及到PATH环境变量,使用echo $PATH可以查看PATH环境变量存储的路径,如果要替换的程序路径在这些路径下,就不需要写完整的绝对路径,只要写存在PATH路径下的文件名即可。比如
execl("/usr/bin/pwd", "pwd", NULL);
execlp("pwd", "pwd", NULL);
execle和execvep可以携带环境变量,需要传入envp[]数组,具体的使用方式和argv[]数组一样,但环境变量的设置需要满足name = value的格式,比如
直接设置环境变量会覆盖子进程从父进程继承下来的环境变量,若想在继承的环境变量上添加变量,可以使用environ指针,该指针指向一个字符串数组,比如
extern char* environ[]; // 声明环境变量
execle("/usr/bin/pwd", "pwd", NULL, environ);
此时替换的进程就会继承父进程的环境变量。
A:这个问题的本质是:为什么执行自己的程序需要带上路径,而执行系统指令只需要程序名?这是因为:当shell解析指令时,将指令分成指令名和选项之后会进行判断,如果指令名没有携带路径,shell会去PATH环境变量下存储的路径中查找该程序。PATH保存了多条路径,大多都是系统指令所在的路径,若将自己程序所在的路径添加到PATH中,执行该路径下的程序也不用携带路径名。但是这样会污染PATH环境变量。设置环境变量的方法
export PATH=$PATH:要添加的路径
不同路径间用":"分割,export会覆盖原来的环境变量,$PATH的值是原来的PATH值,若不添加这条语句,原来的PATH值会被覆盖,不推荐这么做。