【Linux】进程控制1——进程创建和进程终止

2024-06-14 20:20

本文主要是介绍【Linux】进程控制1——进程创建和进程终止,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.进程创建

1.1.再谈fork

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

#include <unistd.h>
pid_t fork(void);//pid_t为整形 

返回值:子进程中的fork()返回0,父进程中的fork()返回子进程的id (pid),出错时返回 -1 

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

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

 pid_t fork(void)

{

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

}

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

子进程的特点:

        1.将父进程的所有数据共享或拷贝 到了子进程中。(若子进程不对父进程的数据进行修改的话,父子进程的数据也是共享的。若子进程对父进程的数据进行修改时,会发生写时拷贝,将父进程的数据进行拷贝一份到子进程中)

写时拷贝 :(是一种延时申请技术,可以提高整机内存的使用率)

        2.子进程和父进程的所有代码共享

        3.由于 程序计数器 和   CPU中存储上下文数据的寄存器 原因,子进程虽然可以共享父进程的所有代码,但是在子进程中是从fork()创建子进程之后的代码开始执行的,fork()创建子进程之前的代码默认已经执行过,不会重复执行,所以子进程会执行子进程创建之后的代码。但是由于fork()进行返回值是在子进程创建之后进行返回的,所以子进程依然会执行fork()的返回值。

我们之前详细讲过fork函数,就不再讨论fork函数了

1.2.创建进程

使用fork函数创建子进程

2.进程退出 

2.1. 退出码

通常main函数最后都要有一句return 0,这些数字有什么意义吗?

int 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函数的返回值实际上是 进程的退出码,用于表示进程是否是正确返回。)

  • 第一种:若退出码是0,则表示进程正确返回,0:success。
  • 第二种:若退出码为非0,则表示进程不正确返回,并且每个退出码都对应一个报错信息,退出码:报错信息。
  • 意义:将返回值返回给上一进程,用于监控进程的退出状态。出错时方便定位错误。

 有人就说了退出码都是数字,我们怎么知道它们分别对应什么错误?

还好c语言自带了一个查询退出码的函数——strerror

#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. 此时代码运行完毕,结果正确 (这个时候main函数的返回值为0则为正确,0:success)
  2. 此时代码运行完毕,结果不正确(这个时候main函数的返回值非0则为不正确,返回值:报错信息)
  3. 此时代码异常终止(这个时候main函数的返回值不具有意义,此时应该去看退出信号)

 2.3.怎么退出进程

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

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

2.3.2.任意地方调用exit。

我们来使用一下 

#include<stdio.h>  
#include<string.h>
#include<unistd.h>
#include<stdlib.h>int AddtoTarget(int from, int end){int sum = 0;for (int i = from; i <= end; ++i){sum += i;}exit(12);// return sum;  }int main(){int ret = AddtoTarget(1, 100);if (ret == 5050)return 0;elsereturn 1;}

 2.3.3.任意地方调用_exit(了解)

 我们来使用一下

#include<stdio.h>  
#include<string.h>
#include<unistd.h>
#include<stdlib.h>int AddtoTarget(int from, int end){int sum = 0;for (int i = from; i <= end; ++i){sum += i;}_exit(12);// return sum;  }int main(){int ret = AddtoTarget(1, 100);if (ret == 5050)return 0;elsereturn 1;}

对比exit和_exit发现,都可以使进程再任意地方结束。

2.3.4.exit()和_exit()的区别 

我们先来看两段代码

#include<stdio.h>  
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{printf("hello linux");sleep(2);exit(1);
}

 

#include<stdio.h>  
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{printf("hello linux");sleep(2);_exit(1);
}

对比发现,exit会刷新缓存区,而_exit并不会刷新缓存区——说明exit可能会调用_exit。

事实上: 

exit()和_exit()的底层区别:

  1. _exit():_exit()是系统调用接口的函数。
  2. exit():而exit()是把_exit()封装在内,并且增加了其他的函数,共同组合成exit()——是一个库函数。

exit是库函数,_exit是系统调用。库函数在系统调用上面。

如果缓存区在内存,exit调用_exit去终止进程,exit/_exit都应该会刷新缓冲区。
所以缓冲区在用户空间,是用户级的缓冲区 

 2.3.5.异常退出

·ctrl + c,信号终止

这篇关于【Linux】进程控制1——进程创建和进程终止的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1061419

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n