当前位置: 首页 > ai >正文

Linux进程概念

程序与进程定义

程序是一个普通文件,或者可以俗称程序员写的代码,它是为了完成特定任务而准备好的指令序列和数据的集合,经过编译后,可以直接执行。比如Windows下使用C语言编写的程序编译链接后可以生成一个.exe的可执行程序,生成的这个可执行程序就是一个二进制程序。计算机的CPU可以直接识别二进制,不断的从内存中取指令和数据,按照程序顺序,执行代码。
进程(process)是一个已经开始执行但还没有终止的程序实例。程序被加载到计算机内存中运行,即运行中的程序。操作系统中所有进程实体共享着计算机系统的CPU、内存、外设等资源,但它们的内存是独立的。
进程由以下几部分组成:

  • 已分配内存的地址空间
  • 安全属性,包括所有权凭据和特权
  • 程序代码的一个活多个执行线程
  • 进程状态

线程:程序执行流的最小单位,操作系统进行调度的基本单位,现代计算机为提高效率而设计。一个进程至少有一个线程,进程产生的线程是共享进程的内存资源的。
进程是程序的动态执行,一旦运行就会有一个进程ID,程序一旦运行结束就会将所占硬件资源释放掉。简单来说进程 = 内核数据结构+代码和数据
进程类似于人类:它们被产生,有或多或少有效的生命,可以产生一个或多个子进程,最终都要死亡。一个微小的差异就是进程之间没有性别差异——每个进程都只有一个父亲。
那么操作系统该如何管理进程呢?答案就是先描述,再组织,就是用结构体把各个进程描述出来,比如进程的状态,优先级等。然后再用数据结构把这些结构体联系起来。为了管理进程,内核必须对每个进程所做的事情进行清除的描述。例如,内核必须知道进程的优先级,它是正在CPU上运行还是因某些事情而被阻塞,给她分配了什么样的地址空间,允许它访问哪个文件等等。
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。在Linux中描述进程的结构体叫做task_structtask_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_struct分类

  • 标示符:描述本进程的唯一标示符,用来区别其它进程。
  • 状态:任务状态,退出代码,退出信号等。
  • 优先级:相对于其它进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其它进程共享内存块的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

查看进程

我们可以通过指令ps ajx或ps aux来查看当前的所有进程
请添加图片描述

上图中的PID是进程的唯一标识符
我们可以自己编写一个代码来查看当前代码的进程,现在我有一个test.c的文件,可以用死循环的方式让其一直执行。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{while(1){sleep(1);}return 0;
}

接着我们通过grep指令配合ps ajx来查看当前代码的进程,输入ps ajx | grep test.exe。必须要再开一个窗口才能输入这个指令。
请添加图片描述

此时我们看到了test.exe的进程了,它的PID为14436。我们要的是自己程序的进程,为什么下面还有一个进程呢?其实第二个进程是grep指令,因为我们通过grep指令过滤出test.exe程序,所以查找进程的时候,也可以查找到grep自己。
我们也可以通过系统调用来获取进程标识符,其中父进程的id叫做PPID,子进程的id叫做PID。我们先通过man手册来看一下getpid()接口的含义。
请添加图片描述

getpid()返回的是调用进程的进程 ID,而getppid()返回的是调用进程的父进程的进程 ID。它们的使用需要包含2个头文件,返回类型是pid_t,本质上是一个int类型并且不需要传参。
接着我们把test.c文件的代码进行更改。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{while(1){printf("pid: %d\n", getpid());printf("ppid: %d\n", getppid());}return 0;
}

请添加图片描述

再通过ps ajx指令查找指定的PID就可以直接找到这个进程了。
请添加图片描述

可以看到,我们确实可以查到PID14988的进程,并且COMMAND属性为./test.exe,意思就是我们通过指令./test.exe执行了该进程。同理也可以找到父进程的PID。
请添加图片描述

这里要提出一个重要的概念:一切在命令行调用的进程,都是bash的子进程

/proc

进程的信息可以通过 /proc 系统文件夹查看,这是一个循文件系统。/proc的主要作用是

  1. 提供系统运行时的信息
    • 可以获取有关进程、内存使用、CPU 信息等各种系统状态的实时数据
    • 例如,通过查看 /proc/cpuinfo 可以了解 CPU 的详细信息,包括型号、核心数等。
  2. 监控进程状态
    • 每个正在运行的进程在 /proc 下都有对应的目录,目录名即为进程的 PID。
    • 可以查看进程的内存使用情况(如 /proc/<PID>/status )、打开的文件( /proc/<PID>/fd )等。
  3. 调整系统参数
    我们先来看一下/proc目录里面有什么
    请添加图片描述
    其中蓝色的文件表示目录。可以看到/proc内部大部分都是以数字命名的目录。而这个数字代表当前进程对应的PID,现在我们来查看一下刚才进程为14510的目录里面有什么。
    请添加图片描述

其中有很多文件,这里解释其中2个比较重要的

  • cwd:代表的是该进程当前的工作目录
    例如:上图bash进程的工作目录是/home/HJW/linux-learning/process
  • exe:代表该进程对应的可执行程序的路径

fork

fork 函数的主要作用是创建一个新的进程。具体来说,fork 函数会创建一个与当前进程几乎完全相同的子进程。我们先来学习fork函数的重要特点和作用

  1. 复制进程状态
    • 子进程会继承父进程的许多属性,如打开的文件描述符、环境变量、当前工作目录等。
  2. 返回值不同
    • 在父进程中,fork 函数返回子进程的进程 ID(PID)。
    • 在子进程中,fork 函数返回 0 。
  3. 实现并发编程
    • 通过创建子进程,可以让父进程和子进程同时执行不同的任务,实现并发操作。
      通过man手册来来进行了解。
      请添加图片描述

接着我们通过一个示例来演示一下。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t ret = fork();printf("hello proc!pid = %d! ppid = %d! ret = %d\n",getpid(),getppid(),ret);return 0;
}

请添加图片描述

第一行是父进程,第二行是子进程。代码中只有一个输出语句,但是结果确有2个,这是为什么呢?这是因为 fork 函数创建了一个新的进程,使得父进程和子进程都会执行后续的代码,但 fork 函数的返回值和 getpid 函数返回的当前进程 PID 在父进程和子进程中是不同的。
第一行语句hello /proc的PID和PPID是由test.exe创建的。而第二行语句的PID和第一行中由fork()创建的进程的PID一样,说明它是由fork创建出来的进程,它的PPID是15779与test.exe一样,说明fork创建的进程,是原先进程的子进程
此时我们就可以通过fork()函数的返回值不同,来判断父子进程了。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{int ret = fork();if(ret < 0){perror("fork");return 1;}else if(ret == 0){ //childprintf("I am child : %d!, ret: %d\n", getpid(), ret);}else{ //fatherprintf("I am father : %d!, ret: %d\n", getpid(), ret);}sleep(1);return 0;
}

请添加图片描述

对此我们可以知道fork函数对于子进程,它的返回值为0,对于父进程,创建新的进程的PID。

父进程与子进程的关系

现在我们来总结一下父进程与子进程之间是什么样的关系
父进程通过特定的系统调用(如 fork )创建子进程。子进程是由父进程创建出来的,它们的关系特点如下:

  1. 资源继承
    • 子进程会继承父进程的部分资源,例如打开的文件描述符、环境变量等。
    • 例如,父进程打开了一个文件用于读取,子进程也可以访问和操作这个文件。
  2. 进程ID关系
    • 父进程有一个唯一的进程标识符(PID),子进程也有自己的 PID。
    • 同时,子进程会记录其父进程的 PID(PPID)。
  3. 执行顺序
    • 父进程和子进程的执行顺序是不确定的,由操作系统的调度器来决定。

子进程和父进程会共用代码段。

http://www.xdnf.cn/news/518.html

相关文章:

  • Day95 | 灵神 | 二叉树 二叉树的垂序遍历
  • U-Boot(Universal Bootloader)简介
  • 不带无线网卡的Linux开发板上网方法
  • 英文论文写作:常用AI工具与【新秀笔目鱼】
  • JAVA的泛型
  • jQuery — 动画和事件
  • SpringBoot学习(过滤器Filter。拦截器Interceptor。全局异常捕获处理器GlobalExceptionHandler)(详细使用教程)
  • 哲学家就餐问题(避免死锁)
  • BootStrap:进阶使用(其二)
  • 计算机网络 实验五 RIP的配置与应用
  • 序列化和反序列化
  • 第9期:文本条件生成(CLIP + Diffusion)详解
  • 基于 Python 的自然语言处理系列(82):Transformer Reinforcement Learning
  • Alan AI - 面向Web的生成式AI SDK
  • 基于C语言实现文件读取
  • Linux 第五讲 --- 权限管理
  • 6.常用控件-QWidget|windowTitle|windowIcon|qrc机制|windowOpacity|cursor(C++)
  • Amlogic S905L3 系列对比:L3A、L3B 与 L3AB 深度解析
  • Unity之如何实现RenderStreaming视频推流
  • 大学英语四级选词填空阅读题和段落匹配解析
  • 【Hot100】54. 螺旋矩阵
  • 2025.04.19-阿里淘天春招算法岗笔试-第一题
  • 金融数学专题6 证券问题与资本利得税
  • Pandas数据统计分析
  • MCS-51单片机汇编语言编程指南
  • ArcPy Mapping 模块基础
  • 3. 进程概念
  • 修改Theme SHELL美化panel
  • Docker 网络详解:从 docker0 网桥到网络命名空间
  • 复习JUC的总结笔记