【Linux】进程的概念及操作进程

news2024/9/22 4:05:39

目录

  • 1.什么是进程
  • 2.描述进程 - PCB
  • 3.进程的具体操作
    • 3.1进程的属性与文件属性的关系
    • 3.2查看进程
      • 准备工作
      • 使用指令查找对应进程:
      • 在文件中查看进程
    • 3.3关闭进程
      • ctrl+c
      • kill
    • 3.4进程的一些特性
    • 3.5通过系统调用获取进程标识符
    • 3.6通过系统调用创建子进程

1.什么是进程

背景:

我们通过冯诺依曼体系结构知道,程序需要运行,需要将其从磁盘中传入内存,在传入CPU,由CPU将其运行起来。

了解了这个背景,我们就能对进程得出下面的理解:

  • 进程 就是被加载到内存中的程序,或者被运行起来的程序就叫做进程

这也是很多教材上的说法,但它并不完全正确,甚至有些片面。

如上图,我们的计算机在运行后不论是我们自己打开的软件还是后台应用的程序,数量是很多的,如果这些程序就这样赤裸裸的交由操作系统来管理,会增加操作系统的负担,所以操作系统需要对每一个程序做整理,将其打包成一个对象,更抽象的讲,将其变成一个变量,操作系统只对这些变量进行管理,就会变得容易。

比如说,在学校中,每个学生都有自己的信息,如姓名、年龄、性别、各科成绩等等,这里的学生就是操作系统中的程序,想要对其进行管理,我们可以将学生的属性,如姓名、年龄等放入结构体中,在通过结构体创建对应的对象,管理这些对象即可,这样我们也就得出了进程的概念。

我们编写的程序通过编译、链接后生成一个可执行程序,该程序本质是一个文件,放在磁盘。在Windows中双击运行,在Linux中使用./运行,使其加载到内存。

在进程加载进内存后,操作系统要对其进行管理,也就是对程序的数据进行管理,从这些程序中抽象出所有的属性来构建一个结构体,然后将每一个进程都创建成一个结构体对象,最后再将所有的结构体对象使用某一种 高效的数据结构(在Linux中使用双链表 组织起来;

而想要获取每个进程中对应的代码时,根据结构体对象调用在内存中的对应的代码即可;

以此将所有的应用程序都已进程的方式运行,而每个进程都有自己独立的地址空间,使进程之间的地址相互隔离。CPU由操作系统同一进行分配,每个进程根据进程优先级的高低都会有机会得到CPU。

所以我们说,进程只是正在运行或将要运行的程序是不正确的,它的理解应当如下:

进程 = 内核关于进程的相关数据结构 + 进程对应内存上的代码和数据

其中,在操作系统中,用来描述和组织进程数据结构(抽象的可以看作结构体),被称为 进程控制块-PCB

2.描述进程 - PCB

从上面的知识中我们知道,单一的进程是由一个特定的结构体来描述出的,该结构体(数据结构)就是进程控制块,可以理解为进程属性的集合。

课本上将进程控制块称之为PCB(process control block),而不同的操作系统,PCB的名称不同,在Linux操作系统下的PCB是:task_struct

在这里插入图片描述

也就是说,每一个进程被加载到内存后,Linux操作系统都会对其创建task_struct对象,并将其与加载到内存的代码和数据产生关联,然后将所有的task_struct使用指针连接成一个双链表(此时该进程即为操作系统内被管理的进程),这样对进程的管理就是对链表的增删查改。(此为简易路线,剩余内容之后会讲)

PCB是用来描述进程的,在C++中我们使用类来描述一个事务,在C语言中我们使用结构体描述,而Linux是用C语言编写的,所以Linux当作描述进程的进程控制块就是结构体task_struct实现的。

  • task_struct是Linux内核的一种数据结构,被装再到内存(RAM)里,并且包含进程的信息。

了解了这些再来看一下在Linux中,task_struct包含的程序的各种属性内容,如下:

task_struct内容分类

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

抽象出来,可以用如下结构体来表示(以LInux为例)

struct task_struct{
    //进程的所有属性
    ....
    //进程对应的代码和数据的地址
    ....
    //下一个进程的地址
    struct task_struct* next;
}

关于进程的进程控制块PCB的task_struct结构体的具体描述可以参考这篇博客Linux中进程控制块PCB-------task_struct结构体结构

了解了这些,我们也就能更具体的感受进程的概念:

进程 = 内核关于进程的相关数据结构 + 进程对应内存上的代码和数据

总结:

  1. PCB是什么

    它就是一个struct结构体,这个结构体中包含着进程大部分的属性

  2. 为什么进程管理中要使用PCB?

    为了管理进程

3.进程的具体操作

知道了进程是什么,我们接下来就来看一下,在LInux下进程的具体操作。

3.1进程的属性与文件属性的关系

我们知道在磁盘中的文件 = 内容 + 属性。

那一个文件,在磁盘当中向内存加载,加载的是这个文件的内容还是属性呢?

答:内容

我们知道,每一个进程都有自己的属性集合叫PCB,那在磁盘中文件的属性和进程的属性有关系吗?

答:有关系,但是关系不大

比如说,在磁盘中,文件的属性一般包含着文件的权限、拥有者、所属组、什么时间创建修改和文件名是什么等等,这些判断用户是否有权限去操作文件和文件的一些基本信息,

而PCB结构体是一种内核数据结构,是由操作系统去创建动态维护的,和磁盘里的属性没有关系,它是重起炉灶,另搭台。就是说,进程里的属性是由操作系统根据获取的代码和数据自己去创建和维护的,和磁盘中的文件属性不同。

例如,进程的pid(process id,进程的id号),这个是文件没有的,而如果操作系统想要知道某个进程对应的文件名或文件的体积大小,它也是可以知道的,所以我们说它们有关系,但关系不大。

3.2查看进程

准备工作

  • 使用到vim、gcc、makefile,若是不熟悉,点击名称查看对应博客即可

我们在Linux中新建文件myprocess.c,编写如下代码

在这里插入图片描述

代码功能:我们知道当程序是以进程的模式在硬件中运行,在执行该程序后,陷入死循环,一直输出“hello process”,此时该程序对应的进程一定是存在的我们可以查看到。

所以我们可以得出如下结论:

结论:

  1. 在Windows系统中,通过双击或打开一个程序,程序就会以进程的方式运行。
  2. 在Linux系统中,执行可执行程序(./)后即为将程序变为进程的形式

下图为makefile文件代码:

在这里插入图片描述

上图表示,我们编写代码的myprocess.c文件生成的对应可执行文件名为MyProcess,下面会直接使用MyProcess可执行文件执行运行该程序

小技巧

因为只有程序运行,才能保证进程的存在,所以我们要在程序运行的情况下,观察对应进程信息,这时我们就需要使用多个页面配合的方式,我使用的是Xshell,如果大家和我使用的相同可以使用如下方法创建多页面。

  1. 右击当前会话框,找到复制会话

    在这里插入图片描述

  2. 复制出的会话与当前会话相同,处在同一文件,共用同一操作系统,一边的发生改变影响另一边

  3. 将复制好的进程拖至合适位置

    在这里插入图片描述

    • 复制会话,即为创建了一个新的bash进程,并且与原来的bash进程共用同一操作系统(后面会讲)
  4. 可使用该方法产生复制多个会话,一次运行多个程序,在查看进程

使用指令查找对应进程:

使用ps axj | head -1 && ps axj | grep 可执行文件名 | grep -v grep指令查找。

注意: 按具体需求使用该指令

在这里插入图片描述

解释:

指令:ps axj为查看当前执行的所有进程,

在这里插入图片描述

|:管道符,表示将前面执行的结果传到后面,执行后面的指令

指令:head -1取所有ps axj所有显示的第一行,也就是上面动图,最后使用白色部分选择的那行,给出每列名称,该指令具体内容查看该篇博客–常见指令

在这里插入图片描述

主要包含以下这几个名称:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND

这里介绍两个本文要用的,剩余的不做研究

  • PID:process ID ,进程的编号
  • PPID:父进程编号,即对应PID的父进程编号

&&:逻辑或,将两端指令的结果结合,输出到屏幕

指令:grep 可执行文件名 ,grep指令用于查找对应字符串,并输出字符串所在行,该指令具体内容,可查看该篇博客–常见指令

在这里插入图片描述

在上面的一张动图中,最终显示的结果如下

在这里插入图片描述

其中打印出的第二行是我们不需要的,可以使用最后一个指令使其不在打印

指令:grep -v grep,-v选项,表示不显示后面字符串(grep)所在行,具体内容,可查看该篇博客–常见指令

在这里插入图片描述

在文件中查看进程

/proc文件中查看(该方式比较冷门)

在这里插入图片描述

  • 数字对应的是对应进程的PID

举例:

我们先执行MyProcess文件,在使用上面的指令查找对应的进程,找到该进程的PID,根据PID在/proc中查看对应文件,如下:

在这里插入图片描述

具体如下(上面得进程不小心关闭了,重新打开了一个)

在这里插入图片描述

当我们查看该PID对应文件的具体信息,会看到如下情况:

在这里插入图片描述

表明该进程对应的程序的位置。

3.3关闭进程

ctrl+c

如下图,在当我们打开一个程序,并处于运行状态,我们可以使用热键ctrl + c结束该程序(结束该进程)

在这里插入图片描述

kill

我们也可以使用kill指令加-9选项后根进程PID结束目标进程,如下

kill -9 PID

在这里插入图片描述

3.4进程的一些特性

  1. 一个程序可以同时形成多个进程

    如下,我们对同一可执行文件执行两次,并查看进程

    在这里插入图片描述

  2. 随着进程创建的先后顺序不同,它的PID是变化的

    如下,我们打开关闭同一可执行文件,并查看对应进程PID的变化

    在这里插入图片描述

    如上图红色的框,表示每次运行该程序后,进程PID的变化,它是变化的,因为每次都需要重新将程序已进程得形式执行,不可能每次都使用相同得PID

  3. 一个程序它创建多次,虽然每次PID不同,但PPID(父进程)相同

    如上图,虽然每次进程的PID不同,但它们的父进程PPID是相同的,再Linux中该父进程为bash

  4. Linux中进程的父进程都是bash(有特殊情况,但毕竟是特殊情况)

    每一次我们开机,都会启动操作系统,那就需要命令行解释器来帮我们做解释命令,所以,bash命令行解释器也是一个进程,它有独立的PID。命令行启动后的所有程序,最后都会变成进程,而该进程对应的父进程就是bash。

    在该篇博客shell及其运行原理中提到过,shell为了保护自己,创建子进程来执行用户的指令。

    如下,我们在/proc文件中根据上图父进程的PID查看对应的信息,是否为bash

    在这里插入图片描述

    注意: 我们的指令(如:ls、touch等),是由C语言编写,./后也会形成进程。

  5. 一个进程结束后,它对应的进程PID文件也会被删除无法打开

    1. 我们在进程结束后,尝试打开对应进程PID文件

      在这里插入图片描述

      如上图,打不开文件,并且在/proc文件中找不到对应的文件

    2. 先打开进程PID文件,后结束进程,观察结束后该文件的变化

      在这里插入图片描述

  6. 我们知道bash也是进程,它是所有进程的父进程,当我们关闭该进程后,相对应的对话就无法使用,需重新创建,如下,我们使用kill指令关闭该进程(不同的系统下展现情况不同但意义相同)

    在这里插入图片描述

    上图我们创建了两个会话,虽然共用同一Linux操作系统,但对应得bash进程不同,我们查看的是右边会话对应得进程,所以它的父进程即为右边得bash,关闭后不会影响左边得会话。

3.5通过系统调用获取进程标识符

系统调用: 计算机是由用户层、操作系统、驱动、硬件等等组成,它们每一层使用接口与相邻层连接,而操作系统给出的供上层开发使用的接口统称为系统调用。(具体看该篇博客–操作系统)

当我们想在程序运行的时候显示它对应的进程PID和PPID,就需要使用系统调用。

我们使用系统调用接口getpid()getppid()来获取进程PID和PPID,这里我们可以通过man手册来查看这两个函数(按q退出)

man getpid

在这里插入图片描述

注意: 函数的返回值pid_t,将其当作int即可,在打印时,使用%d

我们在将myprocess.c文件中的代码做如下修改

在这里插入图片描述

运行该程序后,查看对应的PID和PPID值是否正确

在这里插入图片描述

3.6通过系统调用创建子进程

通过上面的学习我们知道,Linux操作系统通过父进程bash来创建子进程,那父进程是如何来创建子进程的?如何在代码层面直接创建新的子进程呢?这就要学习第三个系统调用接口fork().

可通过man查看fork的信息

man fork

在这里插入图片描述

下面让我们使用fork()看一下结果

首先,我们先将myprocess.c的程序做如下修改:

在这里插入图片描述

运行后的结果如下:

在这里插入图片描述

如上图运行结果,形成两个进程,父进程的PID为10130,子进程的PID为10131(23286为该会话的bash进程PID)

最开始打印父进程的字符串"AAA",fork后打印了父进程的字符串“BBB”,最后打印子进程的字符串“BBB”。

在这里插入图片描述

这是因为在fork后执行流会变成两个执行流,变为两个进程,这两个进程共享fork后的代码,而且并不是必须要先运行父进程后运行子进程,这个根据服务器的不同,结果不同。

整个调用链为:当我们运行该程序时,产生了子进程,而该程序内通过fork又创建了它自己的子进程。

总结: 对于父子进程,子进程是再父进程的基础上创建。

那么问题就来了,操作系统如何知道那个是父进程,那个是子进程的?

答:通过fork的返回值。

让我们在来使用man指令查看fork,并找到它关于返回值的内容,如下:

在这里插入图片描述

若进程创建成功,将子进程的PID返回给父进程,将0返回给子进程,若失败将-1返回给父进程,没有子进程创建,显示errno

注意:返回值类型为pid_t

我们通过下面修改的代码和修改后的运行结果理解上面的内容

在这里插入图片描述
在这里插入图片描述

如上图,fork给父进程返回的是子进程的PID17881,给子进程返回的是0,以此来区分父子进程。

想必这里大家就会有疑问,为什么一个变量会接收到两个返回值?为什么同一个地址却有不同的值?第一个问题我们下面会讲到,第二个设计到进程地址空间,本文为进程的概念,第二个问题在之后的博客会讲到。

了解了这些知识和现象,我们在来看一下一般我们使用fork的方式,将代码修改如下:

运行的结果如下:

在这里插入图片描述

还是一个变量,有两个值,导致该程序即执行了if又执行了else if。这也是fork如何两个返回值的问题?

接下来我们按步骤解答这个问题:

  1. fork做了什么?

    答:创建了子进程。

    我们知道一个进程 = 内核关于进程的相关数据结构 + 进程对应的磁盘代码和数据

    像上面这段程序,运行后先创建了一个进程它有自己对应的代码和数据,在fork后,它创建了自己的子进程,而子进程对应的仍然是父进程的代码和数据,

    在这里插入图片描述

  2. fork创建子进程后,父子进程是否影响着彼此?

    这里先要说一个概念:进程在运行的时候,是具有独立性的!

    比如说,我们在Windows系统下,打开QQ、微信、爱奇艺等待软件,在关闭QQ对其他的软件是没有影响的。

    而上面的程序是父子进程,运行的时候也是一样的,我们运行上面的程序,然后关闭父进程,查看子进程是否可以运行

    在这里插入图片描述

    如上图,父进程关闭后,子进程还可以正常运行,其中子进程的PPID变为1,子进程变为孤儿进程(孤儿进程此处作为了解,需在使用kill关闭子进程)

  3. fork如何看待代码和数据?

    首先,我们编写的代码在运行后不会被改变,例如,某行使用的是if,运行后被改为while,这是不会的。

    其次,运行程序后,在修改数据时,是将数据拷贝到另一个地方,在另一个地方对数据进行修改。

    总结:

    代码:代码是只读的,不会被改变

    数据:当一个执行流尝试修改数据的时候,操作系统(OS)会自动给我们当前进程除法写时拷贝

    所以在fork后形成两个进程共用同一代码和数据,代码时只读的无法进行修改,而数据以写实拷贝的方式两个进程各自私有一份,并且两进程 不互相干扰。(具体内容有关进程地址空间)

  4. fork如何理解两个返回值问题?

    首先,我们要明白,fork是一个系统调用接口,也就是一个函数,而一个函数在最后执行return前,它主要的功能已经执行完毕。其中,调用fork的是父进程,父进程一定执行了一遍fork并且return返回了值。

    在来看fork,它的功能就是创建子进程PCB,使其跑起来,最后return返回值。

    既然fork已经执行到return,说明它的主要功能已经执行完毕,也就是此时子进程已经创建,而子进程创建后也会执行代码,也就是说,fork执行到return后,父进程和子进程都会执行一次,导致返回了两个值。

    这两个值被接收时又发生了写时拷贝,存入了不同的空间,虽然看起来存入同一变量,同一空间。

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

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

相关文章

工程师手册:电源设计中的电容选用规则

摘要 电源往往是我们在电路设计过程中最容易忽略的环节。作为一款优秀的设计,电源设计应当是很重要的,它很大程度影响了整个系统的性能和成本。电源设计中的电容使用,往往又是电源设计中最容易被忽略的地方。一、电源设计中电容的工作原理 在…

前段时间公司招人,面了一个要20K的,一问自动化只会点皮毛···

前段时间公司要招2个自动化测试,同事面了几十个候选人,发现了一个很奇怪的现象,面试的时候,如果问的是框架api、脚本编写这些问题,基本上个个都能对答如流,等问到实际项目的时候,类似“怎么从0开…

vue+elementUI 实现设置还款日字母弹窗组件

1、业务背景 还款业务,设置每月还款日,选每月几号扣款,不需要29、30、31,因为不是每个月都有这三天的 2、预期效果图 3、代码实现 3.1 初始化vue项目 地址:https://cn.vuejs.org/guide/introduction.html 3.2 在项…

KMS钥匙管理系统产品分析

背景: 人造成的不可控因素很多 以前拿公司钥匙的时候要先进行纸质登记,还钥匙的的时候也要进行纸质统计, 这个过程中有很多不可控的因素,如果没有登记怎么办?人把钥匙丢了怎么办呢? 产品设计 引入机器&am…

UDP网络编程

UDP和TCP 前几节我们提到了计算机网络编程中的TCP编程,TCP和UDP都是计算机机网络通信的传输层中的传输协议,今天我们来学习计算机网络编程中的基于UDP传输协议的网络编程 首先我们要了解TCP和UDP的区别 它们是同属于计算机网络传输层的传输协议 TCP&…

LeetCode24两两交换链表中的节点 带有输入和输出的完整程序

题目:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1: 输入:head [1,2,3,4] 输出…

CSAPP学习笔记——虚拟内存(一)

虚拟内存的能力 它将主存看成是一个存储在地址空间的高速缓存,在主存中只保存活动区域,并更具需要在磁盘和主存之间来回传送数据,通过这种方式来高效使用主存(DRAM)它为每个进程提供了一致的地址空间,从而…

python虚拟环境与环境变量

一、环境变量 1.环境变量 在命令行下,使用可执行文件,需要来到可执行文件的路径下执行 如果在任意路径下执行可执行文件,能够有响应,就需要在环境变量配置 2.设置环境变量 用户变量:当前用户登录到系统,…

【LeetCode】剑指 Offer 07. 重建二叉树 p62 -- Java Version

题目链接:https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/ 1. 题目介绍(07. 重建二叉树) 输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的…

回溯算法问题汇总

文章目录模版一. 组合问题77. 组合216.组合总和III17.电话号码的字母组合39. 组合总和40.组合总和II131.分割回文串93.复原IP地址78.子集90.子集II491.递增子序列46.全排列47.全排列 II332.重新安排行程51. N皇后37. 解数独模版 void backtracking(参数) {if (终止条件) {存放…

Webpack(通俗易懂介绍)

主题:为什么需要webpack,用来干什么? 前言 例如:前端不断的技术更新迭代,为了浏览器更好的兼容到以及项目更好的开发,所有才有需要Webpack来打包代码,本文介绍下,Webpack的定义。 …

无FTTR不千兆,华为星光F30让家中不再有“隐秘的角落”

“恒有二者,余畏敬焉。位我上者,灿烂星空;道德律令,在我心中。”康德《实践理性批判》中,将人对外部世界的探索精神,抽象成了对无尽星空的追逐。以前,光为我们照亮现实世界。现在,人…

vue使用axios发送post请求携带json body参数,后端使用@RequestBody进行接收

前言 最近在做自己项目中,做一个非常简单的新增用户场景,但是使用原生axios发送post请求的时候,还是踩了不少坑的。 唉,说多了都是泪,小小一个新增业务,在自己前后端一起开发的时候,硬是搞了好…

使用QQ聊天机器人上传每日健康日报【Nonebot插件教程】

文章目录前言一、需求分析1.功能需求2.技术需求二、流程分析1.分析请求过程2.分析代码编写过程四、代码编写前言 作为2020级入学的大学生,在疫情的笼罩下步入了大学的校门,到校第一件事就是接到了每日进行健康日报身体情况上报的通知,每日醒…

08 OpenCV腐蚀、膨胀与形态学运算

1 腐蚀 腐蚀操作是一种形态学操作,它用于缩小二值图像中的对象,并去除图像中的噪声和细节。其基本原理是将图像中的每个像素与内核进行比较,如果内核覆盖的区域内所有像素值都为非零值,则该像素保持不变;否则&#xf…

django+vue项目搭建,前后端通信打通,

文章目录django 环境搭建1.创建django骨架项目2.创建应用3.试着启动项目,验证环境OK4.基础配置admin.pysettings.py前端项目搭建1.安装vue-cli2.创建前端项目3.创建时候选什么前端项目结构页面上呈现的内容是怎么来的?这里只说明vue部分曲线救国打通vue和…

Linux软件管理YUM

目录 yum配置文件 创建仓库 yum查询功能 yum安装与升级功能 yum删除功能 yum仓库产生的问题和解决之道 yum与dnf 网络源 YUM就是通过分析RPM的标头数据后,根据各软件的相关性制作出属性依赖时的解决方案,然后可以自动处理软件的依赖属性问题&…

1.1 什么是并发

1.1 什么是并发 并发:指两个或更多独立的活动同时发生。并发在生活中随处可见。我们可以一边走路一边说话,也可以两只手同时做不同的动作。 1.1.1 计算机系统中的并发 当我们提到计算机术语的“并发”,指的是在单个系统里同时执行多个独立…

零入门kubernetes网络实战-15->基于golang编程实现给ns网络命名空间添加额外的网卡

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本篇文章主要是想通过golang编程来实现,为veth pair链接的网络命名空间添加网卡,配置veth pair的IP 即,使用代码创建一…

002 常见量化交易平台使用

常见的量化交易平台:米筐,BigQuant,优矿,聚宽,掘金。 本文简单介绍其中的米筐量化交易平台。米筐支持Python,Java编写交易策略进行回测。 一、平台使用 1. 注册账号 平台网址:米筐量化平台 平…