Unix中的进程和线程-1

news2024/11/29 2:53:08

目录

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 为相应的值来表示错误原因。

  

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

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

相关文章

【Docker笔记02】【常用软件安装】

一、前言 本系列是根据 B 站 尚硅谷 Docker 视频 学习记录笔记。因为没有视频课件&#xff0c;部分内容摘自 https://www.yuque.com/tmfl/cloud/dketq0。 本系列仅为自身学习笔记记录使用&#xff0c;记录存在偏差&#xff0c;推荐阅读原视频内容或本文参考笔记。 本文主要介…

Adobe ColdFusion 任意文件读取漏洞复现(CVE-2024-20767)

0x01 产品简介 Adobe ColdFusion是美国奥多比(Adobe)公司的一套快速应用程序开发平台。该平台包括集成开发环境和脚本语言,将可扩展、改变游戏规则且可靠的产品的愿景变为现实。 0x02 漏洞概述 由于 Adobe ColdFusion 的访问控制不当,未经身份认证的远程攻击者可以构造恶…

go语言基础学习-通道(续)

1、如何有序的从通道取值 第一种 判断ok的值 package day13import "fmt"func D131() {ch1 : make(chan int)// 开始goroutine将0-100的数据发送到ch1中go func() {for i : 0; i < 100; i {ch1 <- i}close(ch1)}()for {i, ok : <-ch1 // 通道关闭后再取值o…

计算机网络-HTTP相关知识(一)

HTTP基础 基本概念&#xff1a;HTTP是一种计算机之间交流通信的规范&#xff0c;它允许数据在两点之间传输&#xff0c;这个过程可以包括中转或接力。HTTP不仅仅包括文本&#xff0c;还可以包括图片、音频等超文本。状态码&#xff1a;HTTP状态码分为五类&#xff1a; 2xx&…

移植DM9000驱动至内核(linux-2.6.32.24)

目录 概述 1 移植 DM9000驱动 1.1 添加设备驱动代码 1.2 调整 DM9000 所用的位宽寄存器 1.3 配置MAC地址 2 配置和编译DM9000 Driver至内核 3 验证 概述 本文主要介绍如何移植DM9000的驱动到linux-2.6.32.24内核&#xff0c;笔者详细记录了内核移植过程中遇见的问题&…

【InternLM 实战营第二期笔记】InternLM1.8B浦语大模型趣味 Demo

体验环境 平台&#xff1a;InternStudio GPU&#xff1a;10% 配置基础环境 studio-conda -o internlm-base -t demo 与 studio-conda 等效的配置方案 conda create -n demo python3.10 -y conda activate demo conda install pytorch2.0.1 torchvision0.15.2 torchaudio2…

如何使用 Python 本地客户端操作读写云服务器 Redis 缓存数据库详细教程(更新中)

Redis 基本概述 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的使用 ANSI C 语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库&#xff0c;并提供多种语言的 API。它通常被称为数据结构服务器&#xff0c;因为值&#xff08;value…

Docker配置Mysql

1.首页搜索mysql镜像 2.选择对应版本的MySQL&#xff0c;点击pull 3.pull完成以后&#xff0c;点击images&#xff0c;这里可以看到刚刚pull完成的mysql版本 4.打开命令界面&#xff0c;运行命令 docker images ,查看当前已经pull的images 5.运行命令设置mysql docker run -it…

PHP三种方式读取RSA密钥加解密、签名验签完整教程

目录 第一步、生成公私钥 第二步、三种方式读取RSA密钥 第1种&#xff1a;公私钥弄成一行&#xff0c;必须一行没有空格和换行 第2种&#xff1a;直接复制生成公私钥 第3种;复制密钥存储为.pem文件后缀 第三步、RSA加解密 第四步、RSA签名以及验证签名 第五步、封装完整…

[图像处理] MFC载入图片并进行二值化处理和灰度处理及其效果显示

文章目录 工程效果重要代码完整代码参考 工程效果 载入图片&#xff0c;并在左侧显示原始图片、二值化图片和灰度图片。 双击左侧的图片控件&#xff0c;可以在右侧的大控件中&#xff0c;显示双击的图片。 初始画面&#xff1a; 载入图片&#xff1a; 双击左侧的第二个控件…

QT记事本

QT记事本 1.概述 2.界面  2.1 界面布局  2.2 UI美化stylesheet   2.2.1 准备   2.2.2 stylesheet   2.2.3 效果 2.3 窗口大小调整与子控件自适应 3.信号与槽  3.1 简述  3.2 信号与槽设置   3.2.1 UI控件设置   3.2.2 UI转到槽&#xff08;自动连接&am…

Go 源码之 gin 框架

Go 源码之 gin 框架 go源码之gin - Jxy 博客 一、总结 gin.New()初始化一个实例&#xff1a;gin.engine&#xff0c;该实例实现了http.Handler接口。实现了ServeHTTP方法 注册路由、注册中间件&#xff0c;调用addRoute将路由和中间件注册到 methodTree 前缀树&#xff08;节…

flutter官方案例context_menus

1&#xff1a;根据项目中的案例进行部署 2&#xff1a;运行查看有什么用&#xff0c;可不可以直接复制粘贴 案例地址 https://github.com/flutter/samples/tree/main/context_menus案例展示方法 直接把这个文件夹中的文件复制到lib文件夹中 3&#xff0c;19&#xff0c;4的fl…

关系型数据库mysql(10)MHA的高可用

一. MHA 的相关知识 1. 什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。MHA 的出现就是解决MySQL 单点的问题。MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。MHA能在故障…

[Windows]防火墙,出入站规则失效。

场景&#xff1a; 因为具体需要&#xff0c;在内网中&#xff0c;不想别人发现我们的nacos端口8848&#xff0c;因此我们设置了入站规则&#xff0c;特定的ip地址才能访问。但是实际测试中发现并不起作用。。。 经过一番排查得到一下结果。 为什么有些应用绕过了防火墙配置 有…

JimuReport积木报表 v1.7.4 公测版本发布,免费的JAVA报表工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

Linux速览(2)——环境基础开发工具篇(其一)

本章我们来介绍一些linux的常用工具 目录 一. Linux 软件包管理器 yum 1.什么是软件包? 2. 查看软件包 3. 如何安装软件 4. 如何卸载软件 5.yum补充 6. 关于 rzsz 二. Linux编辑器-vim使用 1. vim的基本概念 2. vim的基本操作 3. vim正常模式命令集 4. vim末行模式…

计算机网络-从输入网址到访问网站的全过程

当我们在浏览器中输入一个网址并按下回车键时&#xff0c;会发生一系列复杂的过程&#xff0c;最终使我们能够看到网页的内容。以下是这个过程的详细步骤&#xff1a; 客户端&#xff1a;首先&#xff0c;用户在浏览器中键入网址&#xff0c;然后浏览器会根据这个网址生成一个H…

vultr ubuntu 服务器远程桌面安装及连接

一. 概述 vultr 上开启一个linux服务器&#xff0c;都是以终端形式给出的&#xff0c;默认不带 ui 桌面的&#xff0c;那其实对于想使用服务器上浏览器时的情形不是很好。那有没有方法在远程服务器安装桌面&#xff0c;然后原程使用呢&#xff1f;至少ubuntu的服务器是有的&am…

C练习题(1)

变种水仙花&#xff08;来自牛课网&#xff09; 题目 变种水仙花数 - Lily Number&#xff1a;把任意的数字&#xff0c;从中间拆分成两个数字&#xff0c;比如1461 可以拆分成&#xff08;1和461&#xff09;,&#xff08;14和61&#xff09;,&#xff08;146和1),如果所有拆…