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

2023-09-17 14:23:06

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都应该会刷新缓冲区。
所以缓冲区在用户空间,是用户级的缓冲区

更多推荐

开学季哪个牌子的电容笔好?ipad2023手写笔推荐

到底是用苹果原装的电容笔,还是用平替的电容笔,这要根据自己的需要来决定,比如经常用在画画上,可以选择苹果原装笔;如果你一天里用来写东西的时间多于用来画画的时间,那你就该考虑一下,买一支更便宜的平替电容笔了。小编整理了一些很实用的电容笔,大家可以参考下哦!一、西圣电容笔官网价格:169元充电方式:独立充电仓西圣虽然低调,

经典算法-----约瑟夫问题(C语言)

目录前言故事背景约瑟夫问题环形链表解决数组解决前言今天我们来玩一个有意思的题目,也就是约瑟夫问题,这个问题出自于欧洲中世纪的一个故事,下面我们就去通过编程的方式来解决这个有趣的问题,一起来看看吧!故事背景据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友

《cpolar内网穿透》外网SSH远程连接linux(CentOS)服务器

本次教程我们来实现如何在外公网环境下,SSH远程连接家里/公司的LinuxCentOS服务器,无需公网IP,也不需要设置路由器。视频教程[video(video-jrpesBrv-1680147672481)(type-csdn)(url-CSDN直播https://live-file.csdnimg.cn/relea

【Java和C++】什么是多态

2023年9月20日,周三上午今天写作业的时候遇到了多态,但我感觉自己多多态还是不够理解,于是又深入研究了一下。不过,我觉得我写的这篇文章还不够完整,比如说没提到什么时候适合使用多态,以后有空再写上吧。目录什么是多态Java方法重写(MethodOverriding)接口(Interface)抽象类(AbstractC

三、支持向量机算法(SVC,Support Vector Classification)(有监督学习)

支持向量机SupportVectorMachine,就是所谓的SVM,它指的是一系列的机器学习算法,根据解决问题的不同,分为SVC(分类)和SVR(回归)SVC,SupportVectorClassification,其本质也是支持向量机supportvector,只不过是用于分类classification任务SVR

【实战】Streamlit+ECharts绘制多图表可视化大屏,超级实用

实战项目背景介绍当我们提到数据可视化,常常会想到众多的工具和库,如Matplotlib、Seaborn甚至于D3.js等。但是,有一个特定的组合正在快速走红:Streamlit和ECharts。Streamlit,作为一个轻量级的Python工具,允许数据科学家和工程师轻而易举地创建交互式的web应用。而ECharts

网络安全(黑客)自学

前言首先我谈下对黑客&网络安全的认知,其实最重要的是兴趣热爱,不同于网络安全工程师,他们大都是培训机构培训出来的,具备的基本都是防御和白帽子技能,他们绝大多数的人看的是工资,他们是为了就业而学习,为了走捷径才去参加培训。而我进大厂主要是靠自学内推进来是,当时我才20岁,基于对脚本小子的热爱,我每次工作做完了,就天天抱着

vue如何实现多页面应用网页

Vue通常用于构建单页面应用(SPA),但是如果你需要构建多页面应用的话,也可以使用Vue。以下是实现多页面应用的一些建议:创建多个Vue实例为每个页面创建一个Vue实例,这样每个页面都可以拥有自己独立的Vue实例,它们之间不会相互影响。在每个页面上,你可以使用Vue组件来组织和管理该页面的所有逻辑。使用路由管理多页面

【吴恩达】prompt engineering(原则 迭代 文本概括 推断、订餐机器人)

简介Introduction基础的LLM训练的模型,问法国的首都什么,可能会将答案预测为“法国最大的城市是什么,法国的人口是多少”许多LLMs的研究和实践的动力正在指令调整的LLMs上。指令调整的LLMs已经被训练来遵循指令。因此,如果你问它,“法国的首都是什么?”,它更有可能输出“法国的首都是巴黎”。指令调整的LLM

设计模式-命令模式

“小度,小度,热死了请打开空调~”,“小度,小度,主人回家了~”,“小度,小度,播放一首炸雷~”,像小度类似的智能产品正在逐步改善我们的生活,只要我们一声令下,产品立马执行,毫不拖延。就如同老板的命令一样,系统需接收到命令后经过一系列分析后,会自动选择执行哪些命令,而发令者完全不关心内部是执行了哪些动作以及哪个具体方法

【校招VIP】排序算法之高级排序

考点介绍:在校招面试中,排序算法是经常被问到的。排序算法又比较多,很容易遗忘和混淆。有相当同学校招卡在排序的实现上,要么是核心代码实现不了,要么是实现方法串台。大厂的考察重点在快速排序等高级排序上。排序算法之高级排序-相关题目及解析内容可点击文章末尾链接查看!一、考点试题1.以下哪种不是非稳定排序算法A.归并排序B.快

热文推荐