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.rhkb.cn/news/293186.html

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

相关文章

保研线性代数机器学习基础复习2

1.什么是群&#xff08;Group&#xff09;&#xff1f; 对于一个集合 G 以及集合上的操作 &#xff0c;如果G G-> G&#xff0c;那么称&#xff08;G&#xff0c;&#xff09;为一个群&#xff0c;并且满足如下性质&#xff1a; 封闭性&#xff1a;结合性&#xff1a;中性…

从零开始的软件开发实战:互联网医院APP搭建详解

今天&#xff0c;笔者将以“从零开始的软件开发实战&#xff1a;互联网医院APP搭建详解”为主题&#xff0c;深入探讨互联网医院APP的开发过程和关键技术。 第一步&#xff1a;需求分析和规划 互联网医院APP的主要功能包括在线挂号、医生预约、医疗咨询、健康档案管理等。我们…

金融衍生品市场

金融衍生品市场 衍生金融品的作用衍生金融工具远期合约期货合约期权 衍生金融品的作用 套期保值&#xff08;Hedging&#xff09; 组合多头头寸(long position)与空头头寸(short position)例&#xff1a;股票与股指期货 投机 衍生金融工具 远期合约 定义&#xff1a;在将来…

论文笔记 - :MonoLSS: Learnable Sample Selection For Monocular 3D Detection

论文笔记✍MonoLSS: Learnable Sample Selection For Monocular 3D Detection &#x1f4dc; Abstract &#x1f528; 主流做法限制 &#xff1a; 以前的工作以启发式的方式使用特征来学习 3D 属性&#xff0c;没有考虑到不适当的特征可能会产生不利影响。 &#x1f528; 本…

Optimizer神经网络中各种优化器介绍

1. SGD 1.1 batch-GD 每次更新使用全部的样本&#xff0c;注意会对所有的样本取均值&#xff0c;这样每次更新的速度慢。计算量大。 1.2 SGD 每次随机取一个样本。这样更新速度更快。SGD算法在于每次只去拟合一个训练样本&#xff0c;这使得在梯度下降过程中不需去用所有训…

css3之3D转换transform

css3之3D转换 一.特点二.坐标系三.3D移动&#xff08;translate3d)1.概念2.透视&#xff08;perpective)(近大远小&#xff09;&#xff08;写在父盒子上&#xff09; 四.3D旋转&#xff08;rotate3d)1.概念2.左手准则3.呈现&#xff08;transfrom-style)&#xff08;写父级盒子…

智能革命:ChatGPT3.5与GPT4.0的融合,携手DALL·E 3和Midjourney开启艺术新纪元

迷图网(kk.zlrxjh.top)是一个融合了顶尖人工智能技术的多功能助手&#xff0c;集成了ChatGPT3.5、GPT4.0、DALLE 3和Midjourney等多种智能系统&#xff0c;为用户提供了丰富的体验。以下是对这些技术的概述&#xff1a; ChatGPT3.5是由OpenAI开发的一个自然语言处理模型&#x…

KeepAlived使用介绍

目录 1、Introduce 2、基本使用 &#xff08;1&#xff09;安装 &#xff08;2&#xff09;配置文件 &#xff08;3&#xff09;使用教程 1、Introduce keepalived是一个用于实现高可用性和负载均衡的开源软件。它提供了一种轻量级的方式来管理多个服务器&#xff0c;并确保…

使用Detours进行HOOK

文章目录 Detours介绍Detours配置Detours进行Sleep Hook Detours介绍 Detours是微软研究院开发的一款软件工具&#xff0c;用于Windows平台上的应用程序重定向和修改。 它可以在运行时修改应用程序的执行路径&#xff0c;允许开发人员注入自定义代码来改变应用程序的 行为&…

数据分析之Tebleau 的度量名称和度量值

度量名称 包含所有的维度 度量值 包含所有的度量 度量名称包含上面所有的维度&#xff0c;度量值包含上面所有的度量 当同时创建两个或两个以上度量或维度时&#xff0c;会自动创建度量名称和度量值 拖入省份为行(这会是还没有值的) 可以直接将销售金额拖到数值这里 或者将销售…

鸿蒙OS开发实例:【ArkTS类库多线程I/O密集型任务开发】

使用异步并发可以解决单次I/O任务阻塞的问题&#xff0c;但是如果遇到I/O密集型任务&#xff0c;同样会阻塞线程中其它任务的执行&#xff0c;这时需要使用多线程并发能力来进行解决。 I/O密集型任务的性能重点通常不在于CPU的处理能力&#xff0c;而在于I/O操作的速度和效率。…

上位机图像处理和嵌入式模块部署(qmacvisual寻找圆和寻找直线)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面有几篇文章&#xff0c;我们谈到过直线拟合、圆拟合和椭圆拟合。当时&#xff0c;我们的做法是&#xff0c;先找到了轮廓&#xff0c;接着找到…

this.$route.back()时的组件缓存

1.this.$route.back()回到上一个路径会重新加载 跳转时,前一个路由的内容会被销毁,当回来时,重新创建树,组件内有保存了距离,没有一开始是0. 2.keep-alive写在router-view上面,这个地方所代表的路由会被保存,因此可以写在上面,保存,当返回时,如果是这个路由,里面的内容是一样…

超强命令行解析工具Apache Commons CLI

概述 为什么要写这篇文章呢?因为在读flink cdc3.0源码的时候发现了这个工具包,感觉很牛,之前写过shell命令,shell是用getopts来处理命令行参数的,但是其实写起来很麻烦,长时间不写已经完全忘记了,现在才发现原来java也有这种工具类,所以先学习一下这个的使用,也许之后自己在写…

canvas画图,画矩形可拖拽移动,可拖拽更改尺寸大小

提示&#xff1a;canvas画图&#xff0c;画矩形&#xff0c;圆形&#xff0c;直线&#xff0c;曲线可拖拽移动 文章目录 前言一、画矩形&#xff0c;圆形&#xff0c;直线&#xff0c;曲线可拖拽移动总结 前言 一、画矩形&#xff0c;圆形&#xff0c;直线&#xff0c;曲线可拖…

AI绘画教程:Midjourney使用方法与技巧从入门到精通

文章目录 一、《AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通》二、内容介绍三、作者介绍&#x1f324;️粉丝福利 一、《AI绘画教程&#xff1a;Midjourney使用方法与技巧从入门到精通》 一本书读懂Midjourney绘画&#xff0c;让创意更简单&#xff0c;让设计…

yolov5 v7.0打包exe文件,使用C++调用

cd到yolo5文件夹下 pyinstaller -p 当前路径 -i logo图标 detect.py问题汇总 运行detect.exe找不到default.yaml 这个是yolov8里的文件 1 复制权重文件到exe所在目录。 2 根据报错提示的配置文件路径&#xff0c;把default.yaml复制放到相应的路径下。&#xff08;缺少相应…

redis和数据库数据不一直问题,缓存常见的三大问题

文章目录 数据一致性缓存常见问题缓存穿透缓存击穿缓存雪崩 数据一致性 1 思路 查询数据的时候&#xff0c;如果缓存未命中&#xff0c;则查询数据库&#xff0c;将数据写入缓存设置超时时间修改数据时&#xff0c;先修改数据库&#xff0c;在删除缓存。 2 代码实现 修改更…

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

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

JMeter基础用法和测试WebSocket请求

目录 JMeter websocket插件安装测试接口的编写添加测试线程组创建取样器创建WebSocket连接创建循环控制器创建WebSocket request-response Sampler创建固定定时器 正则匹配上一个请求的数据做为当前请求参数正则编写使用匹配值 CSV文件读取参数添加汇总报告和结果树 JMeter web…