【Linux进程篇】Linux进程管理——进程创建与终止

W...Y的主页 😊

代码仓库分享💕


目录

 进程创建

fork函数初识

写时拷贝

fork常规用法

fork调用失败的原因

进程终止

进程退出场景

_exit函数

exit函数

return退出


 

 进程创建

fork函数初识

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1

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

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

 当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的进程,看如下程序:

int main( void )
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
printf("After:pid is %d, fork return %d\n", getpid(), pid);
sleep(1);
return 0;
}
运行结果:
[root@localhost linux]# ./a.out
Before: pid is 43676
After:pid is 43676, fork return 43677
After:pid is 43677, fork return 0

这里看到了三行输出,一行before,两行after。进程43676先打印before消息,然后它有打印after。另一个after消息有43677打印的。注意到进程43677没有打印before,为什么呢?如下图所示

所以,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。 

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

上面的fork函数我在前面的博客中已经提到过了,在这里就算一个复习。

写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。这不得不提到虚拟地址和物理地址映射关系了。

当父进程中创建一个子进程时,子进程会拷贝父进程的代码与数据,然后由页表进行映射,映射的位置是相同的。当我们修改子进程中的数据时,操作系统会在物理内存中写时拷贝出一个新空间并重新建立页表映射。

为什么要写时拷贝呢?因为我们说过物理内存是有限的,当数据存入物理内存时,操作系统并不知道你是否要对数据进行修改,所以为了提高利用率,父子进程就会指向同一个物理空间。并且可以提高fork函数的效率。

写时拷贝?为什么要拷贝呢?直接开辟一块一样大的空间不就好了!!!因为我们对数据无非就是增删查改,不一定对数据有大的改动,所以当我们对数据进行操作时,我们进行拷贝后就可以知道这个数据,方便我们进行操作。 

如何做到写时拷贝呢?

我们看到上面这张图,这是一个父子进程的页表,里面存着虚拟内存与物理内存的映射。但是我们发现数据与代码的权限都是只读,代码不可修改我们可以理解,为什么数据我们也不能修改呢?

不是不能修改,而是操作系统故意为之。当我们修改数据时,但是数据的权限为只读。这时就会发现缺页中断,操作系统就会出现进行判断发现时数据进行操作,这个操作是正常的,然后操作系统就会触发写时拷贝!!!

fork常规用法

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

fork调用失败的原因

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

进程终止

当我们在写程序时,好像固定在程序结尾要写一个return 0,为什么不能是return 1 或其他数字呢?main函数的返回值我们叫做退出码,一般0表示进程执行成功,非零表示失败。非零的数字都代表失败的退出的原因。

可以通过 echo $? 查看最近一次进程的退出码,当我们连续进行echo $? 前面是什么都无所谓后面都会变成0,因为当第二次使用echo $? 最近的进程就是其本身。

用户并不知道退出码的具体含义,所以要把错误码转换成错误描述,或者自定义进行。

怎么转换呢:

 我们可以使用程序查看一下:

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

 

我们可以看出来系统给出的错误码有133个,我只截取了首尾,其余自行查看。

我们也可以自己进行枚举:

enum {success=0,open_err,malloc_err};const char* errorToDesc(int code){switch(code){case success:return "success";case open_err:return "file open error";case malloc_err:return "malloc error";default:return "unknown error";}

但是我们函数也是有返回值的,当我们在函数后写一个return 0,返回后主函数也是会执行的。函数退出我们怎么知道函数的执行情况呢?函数程序被我们称为子程序,主程序有退出码作为退出反馈,函数也有退出码。

进程退出场景

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

代码没有执行完,进程出异常是进程收到了异常信号!!!异常信号我们Linux中也有具体实例:

 我们使用kill -l可以查看各种异常信号,每个信号有不同的信号编号,不同的信号编号表明异常原因。

所以任何进程最终执行的情况我们可以使用两个数字表明具体的执行情况:

signumberexit_code情况
00正常退出
!00退出码无意义
0!0代码跑完,结果不正确
!0!0退出码无意义

_exit函数

 这个函数是系统调用接口,因为他存在man(2)中。_exit与exit的调用方式一模一样。

exit函数

这个函数不是程序退出,而是进程终止。status是进程退出时的退出码。在我们代码的任何位置使用exit函数都表示进程退出。 

那exit与_exit有什么区别呢?

 

int main()
{
printf("hello");
exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{
printf("hello");
_exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#

exit函数会支持刷新缓冲区,反之_exit不会刷新缓冲区。其实就是_exit是操作系统为用户提供的系统调用接口,而exit是C语言将_exit进行封装的,为了更好的跨平台性!!!

return退出

eturn是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。


以上就是本次的全部内容,感谢大家观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/335701.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python Selenium 详解:实现高效的UI自动化测试

落日余辉&#xff0c;深情不及久伴。大家好&#xff0c;在当今软件开发的世界中&#xff0c;自动化测试已经成为保障软件质量和快速迭代的重要环节。而在自动化测试的领域中&#xff0c;UI自动化测试是不可或缺的一部分&#xff0c;它可以帮助测试团队快速验证用户界面的正确性…

huggingface的self.state与self.control来源(TrainerState与TrainerControl)

文章目录 前言一、huggingface的trainer的self.state与self.control初始化调用二、TrainerState源码解读(self.state)1、huggingface中self.state初始化参数2、TrainerState类的Demo 三、TrainerControl源码解读(self.control)总结 前言 在 Hugging Face 中&#xff0c;self.s…

Kong网关的负载均衡

安装java环境 查询 java安装包 196 yum list java* 安装java8197 yum install -y java-1.8.0-openjdk.x86_64 检验java8是否安装成功。198 java -version2个tomcat准备 另外一个tomcat区别在于&#xff1a;配置文件。conf/server.xml 启动tomcat [rootlocalhost bin]# ./…

JDBC使用步骤-小白入门

一.JDBC开发流程 加载并注册JDBC驱动创建数据库连接创建Statement对象遍历查询结果关闭连接,释放资源 import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement;public class StandardJDBCSample {public static …

【busybox记录】【shell指令】unlink

目录 内容来源&#xff1a; 【GUN】【unlink】指令介绍 【busybox】【unlink】指令介绍 【linux】【unlink】指令介绍 使用示例&#xff1a; 删除文件 - 默认 常用组合指令&#xff1a; 指令不常用/组合用法还需继续挖掘&#xff1a; 内容来源&#xff1a; GUN &#x…

element-ui 实现输入框下拉树组件(2024-05-23)

用element-ui的 el-input&#xff0c;el-tree&#xff0c;el-popover组件组合封装 import url("//unpkg.com/element-ui2.15.14/lib/theme-chalk/index.css"); <script src"//unpkg.com/vue2/dist/vue.js"></script> <script src"//…

单链表经典算法题理解

目录 1. 前言&#xff1a; 2. 移除链表元素 3. 反转链表 4. 合并两个有序链表 5. 链表的中间节点 6. 环形链表的约瑟夫问题 7. 分割链表 1. 前言&#xff1a; 当我们学习了单链表之后&#xff0c;我能可以尝试的刷一下题了&#xff0c;以下分享一下几道题的解法 2. 移…

HR招聘面试测评,哪些工作岗位需要测评创新能力?

什么是创新能力&#xff1f; 创新能力指在现有的物质基础上&#xff0c;通过某些特定的条件&#xff0c;促成满足未来社会发展的新事物。无论是个人还是国家都需要巨大的创新能力&#xff0c;因为创新是一切发展的根基&#xff0c;离开了创新&#xff0c;所有的发展都是原地踏…

串口通信问题排查总结

串口通信问题排查 排查原则&#xff1a; 软件从发送处理到接收处理&#xff0c;核查驱动、控制及发送接收数据是否正常。硬件从发送到接收&#xff0c;针对信号经过的各段&#xff0c;分段核对信号是否正常。示波器、逻辑分析仪。用万用表、示波器、逻辑分析仪等工具&#xf…

23种设计模式之一— — — —装饰模式详细介绍与讲解

装饰模式详细讲解 一、定义二、装饰模式结构核心思想模式角色模式的UML类图应用场景模式优点模式缺点 实例演示图示代码演示运行结果 一、定义 装饰模式&#xff08;别名&#xff1a;包装器&#xff09; 装饰模式&#xff08;Decorator Pattern&#xff09;是结构型的设计模式…

U盘无法打开?数据恢复与预防措施全解析

在日常生活和工作中&#xff0c;U盘已成为我们存储和传输数据的重要工具。然而&#xff0c;有时我们会遇到U盘无法打开的情况&#xff0c;这无疑给我们带来了诸多不便。本文将深入探讨U盘打不开的现象、原因及解决方案&#xff0c;并分享如何预防此类问题的发生。 一、U盘无法访…

软件架构设计属性之三:结构性属性浅析

文章目录 引言一、结构性属性的定义二、结构性属性的关键要素1. 组件化2. 模块化3. 层次化4. 接口定义5. 数据流6. 依赖管理 三、结构性属性的设计原则1. 高内聚低耦合2. 松耦合3. 清晰的接口4. 可维护性5. 可扩展性 四、结构性属性的实现策略1. 组件划分2. 模块化设计3. 接口设…

数据结构(1):线性表

1 线性表的顺序实现 创建的新项目是cpp类型哦&#xff01; 1.1 初始化 1.1.1 静态分配 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #define MaxSize 10 //定义顺序表的长度 typedef struct {int data[MaxSize];//用静态的数组存放元素&#xff01;int lengt…

目标检测 | R-CNN、Fast R-CNN与Faster R-CNN理论讲解

☀️教程&#xff1a;霹雳吧啦Wz ☀️链接&#xff1a;https://www.bilibili.com/video/BV1af4y1m7iL?p1&vd_sourcec7e390079ff3e10b79e23fb333bea49d 一、R-CNN R-CNN&#xff08;Region with CNN feature&#xff09;是由Ross Girshick在2014年提出的&#xff0c;在PAS…

基于文本来推荐相似酒店

基于文本来推荐相似酒店 查看数据集基本信息 import pandas as pd import numpy as np from nltk.corpus import stopwords from sklearn.metrics.pairwise import linear_kernel from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extrac…

蓝桥杯-AB路线(详细原创)

问题描述&#xff1a; 有一个由 N M 个方格组成的迷宫&#xff0c;每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格&#xff0c;目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。 由于特殊的原因&#xff0c;小蓝的路线必须先走 K 个 A 格子、再…

java高级——Collection集合之List探索(包含ArrayList、LinkedList、Vector底层实现及区别,非常详细哦)

java高级——Collection集合之List探索 前情提要文章介绍提前了解的知识点1. 数组2. 单向链表3. 双向链表4. 为什么单向链表使用的较多5. 线程安全和线程不安全的概念 ArrayList介绍1. 继承结构解析1.1 三个标志性接口1.2 AbstractList和AbstractCollection 2. ArrayList底层代…

刷爆leetcode第六期

题目一 用队列实现栈 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶。 int pop() 移除…

MVC架构中的servlet层重定向404小坑

servlet层中的UserLoginServlet.java package com.mhys.servlet; /*** ClassName: ${NAME}* Description:** Author 数开_11* Create 2024-05-29 20:32* Version 1.0*/import com.mhys.pojo.User; import com.mhys.service.UserService; import com.mhys.service.impl.UserSer…

Paddle使用问题No module named ‘paddle.fluid’

这是Paddle版本的问题&#xff0c;从飞桨框架 2.5 版本开始&#xff0c;已经废弃了 paddle.fluid 。 ​解决方案&#xff1a;修改paddle版本 pip install paddlepaddle2.4.0