【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 <stdio.h>
#include <unistd.h>

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;
}

2.进程终止

2.1退出码

int main()
{
    
    
	//...
	return 0;
}

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

#include<stdio.h>                                                                                                            
#include<string.h>                                                                                                           
                                                                                                                                                                                                                                                      
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;             
 }  
echo $? //查看进程退出码

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

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

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

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

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

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

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

#include<stdio.h>
#include<string.h>

int main()
{
    
    
    for(int i=0;i<200;++i)
    {
    
    
       printf("%d: %s\n",i,strerror(i));                                                                                                                         
    } 
     return 0;
 }

在这里插入图片描述
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 }

在这里插入图片描述

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 }

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

int main()
{
    
    
     printf("hello linux");
     sleep(2);
     exit(1);
}

在这里插入图片描述

int main()
{
    
    
     printf("hello linux");
     sleep(2);
     _exit(1);
}

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

猜你喜欢

转载自blog.csdn.net/fight_p/article/details/132852662