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 process
        printf("Parent process is running with PID: %d\n", getpid());
        sleep(5); // Sleep for a while to allow child process to become orphan
        printf("Parent process is terminating\n");
    } else if (child_pid == 0) { // Child process
        printf("Child process is running with PID: %d, Parent PID: %d\n", getpid(), getppid());
        sleep(10); // Sleep for a while to become zombie
        printf("Child process is terminating\n");
    } else { // Error handling
        perror("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 为相应的值来表示错误原因。

  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件名目录名或卷标语法不正确:数据恢复策略与预防措施

一、文件名目录名或卷标语法不正确的现象 在日常使用电脑或移动设备时&#xff0c;我们经常会遇到“文件名目录名或卷标语法不正确”的错误提示。这种错误通常发生在尝试访问、修改或删除文件、目录或卷标时&#xff0c;系统会提示无法完成操作&#xff0c;因为文件名、目录名…

星云曲库测试报告

文章目录 一、项目介绍1.1项目背景1.2功能介绍 二、测试环境三、测试执行过程3.1功能测试3.1.1登录页面测试3.1.2歌曲列表页面测试3.1.3“我喜欢”页面测试3.1.4上传页面测试 3.2界面自动化测试3.2.1登录页面测试3.2.2歌曲列表页面测试3.2.3“我喜欢”页面测试3.2.4上传页面测试…

Unity TrailRenderer的基本了解

在Unity中&#xff0c;TrailRenderer组件用于在对象移动时创建轨迹效果。通常用于增强游戏中的动态物体&#xff0c;比如子弹、飞行道具或者角色移动时的拖尾效果。 下面来了解下它的基本信息。 1、创建 法1&#xff1a;通过代码创建 using UnityEngine;public class Trail…

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;写父级盒子…

精品PPT-2023年无人驾驶汽车车联网网络安全方案

以下是部分PPT内容&#xff0c;请您参阅。如需下载完整PPTX文件&#xff0c;请前往星球获取&#xff1a; 无人驾驶安全架构是一个复杂的系统&#xff0c;它涉及到多个关键组件和层次&#xff0c;以确保无人驾驶车辆在各种情况下都能安全、可靠地运行。以下是一些主要的无人驾驶…

并查集

本文用于个人算法竞赛学习&#xff0c;仅供参考 目录 一.什么是并查集 二.并查集实现 三.路径优化 四.时间复杂度 五.并查集路径压缩 模板 五.题目 一.什么是并查集 并查集&#xff08;Disjoint Set&#xff09;是一种数据结构&#xff0c;用于处理一系列不相交的集合的合…

javaIO

file类 一个File类的对象可以表示一个具体的文件或目录 mkdir 创建单级文件夹 mkdirs 创建多级文件夹 delete 删除一个文件夹时&#xff0c;文件夹里面必须是空的 listfiles 将文件夹的子集放到一个file类型的数组中 输入及输出的概念 输入input 输出output 把jav…

pyinstaller打包多线程pyqt5程序后,报错,反复弹窗等问题

报错1&#xff1a; Traceback (most recent call last): File “MPL.py”, line 502, in File “Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_multiprocessing.py”, line 45, in _freeze_support ValueError: not enough values to unpack (expected 2, got 1) 报…

STM32学习笔记(10_3)- 软件I2C读写MPU6050

无人问津也好&#xff0c;技不如人也罢&#xff0c;都应静下心来&#xff0c;去做该做的事。 最近在学STM32&#xff0c;所以也开贴记录一下主要内容&#xff0c;省的过目即忘。视频教程为江科大&#xff08;改名江协科技&#xff09;&#xff0c;网站jiangxiekeji.com 本期开…

分布式之分布式事务详解

分布式事务与实战运用 什么是分布式事务&#xff1f; 业务场景&#xff1a;用户A转账100元给用户B&#xff0c;这个业务比较简单&#xff0c;具体的步骤&#xff1a; 1、用户A的账户先扣除100元 2、再把用户B的账户加100元 如果在同一个数据库中进行&#xff0c;事务可以保证…

C语言之Linux内核回调函数写法(八十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

论文阅读,Accelerating the Lattice Boltzmann Method(五)

目录 一、Article:文献出处&#xff08;方便再次搜索&#xff09; &#xff08;1&#xff09;作者 &#xff08;2&#xff09;文献题目 &#xff08;3&#xff09;文献时间 &#xff08;4&#xff09;引用 二、Data:文献数据&#xff08;总结归纳&#xff0c;方便理解&am…

Nginx的反向代理

Nginx的反向代理 location ^~ /aaa {proxy_pass http://192.168.15.78/; } 1. 跨域 2.Nginx 代理服务器缓存 3.Nginx 负载均衡 4. 动静分离 Nginx的跨域 跨源资源共享 (CORS) 是一种机制&#xff0c;它使用额外的 HTTP 标头让用户代理获得访问来自不同来域的服务器上选定资…

LongAdder 和 AtomicLong

有幸看到一篇关于这个讲解 2个类的讲解&#xff0c;自己也归纳总结一下。 一、解析 看源码底层会发现实现机制不一样&#xff0c;当然这个也是必须的 LongAdder 点进去之后会发现&#xff0c;CAS 它是一个CAS的实现类。至于Cell类JVM提供的内置函数 官方说法是&#xff1a;…

Python版【植物大战僵尸 +源码】

文章目录 写在前面&#xff1a;功能实现环境要求怎么玩个性化定义项目演示&#xff1a;源码分享Map地图:Menubar.py主菜单 主函数&#xff1a;项目开源地址 写在前面&#xff1a; 今天给大家推荐一个Gtihub开源项目&#xff1a;PythonPlantsVsZombies&#xff0c;翻译成中就是…

小程序开发调试指南

调试指南 一、Vconsole 一些线上问题需要在真机中进行调试或日志输出&#xff0c;为方便查看前端日志的输出&#xff0c;App 端提供了调试模式, 打开调试模式后面板界面中会显示 vConsole 按钮&#xff0c;点击后可打开手机端 vConsole界面查看日志输出。 1.如何开启 点击小…

JumpServer 堡垒主机

JumpServer 堡垒机帮助企业以更安全的方式管控和登陆各种类型的资产 SSH&#xff1a;Linux/Unix/网络设备等Windows&#xff1a;Web方式连接/原生RDP连接数据库&#xff1a;MySQL、Oracle、SQLServer、PostgreSQL等Kubernetes&#xff1a;连接到K8s集群中的PodsWeb站点&#x…

复现黄金票据

一、搭建环境 搭建域环境可以点击这个查看步骤 在这里面monowall的配置查看 二、实验步骤 拿到域名 拿到SID 使用mimikatz拿到krbtgt用户的哈希 删除票据 也可以使用mimikatz.exe删除票据&#xff0c;命令是kerberos::purge 伪造票据 拿到域控 伪造成功

物联网实战--入门篇之(六)嵌入式-WIFI驱动(ESP8266)

目录 一、WIFI简介 二、基础网络知识 三、思路讲解 四、代码分析 4.1 状态机制 4.2 客户端连接 4.3 应用数据接收处理 4.4 数据发送 4.5 主函数调用 4.6 网络连接ID分配 五、总结 一、WIFI简介 WIFI在我们生活中太常见了&#xff0c;手机电脑都可以用WiFi连接路由器进行上…

MySQL核心命令详解与实战,一文掌握MySQL使用

文章目录 文章简介演示库表创建数据库表选择数据库删除数据库创建表删除表向表中插入数据更新数据删除数据查询数据WHERE 操作符聚合函数LIKE 子句分组 GROUP BY HAVINGORDER BY(排序) 语句LIMIT 操作符 分页查询多表查询-联合查询 UNION 操作符多表查询-连接的使用-JOIN语句编…