Unix中的进程和线程-1

目录

1.如何创建一个进程

2.如何终止进程 

2.2遗言函数

3.进程资源的回收

4.孤儿进程和僵尸进程 

孤儿进程 (Orphan Process):

僵尸进程 (Zombie Process):

代码示例:

5. 进程映像的更新


在Linux中,进程和线程是操作系统进行工作调度的基本单位,它们各自拥有特定的属性和作业环境。 进程是指在系统中正在运行的一个应用程序。每个进程间都有各自独立的内存空间,即使是同一个程序的不同实例也不会共享这些信息。比如说,你可以打开多个计算机浏览器,它们都是独立的进程。 每个进程至少有一个线程,这个线程被称为主线程。线程是进程中的一个单独的序列流,是进程的实际操作单位。线程在同一进程的上下文中运行,并共享同一进程的命名空间,这意味着它们共享同样的代码段、数据段和开放的文件资源等。 关于进程和线程之间的关系,你可以将进程看作是土地,而线程则像是在土地上进行工作的人。每块土地(进程)上可以有一个或多个工人(线程),他们共享土地资源(进程的内存空间和资源),但各自进行不同的工作。 总结一下,进程提供运行环境的上下文,而线程则在这个上下文中实际执行任务。线程的切换比进程的切换开销更小,因此使用多线程可以在同一进程内同时执行多项任务,提高程序的效率。

1.如何创建一个进程

 在unix中我们使用fork函数来创建当前进程下的子进程:

  fork() 是一个非常重要的系统调用。它被用来创建新的进程。这个新的进程被称为子进程,而创建子进程的进程被称为父进程。 fork() 函数的独特之处在于,它只被调用一次,但却返回两次。它在父进程中返回新创建子进程的进程 ID,而在子进程中则返回 0。如果在调用 fork() 期间发生错误,例如无法获取到必要的内核资源,那么就会返回一个负数。 当 fork() 创建子进程时,子进程是父进程的一个复制品。它会获得父进程的数据空间、堆和栈的复制品。这意味着父进程中的数据和状态将会被复制到子进程中。然而,父进程和子进程的执行环境是独立的,他们有各自独立的进程ID,要传递信息需要使用特定的进程间通信(IPC)机制。 但是,值得注意的是,虽然子进程复制了父进程的上下文,但一些值,如一些系统资源(例如打开的文件描述符)、进程标识符(PID)、父进程标识符(PPID)等会被改变。 命名为 fork() 的函数名代表着“分叉”的意思,恰当的描述了创建新进程的行为,即从一个进程分出一个新的进程,这两个进程之后将会独立执行。

    (查看手册)

使用例子:

#include<t_stdio.h>
#include <unistd.h>
int main(){pid_t pid;//在父进程中创建一个子进程pid = fork();if(pid==-1)E_MSG("fork",-1);if(pid==0){//子进程执行的代码printf("child process..\n");}else {//{父进程执行的代码printf("parent process ..\n");}//父进程和子进程都能执行的代码printf("all ok..\n");return 0;
}

2.如何终止进程 

我们常用return 和exit来终止一个进程:

 使用例子:当前进程会停在gechar()这,这时我们查看我们当前进程与父进程的关系:

#include <stdlib.h>
#include<stdio.h>
int main(void){getchar();exit(1);}

这里说明我们执行程序a.out的时候,其父进程是bash,我们使用exit(status)就会返回status给父进程:

2.2遗言函数

 

#include<t_stdio.h>
#include <stdlib.h>#include <unistd.h>
void bye(void){printf("bye bye..\n");return;}
void goodbye(void){printf("good bye..\n");return;
}
int main(void ){//向进程注册遗言函数atexit(bye);atexit(bye);atexit(goodbye);//创建子进程pid_t pid=fork();if(pid==-1)E_MSG("fork ",-1);getchar();return 0;
}

先是父进程的遗言函数,如何是子进程的遗言函数。

3.进程资源的回收

#include<t_stdio.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
int main(void ){//创建子进程int pid;pid = fork();if(pid==-1)E_MSG("fork .. ",-1);if(pid==0){//子进程执行代码printf("child process.. %d\n",getpid());exit(0);}else {//父进程执行代码//父进程阻塞等待回收子进程的资源wait(NULL);printf("parent process..\n");}return 0;
}

只有子进程被终止父进程才会终止:

同样还有另一个回收函数:

具体使用可以参见手册。

4.孤儿进程和僵尸进程 

在Linux中,孤儿进程和僵尸进程是两种不同的状态,它们都与进程的生命周期和父子进程关系相关。下面我会分别解释这两种状态,并且提供一个代码例子进行详细分析。

孤儿进程 (Orphan Process):

当一个父进程终止或者退出时,它的子进程可能还在运行。这时,子进程将被 init 进程(进程号为1)收养。收养后的子进程会成为 init 进程的孩子,成为孤儿进程。孤儿进程不再有父进程,因此由 init 进程负责对其进行处理。

僵尸进程 (Zombie Process):

当一个进程完成了执行,但其父进程还没有通过 wait() 或 waitpid() 系统调用来获取其终止状态时,该进程会进入僵尸状态。僵尸进程的进程控制块仍然存在,但是进程已经没有任何执行代码,也没有分配到 CPU 时间片。僵尸进程不占用内存空间,但会占用一定的进程号(PID)资源。

代码示例:

下面是一个简单的 C 语言示例代码,演示了孤儿进程和僵尸进程的情况:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main() {pid_t child_pid = fork();if (child_pid > 0) { // Parent processprintf("Parent process is running with PID: %d\n", getpid());sleep(5); // Sleep for a while to allow child process to become orphanprintf("Parent process is terminating\n");} else if (child_pid == 0) { // Child processprintf("Child process is running with PID: %d, Parent PID: %d\n", getpid(), getppid());sleep(10); // Sleep for a while to become zombieprintf("Child process is terminating\n");} else { // Error handlingperror("fork");exit(EXIT_FAILURE);}return 0;
}

在这个例子中,父进程创建一个子进程,然后父进程会休眠5秒后退出。在此之前,子进程还在运行,但父进程已经退出,因此子进程会成为孤儿进程。而子进程会休眠10秒后退出,这时它会成为僵尸进程,因为父进程没有处理子进程的退出状态。

要观察进程状态,你可以使用 ps 命令,例如 ps aux | grep <process_name>,其中 <process_name> 是你的程序的名称,或者使用 top 命令观察进程列表。

5. 进程映像的更新

      在Unix系统中,"进程映像更新"是一个与程序执行密切相关的概念。它通常指的是当一个进程调用 exec系列函数(例如: execv, execvp, execp, execl, execlp, execle等)时,它原本的程序映像(即内存中的代码和数据)会被新的程序映像所取代,这个过程就被称为"进程映像更新"。 一个典型的例子是通过 shell 运行命令。当你在 shell 中输入一个命令并且按下回车的时候,shell会创建一个子进程,并在这个子进程中调用 exec 来执行你输入的命令。而在 exec 执行完成后,这个子进程的映像就被替换为你输入命令的程序映像。

 进程映像更新与父子进程关系密切,因为往往在子进程中执行 exec() 系列函数来代替原有程序。对于 bash shell 命令或者其他需要新建进程来执行的程序,都是新建了子进程,然后在子进程中执行执行 exec() 系列函数来替换新的程序。 下面这个 C 语言例子就是一个典型的父子进程与 exec() 函数相结合的例子,展示了这种动态的父子进程交替和进程映像更新:

#include<t_stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void){//创建子进程pid_t pid=fork();if(pid==-1)E_MSG("fork",-1);if(pid==0){printf("child process..\n");//更新进程的映像execlp("ps","ps","-o","pid,ppid,pgrp,comm","NULL");//只有exec执行失败了才会执行以下代码perror("execlp");exit(0);}else{wait(NULL);}return 0;
}

这段代码首先创建了一个子进程,然后在子进程中替换原有进程映像以运行 ps 命令。父进程则等待子进程终止。 下面是我对各处代码的详细解释:

  • #include <t_stdio.h>:这是一种非标准的头文件,可能是一个特定环境的定制头文件。通常我们使用标准库 <stdio.h> 来进行输入输出操作。
  • #include <unistd.h>:这个头文件提供了对POSIX操作系统 API 的访问。包括fork(), exec()等函数。
  • #include <sys/types.h> 和 #include <sys/wait.h>:这两个头文件用于支持进程控制相关的数据类型和函数。
  • pid_t pid = fork();:使用 fork() 函数创建了一个新的子进程。
  • if(pid == -1)E_MSG(\"fork\",-1);:如果 fork() 函数返回 -1,表示创建失败,此时调用 E_MSG() 函数处理错误。
  • if(pid == 0){}:如果 fork() 函数返回值为 0,表示当前处于子进程环境。
  • execlp(\"ps\",\"ps\",\"-o\",\"pid,ppid,pgrp,comm\",\"NULL\");:调用 execlp() 函数在子进程中运行 ps 命令。参数 "ps" 是要执行的命令,接下来的参数是传递给 ps 命令的参数。
    • "ps":表示要运行的是 ps 命令,这个命令用于查看进程状态。
    • "-o":这个参数用于指定 ps 命令输出的格式。
    • "pid,ppid,pgrp,comm":这是 -o 参数后的具体格式,表示输出的是进程ID,父进程ID,进程组ID,以及命令名。
    • "NULL":这应该表示参数列表的结尾,但 "NULL" 这样的字符串并不会被 execlp() 函数识别为参数列表结束,通常我们使用 NULL 或者 (char*) 0 来表示参数列表的结束。
  • perror(\"execlp\");:如果 execlp() 函数调用失败,则会执行到这条语句,显示错误信息。
  • exit(0);:子进程结束运行,返回 0 表示正常退出。
  • wait(NULL);:这是在父进程中的语句,父进程调用 wait() 函数等待子进程结束。NULL 表示父进程不关心子进程的退出状态。 大致上,这个程序的作用就是创建一个子进程,然后在子进程中运行 ps -o pid,ppid,pgrp,comm 命令,输出相关的进程信息,之后子进程退出,父进程在子进程退出后也结束运行。

exec() 函数实际上是一系列相关函数的函数家族(通称 exec 函数族或者 exec 系列函数),包括 exec, execp, execv, execvp, execve, execvpe 这些函数。这些函数主要用于在进程中加载和运行新的程序,它会替换当前进程的映像为新的程序映像,新程序从 main 函数开始执行,直到 main 函数返回或者调用 exit 结束。 下面我将逐一解释这些函数及其参数:

  • exec(): exec 是其他函数的基础,但通常我们不直接使用它,而是使用它的一些封装版本。这个函数会替换当前进程映像为新的程序映像。
  • execv() 和 execvp(): 这两个函数接受两个参数。第一个参数是我们要运行的程序的路径。第二个参数是一个字符指针数组,该数组包含新程序的命令行参数,最后一个指针后面必须是一个 NULL 指针。这两个函数的区别在于, execvp() 会使用环境变量 PATH,去寻找我们要执行的程序。
  • execve() 和 execvpe(): 这两个函数比上面的函数多一个参数,即新程序的环境变量的列表。这个参数同样是一个字符指针数组,数组中的每个字符串都有 NAME=VALUE 的形式,最后一个指针后面必须是一个 NULL 指针。 execvpe() 同样会使用环境变量 PATH 去寻找程序。 总的来说, exec 系列函数用于在当前进程中启动新的程序,完全替代了原有的程序并重新开始执行。所以它通常用于实现程序间的切换。这些函数只有在出错时返回,如果正常执行,调用 exec 系列函数的代码之后的部分都不会被执行,因为已经被新程序替换了。如果出现错误,比如文件不存在或者权限不足等,那么就会返回 -1,并设置 errno 为相应的值来表示错误原因。

  

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

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

相关文章

CAS 的 ABA 问题

一、什么是 ABA 问题 ABA 的问题: 假设存在两个线程 t1 和 t2. 有⼀个共享变量 num, 初始值为 A. 接下来, 线程 t1 想使⽤ CAS 把 num 值改成 Z, 那么就需要 先读取 num 的值, 记录到 oldNum 变量中. 使⽤ CAS 判定当前 num 的值是否为 A, 如果为 A, 就修改成 Z. 但是, 在…

CaT论文翻译

CaT: Balanced Continual Graph Learning with Graph Condensation CaT&#xff1a;通过图压缩实现平衡的连续图学习 Abstract 持续图学习(CGL)的目的是通过以流方式输入图数据来持续更新图模型。由于模型在使用新数据进行训练时很容易忘记以前学到的知识&#xff0c;因此灾…

基于SSM的宠物医院信息管理系统

项目简介 主要功能包括首页、个人中心、用户管理、医学知识管理、科室信息管理、医生信息管理、订单信息管理等。 管理员模块:管理员登录进入宠物医院信息管理系统可以查看个人中心、用户管理、医生管理、医学知识管理、科室信息管理、医生信息管理、预约挂号管理、医嘱信息管理…

AtCoder Beginner Contest 342 A - D

A - Yay! 大意 给定字符串&#xff0c;其中有且仅有一个字符与其他不同&#xff0c;输出这个字符的下标&#xff08;从1开始&#xff09;。 思路 桶排序统计次数即可。 代码 #include<iostream> #include<vector> using namespace std; int main(){string s;…

【前端面试3+1】06继承方式及优缺点、缓存策略、url输入到渲染全过程、【二叉树中序遍历】

一、继承有哪些方式&#xff1f;以及优缺点 继承的方式包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承和组合式继承。 1.原型链继承&#xff1a; 实现方式&#xff1a;将子类的原型指向父类的实例来实现继承。优点&#xff1a;简单易懂&#xff0c;代码量少。…

如何制作伸缩侧边栏?

目录 一、html-body 二、CSS 三、JS 四、完整代码 五、效果展示 一、html-body 侧边栏的伸缩需要用户触发事件&#xff0c;这里使用button为例&#xff0c;用户点击按钮实现侧边栏的打开和关闭。 <body><!-- 按钮&#xff0c;可以用文字、图片等作为事件源&am…

十三.youyiku分析案例(数分)

目录 youyiku分析案例(数分) 大框操作 1.销售情况随着时间的变化 2.不同产品的销量 柱状图(bar)改颜色排序 seaborn库(柱) --排序 3.每个城市的人喜欢的购物方式 4-不同年龄段的购物方式 添加标签段及值 --创建新表 对标签分别计算 绘图分析 youyiku分析案例(数分…

session反序列化漏洞2——ctfshow web263

题目链接 https://ctf.show/challenges#web263-723 解题思路 进去先是一个登录框 目录扫描一下发现存在源码泄露 查看源码 index.php error_reporting(0);session_start();//超过5次禁止登陆if(isset($_SESSION[limit])){$_SESSION[limti]>5?die("登陆失败次数超…

智过网:中级安全工程师一年能挣多少钱?工资高吗?

中级安全工程师的年收入是广大从业者非常关心的问题。毕竟&#xff0c;了解自己所处行业的薪资水平&#xff0c;不仅有助于规划个人职业发展&#xff0c;还能为日常生活中的决策提供重要参考。那么&#xff0c;中级安全工程师一年究竟能挣多少钱呢&#xff1f;工资又是否算高呢…

[leetcode] 100. 相同的树

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true示例 2&a…

YOLOV5训练自己的数据集教程(万字整理,实现0-1)

文章目录 一、YOLOV5下载地址 二、版本及配置说明 三、初步测试 四、制作自己的数据集及转txt格式 1、数据集要求 2、下载labelme 3、安装依赖库 4、labelme操作 五、.json转txt、.xml转txt 六、修改配置文件 1、coco128.yaml->ddjc_parameter.yaml 2、yolov5x.…

第1章.提示词:开启AI智慧之门的钥匙

什么是提示词&#xff1f; 提示词&#xff0c;是引导语言模型的指令&#xff0c;让用户能够驾驭模型的输出&#xff0c;确保生成的文本符合需求。 ChatGPT&#xff0c;这位文字界的艺术大师&#xff0c;以transformer架构为基石&#xff0c;能轻松驾驭海量数据&#xff0c;编织…

嵌入式第一部分-第一集:ARM那些你得知道的事

ARM&#xff1a;Advanced RISC Machine&#xff0c;先进精简指令集机器 ARM公司只做设计&#xff0c;不生产。 国内IC生产厂商&#xff1a;华为海思、全志、瑞芯微、MTK&#xff08;联发科&#xff09; 扩展&#xff1a;ARM的商业模式了解。 使用三星S5PV210开发板进行视频的讲…

大数据 - Hadoop系列《五》- HDFS文件块大小及小文件问题

系列文章&#xff1a; 大数据- Hadoop入门-CSDN博客 大数据 - Hadoop系列《二》- Hadoop组成-CSDN博客 大数据 - Hadoop系列《三》- HDFS&#xff08;分布式文件系统&#xff09;概述_大量小文件的存储使用什么分布式文件系统-CSDN博客 大数据 - Hadoop系列《三》- MapRedu…

LeetCode算法——数组/字符串篇

对刷过的算法进行总结&#xff0c;所用解法都是最符合我个人逻辑的&#xff0c;以后再刷的话就看这篇帖子了 # 代码随想录——数组理论基础 首先要知道数组在内存中的存储方式&#xff0c;这样才能真正理解数组相关的面试题 数组是存放在连续内存空间上的相同类型数据的集合 …

IntelliJ IDEA 2023 for Mac 好用的Java开发工具

IntelliJ IDEA 2023是一款由JetBrains开发的强大的集成开发环境&#xff08;IDE&#xff09;软件&#xff0c;适用于多个编程语言。它旨在提高开发人员的生产力和代码质量&#xff0c;具有以下多种特色功能&#xff1a; 软件下载&#xff1a;IntelliJ IDEA 2023 v2023.3.6中文激…

2d导入人物素材进行分割后设置图层

1、设置分辨率大小 2、相机调整大小&#xff0c;要符合场景 3、选择2D sprite 编辑器 或者 点击这个也行 4、分割图像 5、设置过滤模式 6、图层设置

CAJViewer8.1下载地址及安装教程

CAJViewer是中国学术期刊&#xff08;CAJ&#xff09;全文数据库的专用阅读软件。CAJViewer是中国知识资源总库&#xff08;CNKI&#xff09;开发的一款软件&#xff0c;旨在方便用户在线阅读和下载CAJ数据库中的学术论文、期刊和会议论文等文献资源。 CAJViewer具有直观的界面…

Modbus协议介绍

Modbus存储区 从机存储数据&#xff0c;那么肯定要有一个存储区&#xff0c;那就需要文件操作&#xff0c;我们都知道这文件可以分为只读(-r)和读写(-wr)两种类型 并且存储的数据类型可以分为 &#xff1a;布尔量 和 16位寄存器 布尔量比如IO口的电平高低&#xff0c;灯的开关…

金仓方案 | 全国高速联网收费系统国产化升级进行时

近年来&#xff0c;国家提出了交通强国建设战略&#xff0c;鼓励在交通领域加强自主创新&#xff0c;推动产业转型升级。高速公路收费系统作为高速公路行业的核心&#xff0c;也是交通领域信息技术应用创新的重中之重&#xff0c;自主可控问题成为系统建设和运营的关键难点。 2…