当前位置: 首页 > news >正文

linux学习第19、20天(父子进程)

ps ajx -->查看pid,ppid,gid,sid

父子进程

父子进程相同:

                刚fork后,data段、text段、堆,栈、环境变量、全局变量、进程工作目录位置、信号处理方式

父子进程不同:

                进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集

父子进程共享:

                读时共享、写时复制——全局变量

                1:文件描述符

                2:mmap映射区

gdb调试

注意要在fork前调试

设置父进程调式:set follow-fork-mode parent(默认也是)

设置子进程调试:set follow-fork-mode child

exec函数族

fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变,换核不换壳。

其实有六种以exec开头的函数,统称exec函数:

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ..., char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

execlp函数

加载一个进程,借助PATH环境变量      

int execlp(const char *file, const char *arg, ...);              成功:无返回;失败:-1

    参数1:要加载的程序的名字。该函数需要配合PATH环境变量来使用,当PATH中所有目录搜索后没有参数1则出错返回。

    该函数通常用来调用系统程序。如:ls、date、cp、cat等命令。

可以看到,程序执行了ls -lh功能

execl函数

加载一个进程, 通过 路径+程序名 来加载。

    int execl(const char *path, const char *arg, ...);             成功:无返回;失败:-1

对比execlp,如加载"ls"命令带有-l,-F参数

execlp("ls", "ls", "-l", "-F", NULL);        使用程序名在PATH中搜索。

execl("/bin/ls", "ls", "-l", "-F", NULL);    使用参数1给出的绝对路径搜索。

用execl也能执行ls这些,把路径给出来就行,但是这样麻烦,所以对于系统指令一般还是用execlp

execvp函数

孤儿进程和僵尸进程

孤儿进程

        孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

僵尸进程

僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

注意:

        kill 对僵尸进程无效,只能杀死父进程结束僵尸进程。这里要注意,每个进程结束后都必然会经历僵尸态,时间长短的差别而已。

        子进程终止时,子进程残留资源PCB存放于内核中,PCB记录了进程结束原因,进程回收就是回收PCB。

wait回收子进程

wait函数

        一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

 pid_t wait(int *status);      回收子进程退出资源,阻塞回收任意一个

       返回值:

                成功:清理掉的子进程pid;

                失败:-1 (没有子进程)

                status是传出参数

该函数有三个功能:

                ① 阻塞等待子进程退出

                ② 回收子进程残留pcb资源

                ③ 获取子进程结束状态(退出原因)。

        当进程终止时,操作系统的隐式回收机制会:

        1.关闭所有文件描述符

        2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)

        可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

         1.  WIFEXITED(status) 为非0    → 进程正常结束

                WEXITSTATUS(status) 上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)

         2. WIFSIGNALED(status) 为非0 → 进程异常终止

                WTERMSIG(status) 上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

        *3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态

                WSTOPSIG(status) 上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

                WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

获取子进程退出值和异常终止信号

正常情况

信号终止情况

waitpid函数

        pid_t waitpid(pid_t pid, int *status, int optains); 

        指定回收某一个进程,可以设置非阻塞

        参数:

                pid:指定回收的子进程pid

                        >0:待回收的的子进程pid

                        -1:任意子进程

                        0:同组子进程

                status:(传出)回收进程的状态

                optains:WNOHANG指定回收方式为非阻塞

        返回值:

                >0:表成功回收的子进程pid

                0:函数调用时,参3指定了WNOHANG,并且,没有子进程结束

                -1:失败,error

总结:

        wait、waitpid:一次调用,只回收一个子进程。

                                想回收多个。用while

例子(错误版)(这是因为,子进程里的pid和父进程里的pid不是一个pid,父进程里的pid还是0)

  1. //指定回收一个子进程错误示例  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <unistd.h>  
  6. #include <sys/wait.h>  
  7. #include <pthread.h>  
  8.   
  9. int main(int argc, char *argv[])  
  10. {  
  11.     int i;  
  12.     pid_t pid, wpid;  
  13.   
  14.     for (i = 0; i < 5; i++) {         
  15.         if (fork() == 0) {       // 循环期间子进程不 fork   
  16.             if (i == 2) {  
  17.                  pid = getpid();  
  18.                  printf("------pid = %d\n", pid);  
  19.             }  
  20.             break;  
  21.         }  
  22.     }  
  23.   
  24.     if (5 == i) {       // 父进程 表达式 2 跳出  
  25.         sleep(5);  
  26.   
  27.         //wait(NULL);                           // 一次wait/waitpid函数调用,只能回收一个子进程.  
  28.         //wpid = waitpid(-1, NULL, WNOHANG);    //回收任意子进程,没有结束的子进程,父进程直接返回  
  29.         //wpid = waitpid(pid, NULL, WNOHANG);   //指定一个进程回收  
  30.           
  31.         printf("------in parent , before waitpid, pid= %d\n", pid);  
  32.         wpid = waitpid(pid, NULL, 0);  //指定一个进程回收  
  33.         if (wpid == -1) {  
  34.             perror("waitpid error");  
  35.             exit(1);  
  36.         }  
  37.         printf("I'm parent, wait a child finish : %d \n", wpid);  
  38.   
  39.     } else {            // 子进程 break 跳出  
  40.         sleep(i);  
  41.         printf("I'm %dth child, pid= %d\n", i+1, getpid());  
  42.     }  
  43.   
  44.     return 0;  
  45. }  

代码运行如下,可以看到,传到父进程里的pid是0,但是

例子(正确版

注意:一个wait或者waitpid一次只能清理一个子进程,清理多个用循环。

http://www.xdnf.cn/news/793549.html

相关文章:

  • 选择正确的电平转换解决方案
  • HertzBeat的告警规则如何配置?
  • Flowith,有一种Agent叫无限
  • MyBatis 深度解析:高效 Java 持久层框架实践指南(基于 3.5.10)
  • 黑马程序员TypeScript课程笔记—class篇
  • windows环境下Ubuntu系统怎么重置root密码
  • 鸿蒙5.0项目开发——横竖屏切换开发
  • 深入解析 Java 中的 synchronized:从使用到底层原理的全面详解
  • C++中锁和原子操作的区别及取舍
  • 楼宇自控系统联动暖通空调:解密建筑环境舒适度提升路径
  • 域自适应 (Domain Adaptation,DA)基础
  • JS对数据类型的检测
  • TitanIDE智算版:一键开启云端算法开发环境
  • Servlet 生命周期
  • 高性能MCU的MPU与Cache优化详解
  • 线性动态规划
  • 张雪峰为9岁女儿申请40个左右商标!
  • 超声波粒度仪市场报告:行业现状、竞争格局与未来趋势分析
  • 原子操作与非原子操作
  • RTOS,其高级使用
  • TypeScript中class的两种继承方式extends和implements的对比
  • HTML5新特性
  • DAY 20 奇异值SVD分解
  • ant-design-vue select 下拉框不好用解决
  • Nginx 的配置文件
  • GCC内存占用统计使用指南
  • 【Android】双指旋转手势
  • AI 驱动工业:应用场景、挑战与未来趋势
  • SP网络结构:现代密码学的核心设计
  • SAP是什么?SAP概述