• Linux学习之:进程的控制


    创建一个进程都创建了哪些内容

    1.进程的PCB块
    2.进程的虚拟地址空间mm_struct
    3.进程的页表
    这三个一起才算完成了进程的创建!

    为什么要有地址空间?(重要!)

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    fork()创建子进程

    进程调用fork,当控制转移到内核中的fork代码后,
    内核做:
    1.分配新的内存块和内核数据结构给子进程
    2.将父进程部分数据结构内容拷贝至子进程
    3.添加子进程到系统进程列表当中
    4.fork返回,开始调度器调度

    通常,父子代码共享,父子再不写入时,数据也是共享的

    fork()返回值

    子进程返回0,
    父进程返回的是子进程的pid

    写时拷贝

    通常,父子代码共享,父子再不写入时,数据也是共享的,当任意(是任意一方!)一方试图写入,便以写时拷贝的方式各自一份副本
    因此发生写时拷贝后表面上看父子进程还是指向同一个地址同一个空间,其实子进程的地址已经被页表映射到其他地方去了!!在这里插入图片描述

    fork常规用法

    1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
    2.一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

    fork调用失败的原因

    1.系统中有太多的进程
    2.实际用户的进程数超过了限制

    在这里插入图片描述

    进程终止

    进程退出场景

    代码运行完毕,结果正确
    代码运行完毕,结果不正确
    代码异常终止

    进程常见退出方法

    正常终止(可以通过 echo $? 查看进程退出码):
    1.从main返回
    2.调用exit
    3._exit

    在这里插入图片描述

    exit()与_exit()

    在这里插入图片描述

    进程等待

    进程等待必要性(重要!)

    首先需要了解进程退出OS做了些什么
    在这里插入图片描述
    在这里插入图片描述

    进程等待的方法

    在这里插入图片描述

    阻塞的本质

    在这里插入图片描述

    status状态码(重要!!!)

    在这里插入图片描述
    在这里插入图片描述

    宏观理解waitpid

    在这里插入图片描述

    进程程序替换(重要!)

    替换原理

    在这里插入图片描述
    在这里插入图片描述

    如何替换?

    在这里插入图片描述

    替换函数

    在这里插入图片描述
    在这里插入图片描述

    程序替换其实只有一个系统调用其他的都是进行封装过的

    在这里插入图片描述

    做一个简易的shell

    主要是为了理解我们在命令行创建的进程都是bash的子进程,bash也是利用程序替换的方法让作为子进程的我们去执行自己的代码(这层理解很重要!!!)

    在这里插入图片描述

    在这里插入图片描述

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define NUM 128
    #define CMD_NUM 64
    
    int main()
    {
        char command[NUM];
        for( ; ; ){
            char *argv[CMD_NUM] = { NULL };
            //1. 打印提示符
            command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
            printf("[who@myhostname mydir]# ");
            fflush(stdout);
            //2. 获取命令字符串
            fgets(command, NUM, stdin);
            command[strlen(command) - 1] = '\0'; //"ls\n\0"
            //printf("echo: %s\n", command);
    
            //"ls -a -b -c\0";
            //3. 解析命令字符串, char *argv[];
            //strtok();
            const char *sep = " ";
            argv[0] = strtok(command, sep);
            int i = 1;
            while(argv[i] = strtok(NULL, sep)){
                i++;
            }
    
            //4.检测命令是否是需要shell本身执行的,内建命令
            if(strcmp(argv[0], "cd") == 0){
                if(argv[1] != NULL) chdir(argv[1]);
                continue;
            }
    
            //5. 执行第三方命令
            if(fork() == 0){
                //child
                execvp(argv[0], argv);
                exit(1);
            }
    
            int status = 0;
            waitpid(-1, &status, 0);
            printf("exit code: %d\n", (status >> 8)&0xFF);
    
            //for(i=0; argv[i]; i++){
            //    printf("argv[%d]: %s\n", i, argv[i]);
            //}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
  • 相关阅读:
    方舟开服配置教程服务器怎么开
    WordPress主题开发( 七)之—— 模版文件继承规则
    学习C++第二十五课--using定义模板别名与显式指定模板参数笔记
    [Vue]路由守卫
    web前端-JavaScript中的call、apply和bind方法(改变this指向)
    ip netns网络空间使用
    SpringBoot HTTP接口实战-基础篇
    用vue styleguidist 写组件文档
    第五章、实现Web适配器
    MySQL索引
  • 原文地址:https://blog.csdn.net/jiaao666/article/details/126354666