初识进程

news/2024/11/20 7:05:17/

文章目录

  • 一、进程的概念
    • 1. 进程是什么及进程的管理
    • 2. Linux 下的 pcb
    • 3. 系统调用接口 getpid 和 getppid
    • 4. 系统调用接口 fork

一、进程的概念

1. 进程是什么及进程的管理

在 Linux下 ./binaryfile 运行一个程序或者在 Windows下双击运行一个程序时,程序就变成了一个进程

根据冯诺依曼体系结构,程序运行起来后,程序的代码和数据就会被操作系统加载到内存,但一个程序仅仅被加载到内存,并不代表该程序就是进程

将这里的内存比作学校程序比作人进程比作学校的学生,如果一个人处在学校中,并不能说明这个人就是学校的人,如:学校的保安,食堂阿姨等这些并不是学生,只有那些被学校管理起来的,并且信息在学校的学生档案中的人才能被称作学生,所以只有程序被操作系统管理起来,并且程序的代码和数据的相关信息被操作系统记录了,这个程序才被称做进程

在学校中,当学生很多时,我们需要对学生进行管理,在操作系统中,当很多程序运行起来时,加载到内存中的代码便会很多,操作系统便需要对这些代码和数据进行管理

根据我们对管理进行的建模,可以知道操作系统对加载到内存中代码和数据的管理方式:先描述,在组织

  • 先描述:为了管理程序运行后加载到内存中的代码和数据,操作系统采用了一个结构体对象 pcb,用来描述加载到内存中的代码和数据的相关属性,其中 pcb 中有一个内存指针,用来指向内存中的代码和数据
    在国内的教材中 pcb(process control block) 统一称作进程,在国外有的叫做任务,Linux 下的 pcb 称做 task_struck
    进程管理
    进程 = 内核中关于程序的相关结构体 + 程序的代码和数据

  • 在组织:每一个加载到内存中的代码和数据操作系统都会为其创建一个 pcb 对象,因此我们可以在 pcb 对象中在加上 pcb 结构体指针,构成数据结构中的链表
    Linux 下采用双链表的形式组织

当操作系统想新增一个进程时(启动一个程序),只需要创建一个 pcb,然后录入该程序的属性到 pcb 中,然后在链表中插入该 pcb

当操作系统想杀掉一个进程时(结束程序的运行),只需遍历链表,找到该进程的 pcb,然后通过内存指针释放 pcb 指向的内存中的代码和数据,在再链表中释放该 pcb 结点即可

当操作系统想查看一个进程的运行状态时(查看程序运行是否正常),只需要遍历链表,找到该进程的 pcb,然后查看状态信息即可

当操作系统想找到一个优先级别较高的进程执行时(让 CPU 运行指定程序),只需要遍历链表,找到该进程的 pcb,然后通过内存指针找到 pcb 指向的代码和数据,让 CPU 执行即可

通过先描述,再组织的方式,操作系统对进程的管理被完全的转换成了对 pcb 结构体组成的链表数据结构的增删查改

运行的可执行程序都要被操作系统转换为进程来调度以便完成特定的任务,因此当我们运行一个程序时,就称作 创建了一个进程

仅个人当前理解:软件其实就是一个在磁盘上的二进制文件,当软件运行起来后,便需要加载到内存,所以操作系统对进程的管理,便是对软件资源的管理

进程 = 内核中关于进程的相关数据结构 + 进程的代码和数据

2. Linux 下的 pcb

为了操作系统管理进程,需要描述出进程的共同属性,从而产生了结构体 pcb

task_struct 的字段

  • 标识符:描述本进程的唯一标识符,用来区别进程
  • 状态:任务状态,退出代码,退出信号等
  • 优先级:相对于其他进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:包括程序diamante和进程的相关数据结构的指针,还有和其他进程共享的内存块的指针

pcb 和可执行程序的文件属性关系不大

3. 系统调用接口 getpid 和 getppid

如何证明程序运行起来,便成为了一个进程?

当我们创建一个进程时,操作系统就会在 /proc 目录下创建一个该进程 pid 为名的目录,该目录下存在该进程的属性(文件路径等),当进程终止时,/proc 目录中也会删除该进程 pid 为名的目录

预备知识1:
ps ajx:查看系统中所有的进程
ls /proc:查看系统中的所有进程,其中目录名为数字的表示进程的标识符 pid

[starrycat@iZ2vcer6gtjgqa43cdpeeaZ code]$ ps ajxPPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND0     1     1     1 ?           -1 Ss       0   3:57 /usr/lib/systemd/systemd --system --deserialize 170     2     0     0 ?           -1 S        0   0:00 [kthreadd]2     3     0     0 ?           -1 S        0   0:08 [ksoftirqd/0]2     5     0     0 ?           -1 S<       0   0:00 [kworker/0:0H]2     7     0     0 ?           -1 S        0   0:00 [migration/0]
...
[starrycat@iZ2vcer6gtjgqa43cdpeeaZ code]$ ls /proc
1     14     21134  25405  28     350  47   539   6770  854        crypto       interrupts  kpagecount  mtrr          softirqs       uptime
10    1523   21252  25512  280    36   49   557   6771  9          devices      iomem       kpageflags  net           stat           version
101   15631  21262  26     29     365  5    587   7     acpi       diskstats    ioports     loadavg     pagetypeinfo  swaps          vmallocinfo
1019  16     22     27     296    37   50   598   787   buddyinfo  dma          irq         locks       partitions    sys            vmstat
...

预备知识2:
pid_t getpid(void):返回调用该函数的进程标识符 pid,需要包含头文件 <sys/types.h> 和 <unistd.h>
pid_t:有符号整形的 typedef

如果想了解更多关于 getpid 函数的内容,通过 man 2 getpid 即可查看

接下来通过代码证明程序运行起来,便成为了一个进程

在 process.c 中写好如下代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){printf("我是一个进程了,我的 pid 是:%d\n", getpid());sleep(1);}return 0;
}

证明1:
打开新的窗口,通过命令查看发现,运行的程序 process 可以查到他的进程标识符 pid

其中 ps ajx | head -1 表示获取进程的第一行属性字段(便于观看)
&& 表示当前一条指令执行成功后执行后一条指令
ps ajx | grep process | grep -v grep 表示筛选出 process 的进程,并且去除掉 grep 这个进程
在这里插入图片描述

过程2:
用 ctrl + c 终止程序后,便查找不到该进程了

在这里插入图片描述

终上所述,程序运行起来后就变成了进程


在进程中存在着父子进程的概念

pid_t getppid(void):返回调用该函数的进程的父进程标识符 pid,需要包含头文件 <sys/types.h> 和 <unistd.h>
pid_t:有符号整形的 typedef

在 process.c 中写好如下代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{while(1){printf("我是一个进程了,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());sleep(1);}return 0;

不断的执行 process 会发现子进程的进程标识符一直在增加,但是父进程的进程标识符 6771 一直不变,通过 ps 命令后可以发现 6771 这个进程标识符就是命令行解释器 bash,即命令行执行的程序都是通过创建子进程的方式去执行的,是为了避免执行的程序挂了,导致影响 bash 自己

命令行解释器 bash 其实就是在 /bin/bash 的一个二进制可执行程序,因此 bash 也是一个进程
在这里插入图片描述

命令行启动的所有程序,都是 bash 创建的子进程,为了防止子进程挂了,导致影响自己

4. 系统调用接口 fork

命令行是如何创建子进程的呢?

pid_t fork(void):如果创建子进程成功,则给调用该函数的父进程返回子进程的 pid,给子进程返回 0,如果失败则返回 -1,需要包含头文件 <sys/types.h> 和 <unistd.h>

  • 在代码中执行到 fork 语句后,执行流变成了两个执行流
  • fork 之后的代码,父子进程都会执行,因此我们可以用 if 语句来让执行流分流,以便父子进程执行不同的代码块

在 process.c 中写好如下代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t ret = fork();if (ret == 0){// 执行子进程while (1){printf("我是子进程,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());sleep(1);}}else if (ret > 0){// 执行父进程 while (1){printf("我是父进程,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());sleep(1);}}else {}return 0;
}
  • fork 创建子进程成功之后,父进程和子进程谁先被 CPU 调度运行是由操作系统决定的

可以通过 fork 系统调用来创建子进程

fork 创建子进程的过程:操作系统为了可以管理子进程,会根据父进程的 pcb 创建子进程的 pcb,并且子进程的 pcb 会和父进程的 pcb 指向同一块代码和数据

在这里插入图片描述

虽然父进程创建子进程后,子进程指向父进程的代码和数据,但是不同的进程在运行时是独立的,父子进程在运行时也是独立的

kill -9 pid 功能:杀掉进程

再次运行 process 之后,在另一个窗口中输入 kill -9 父进程 pid,此时子进程任然可以正常运行
在这里插入图片描述

父子进程是如何做到独立的呢?

  • 在代码层面:可执行程序的代码都是二进制机器指令了,是只读的,不可能被修改,因此父子进程可以一起读代码,只需要记住自己进程执行代码的位置即可
  • 在数据层面:Linux 操作系统采用写时拷贝的方式来保证数据的独立性,即:当某一个进程想要修改数据时,操作系统会自己拷贝一份数据到别的位置然后进行修改

为什么一个函数会有两个返回值?
因为一个函数在执行 return 之前,函数的主题功能已经完成了,对于 fork 函数,在 return 之前已经创建好子进程了,此时便有父子进程两个执行流,于是父子进程都会执行 return 语句,也就产生了两个返回值的现象


http://www.ppmy.cn/news/34821.html

相关文章

八大排序算法之归并排序(递归实现+非递归实现)

目录 一.归并排序的基本思想 归并排序算法思想(排升序为例) 二.两个有序子序列(同一个数组中)的归并(排升序) 两个有序序列归并操作代码: 三.归并排序的递归实现 递归归并排序的实现:(后序遍历递归) 递归函数抽象分析: 四.非递归归并排序的实现 1.非递归归并排序算法…

<Linux>进程控制

进程控制 文章目录进程控制一、进程创建1.fork函数认识2.写时拷贝3.fork常规用法4.fork调用失败的原因二、进程终止1.进程退出场景2.进程退出码3.进程退出的方式三、进程等待1.进程等待是什么&#xff1f;2.进程等待的必要性3.进程等待的方法3.1.wait函数3.2.waitpid函数4.如何…

零基础小白如何入门网络安全?

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b; 学习XXX技能没方向&#xff1b; 学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之…

leetcode每日一题:134. 加油站

系列&#xff1a;贪心算法 语言&#xff1a;java 题目来源&#xff1a;Leetcode134. 加油站 题目 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[…

Reids中的有序集合Zset

有序集合&#xff08;ZSet&#xff09; 文章目录有序集合&#xff08;ZSet&#xff09;常用命令zaddzrevrangezrangezrangebyscore/zrevrangebyscorezscorezcardzremzincrbyzcountzmpopzrank /zrevrank常用命令 命令作用zadd key score member添加元素zrevrange key start sto…

【Java集合面试宝典】HashMap的put流程和特性?HashMap的扩容机制?原理— day08

目录 数组和链表分别适用于什么场景&#xff0c;为什么&#xff1f; 数组 链表 List和Set的区别 List和Map、Set的区别 HashMap 、HashTable 和TreeMap有什么区别&#xff1f; hashmap的特性 HashMap和HashTable有什么区别&#xff1f;&#xff08;必会&#xff09; J…

服务端测试知识汇总

目录 服务端测试思想 经济学⻆度 ⾦字塔模型 技术⻆度 HTTP协议 三次握⼿ HTTP完整请求 通信模式 URI信息 请求⽅法 请求状态码 请求/响应头 常⽤请求数据格式 COOKIE请求流程 SESSION请求流程 TOKEN请求流程 API测试维度 单接⼝测试 多个接⼝测试 …

UEngine 运行器帮助

UEngine 运行器帮助 帮助简述 安装APK&#xff1a;点浏览按钮&#xff0c;选中需要安装的APK&#xff0c;然后点安装按钮 卸载APK&#xff1a;在卸载APK下面的输入框内输入需要卸载的APK包名&#xff0c;点卸载按钮&#xff0c;如果无法获取包名&#xff0c;可以通过浏览APK文件…