• 【linux】进程创建,进程终止


    1.进程创建

    在前面创建子进程的时候就学过了fork函数,它能从已经存在进程中创建一个新进程,新进程为子进程,而原进程为父进程。

    当进程调用fork,当控制转移到内核中的fork代码后,内核做:

    分配新的内存块和内核数据结构给子进程
    将父进程部分数据结构内容拷贝至子进程
    添加子进程到系统进程列表当中
    fork返回,开始调度器调度

    在这里插入图片描述fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。

    1.1写时拷贝

    通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图
    在这里插入图片描述
    写时拷贝在进程地址空间详细讲述了,这里不再细说。

    这里主要讲解fork返回值的问题。

    1.如何理解fork返回之后,给父进程返回子进程的pid,给子进程返回0。

    父亲:孩子-----> 1:n的关系。
    一个父亲有一个或者n个孩子,如果想找到某个孩子,必须知道孩子的名字。
    孩子有且只有以恶搞父亲,想要找父亲的时候,有操作系统提供的getppid()。

    2.如何理解fork函数有两个返回值问题。

    在这里插入图片描述
    当一个函数准备return的时候,核心代码已经执行完了,所以子进程早已经创建好了,并且可能在OS运行队列中,准备被调度了。
    前面所说父子进程共享fork之后的代码,其实不准确,在fork函数之内父子进程就已经分流共享了,所以父子进程各自执行return。

    3.如何理解同一个id值,怎么可能保存两个不同的值。让if else同时执行

    pid_t id = fork();返回的本质就是写入,所以谁先返回,谁就先写入id,因为进程具有独立性,所以写时拷贝,因此同一个id,地址是一样,但是内容是不一样。

    1.2fork常规用法

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

    后面再程序替换具体讲exec函数。

    1.3fork调用失败的原因

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

    这是一段一直创建子进程的代码,等到不能创建子进程了就退出,会看见创建子进程个数。

    #include 
    #include 
    
    int main()
    {
        int cnt = 0;
        while(1)
        {
            int ret = fork();
            if(ret < 0){
                printf("fork error!, cnt: %d\n", cnt);
                break;
            }
            else if(ret == 0){
                //child
                while(1) sleep(1);
            }
            //partent
            cnt++;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.进程终止

    2.1退出码

    int main()
    {
    	//...
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通常main函数最后都要有一句return 0;其实这是进程退出的时候,对应的退出码,标定进程执行的结果是否正确。

    #include                                                                                                            
    #include                                                                                                           
                                                                                                                                                                                                                                                          
    int AddtoTarget(int from,int end)                                                                                            
    {                                                                                                                            
         int sum=0;                                                                                                               
         for(int i=from;i<end;++i)                                                                                                
         {                                                                                                                        
             sum+=i;                                                                                                              
         }                                                                                                                        
         return sum;                                                                                                              
     }                                                                                                                            
                                                                                                                                  
    int main()                                                                                                                   
    {                                                                                                                            
                                                                                                                                                                                                                                                     
         //写代码是为了完成某件事情,我们如何得知事情完成的怎么样呢?
         //进程退出码                                                                                                                                                 
         int num=AddtoTarget(1,100);
         if(num == 5050)       
             return 0;         
         else                                                                                                                       
             return 1;                                                                                                        
         return 0;             
     }  
    
    • 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
    echo $? //查看进程退出码
    
    • 1

    $? 该符号永远记录最近一个进程在命令行中执行完毕时对应的退出码。

    在这里插入图片描述
    这里下面显示0是怎么回事呢?

    这是因为echo 也是一个子进程,因此剩下的三个的是echo &?是一个echo子进程的退出码。

    既然知道退出码。那如何设定main函数的返回值呢?
    如果不关心进程退出码,return 0就行。
    如果未来我们要关心进程退出码的时候,要返回特定的数据表明特定的错误。

    退出码的意义: 0 标识success , !0 标识失败,!0具体是几,标识不同的错误。
    但是数字对人不友好,对计算机友好。

    所以一般而言,退出码都必须有对应退出码的文字描述
    1.可以自定义
    2.可以使用系统的映射关系

    C库给我们提供一个函数,查看进程退出码对应的信息
    在这里插入图片描述

    #include
    #include
    
    int main()
    {
        for(int i=0;i<200;++i)
        {
           printf("%d: %s\n",i,strerror(i));                                                                                                                         
        } 
         return 0;
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    strerror记录了对应的退出码的映射信息,总共135个,这里截取了一小部分。

    2.2进程退出场景

    1.代码跑完,结果正确--------return 0;
    2.代码跑完,结果不正确--------return !0;
    3.代码没跑完,程序异常了,退出码无意义;(比如野指针,越界等等)

    2.3进程如何退出

    1.main函数return返回,其他函数return是调用结束。

    2.任意地方调用exit。
    在这里插入图片描述

      1 #include<stdio.h>  
      2 #include<string.h>  
      3 #include<unistd.h>  
      4 #include<stdlib.h>
      5 int AddtoTarget(int from,int end)
      6 {
      7     int sum=0;
      8     for(int i=from;i<=end;++i)
      9     {
     10         sum+=i;                                                                                                                                                  
     11     }  
     12     exit(12);  
     13    // return sum;  
     14 }  
     15   
     16 int main()  
     17 {  
     18     int ret=AddtoTarget(1,100);  
     19     if(ret == 5050)  
     20         return 0;  
     21     else  
     22         return 1;  
     23 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    3._exit(了解)
    在这里插入图片描述

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<unistd.h>
      4 #include<stdlib.h>
      5 int AddtoTarget(int from,int end)
      6 {
      7     int sum=0;
      8     for(int i=from;i<=end;++i)
      9     {
     10         sum+=i;
     11     }
     12    // exit(12);
     13    _exit(12);                                                                                                                                                    
     14    // return sum;
     15 }
     16 
     17 int main()
     18 {
     19     int ret=AddtoTarget(1,100);
     20     if(ret == 5050)                                                                       
     21         return 0;                                    
     22     else                                   
     23         return 1; 
     24 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    对比exit和_exit发现,都可以使进程再任意地方结束。
    exit是库函数,_exit是系统调用。
    库函数在系统调用上面。(操作系统哪里画过图)
    再看一段代码

    int main()
    {
         printf("hello linux");
         sleep(2);
         exit(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    int main()
    {
         printf("hello linux");
         sleep(2);
         _exit(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    对比发现,exit会刷新缓存区,而_exit并不会刷新缓存区。
    这里要说明的是exit会调用_exit。
    在这里插入图片描述
    问缓冲区在哪?
    如果在内存,exit调用_exit去终止进程,exit/_exit都应该会刷新缓冲区。
    所以缓冲区在用户空间,是用户级的缓冲区

  • 相关阅读:
    【线性表的原地逆置】
    Kubernetes 节点异常检测
    DBCO-PEG-carboxyl COOH-PEG-DBCO 二苯并环辛炔-聚乙二醇-羧酸 羧酸修饰PEG二苯并环辛炔
    C++(Qt)软件调试---自动注册AeDebug(17)
    unity基础4-常用插件
    SVN使用
    CentOS7.9中使用packstack安装train版本
    【python】内置函数——isinstance()/issubclass()判断参数1是否是参数2类型
    java计算机毕业设计雁门关风景区宣传网站源程序+mysql+系统+lw文档+远程调试
    Shell判断:模式匹配:case(三)
  • 原文地址:https://blog.csdn.net/fight_p/article/details/132852662