Linux基础——进程的概念和控制(操作系统级讲解)

news2025/1/23 7:00:09

前言

我们经常会听到一个概念——进程。但是进程并不是一个孤立的概念,需要对操作系统有比较深入的了解。所以这篇博客将在读者的脑中先对操作系统构建一个大概的印象,再对进程做了解。

冯诺依曼结构

冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置,因此程序指令和数据的宽度相同,如英特尔公司的8086中央处理器的程序指令和数据都是16位宽。

简单来说,数据和指令同时被储存在同一个储存器中。

这里的储存器就是内存,运算器和控制器共同组成了中央处理器,其他都是外设。包括了输入设备:磁盘、键盘、鼠标……,还有输出设备:磁盘、显示屏、音响……

由于内存的掉电易失,刚开机时内存里是空白的,指令和数据只能从磁盘中读取。

CPU(中央处理器),通过读取内存中的指令进行各种各样的数据处理。 体系结构决定了数据和指令只能先被拷贝到内存才能被CPU处理。

那么数据在外设、内存和CPU之间的流动并非不受控制的,那么由谁来控制?

由操作系统来控制。

操作系统

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括: 内核(进程管理,内存管理,文件管理,驱动管理) 其他程序(例如函数库,shell程序等等)。

设计OS的目的

与硬件交互,管理所有的软硬件资源

为用户程序(应用程序)提供一个良好的执行环境

由于直接使用操作系统的接口比较繁琐,而且需要对系统有比较深入的了解,所以使用指令(touch创建文件、rm移除文件等)、编程(cout/cin控制输入输出)来封装系统接口,使系统的操作简洁明了。 

操作系统是怎么管理进行进程管理的呢?很简单,先把进程描述起来,再把 进程组织起来!

进程的管理也满足先描述,后组织的原则。

进程

进程的基本概念

课本概念:程序的一个执行实例,正在执行的程序等

内核观点:担当分配系统资源(CPU时间,内存)的实体。

描述进程-PCB

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。 课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

PCB就是一个自定义的结构体类型。

 在Linux中描述进程的结构体叫做task_struct。 task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_ struct内容分类

标示符: 描述本进程的唯一标示符,用来区别其他进程。

状态: 任务状态,退出代码,退出信号等。

优先级: 相对于其他进程的优先级。

程序计数器: 程序中即将被执行的下一条指令的地址。

内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

上下文数据: 进程执行时处理器的寄存器中的数据。

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 其他信息

 进程 = struct tast_struct结构体对象 + 磁盘中的程序代码

见一见进程

 如图所示是正在运行的程序就是一个进程,但是我们怎么看到进程的属性呢?

使用 ps  axj :

 杀死一个进程

使用 kill -9 [进程ID]:

 系统接口getpid

getpid是用来获取当前进程的ID的系统接口。

 

 内存级的目录文件proc

 而且在进程运行时删除磁盘上的代码,进程仍然照常运行。只是目录中的exe开始报警:

命令行中启动的进程,父进程都是bash。

创建子进程

使用fork创建子进程:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("创建子进程失败:");
    }
    else if(id > 0)
    {
        printf("这是一个父进程 pid: %d\n",getpid());
    }
    else
    {
        printf("这是子进程 pid: %d\n",getpid());
    }

    return 0;
}

 

进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)。

下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

 

 一个cpu有一个运行队列,队列中都是进程控制块。

让进程处于运行状态,本质上是让某一个进程的进程控制块(PCB)被插入到运行队列等待占用CPU的资源。

并不是只有运行队列,由于进程会有别的状态,自然也会在别的队列下等待,比如说等待外设资源时处于阻塞状态,这个进程就不在运行队列,而处在等待外设的队列。

进程的状态不同,本质是PCB为了占用不同资源处在不同队列中。

以上我们知道了进程的运行状态是进程在运行队列中等待占用CPU资源,阻塞状态是进程在阻塞队列下等待外设资源,接下来我们讲一下挂起状态

挂起状态

挂起就是将进程的代码和数据暂时存放到磁盘中。

阻塞不一定挂起,挂起一定阻塞。

挂起也有可能与其他状态组合,在其他队列中时代码和数据被存放到磁盘中。 

进程的不同状态

R

R (runing)运行状态

#include<stdio.h>  
   
int main()  
{  
    int a = 0;  
    while(1)  
    {  
        a = 1 + 1;
    } 
    return 0;                                                                                                              
}  

可以看出,这时只占用cpu资源,进程状态时R。

 S

S (sleeping) 浅度睡眠

对程序做一点改变:

 这时,由于进程等待显示器资源的时间长于等待cpu的资源,大多数时间进程的状态都是S,是一种阻塞状态。

 等待外设或者sleep,都是S状态。

 T

T (stop) 暂停状态

 先来看看kill的操作:

T状态是暂停:

使用 kill -19  进程号,就可以使得进程变为T状态,这是一种暂停状态。

 使用 kill -18  进程号,就可以使得进程变回原来的状态。

 但是这是S后面的加号没有了,并且这时ctrl + C已经不能使程序结束了,这说明进程已经变为了后端进程,只能直接用kill -9杀死。

 D

D (disk sleep) 深度睡眠

在高IO的情况下,资源紧张,操作系统为了合理利用资源会将一些进程变为深度睡眠状态,该状态的进程无法被杀死,只能通过断电或者进程自己醒来解决。

t

t (tracing  stop)  被追踪的暂停状态

 

 我们可以看到,在使用gdb打断点后,进程运行到断点之后停止,这个状态就是被追踪的暂停状态。

Z

Z (zombie) 僵尸状态

当进程结束时,并不能马上释放进程控制块,因为这时候父进程和操作系统不知道,需要父进程接收之后,子进程的状态被父进程和操作系统所知,才能释放。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("这是子进程: pid : %d  ppid : %d\n", g    etpid(),getppid());
        sleep(5);
        exit(1);
    }
    else
    {
        while(1)
        {

            printf("这是父进程: pid : %d  ppid : %d\n    ", getpid(),getppid());
            sleep(2);                                 
        }
    }
    return 0;
}

 

 我们明显看到,子进程结束后,由于父进程没有及时接收处理,子进程变成僵尸状态,进程的僵尸状态也会造成内存的泄露。

X

X (dead)  死亡状态

僵尸进程和孤儿进程

僵尸进程

像上述一样,由于子进程结束,并且父进程并没有及时接收子进程,导致子进程的数据没有被及时释放。所以说,僵尸进程是一个问题,僵尸进程造成内存泄漏等。

孤儿进程

孤儿进程是由于父进程先一步结束,这时子进程被称为“孤儿进程”。

孤儿进程被1号init进程领养,由init进程回收。

先写一个程序:

 #include<stdio.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<stdlib.h>
 int main()
 {
     pid_t id = fork();
     if(id == 0)
     {
         while(1)
         {

             printf("这是子进程: pid : %d  ppid : %d\n", getpid(),getp    pid());
             sleep(1);                                                 
         }
     }
     else
     {
 
             printf("这是父进程: pid : %d  ppid : %d\n", getpid(),getp    pid());
             sleep(3);
             exit(1);
     }
 }

在三秒钟后,父进程死亡,留下子进程被系统进程收养。

 从前台进程变为后台进程

进程优先级

权限决定了能不能做,优先级是已经可以做这件事,只是决定了这件事什么时候做。

由于资源太少,需要确定优先级,谁先谁后,在Linux中优先级用整型编号表示。

 其中,priority表示权限。

在上图中是80.

最终优先级 = 老的优先级(80) +  nice(-20 ~ 19)

 上图是我改进程的优先级的过程,由于修改优先级需要权限,第一步使用: sudo top

然后键入 r  ,想要修改的进程的pid,  最后输入nice值。

进程的其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级

独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发

进程切换

 特别值得注意的是,在进程被剥离时。cpu中寄存器中属于这个进程的数据保存到pcb中,当进程恢复运行时,再把数据拷贝到cpu中的寄存器上。

环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

我们自己编译生成的可执行程序和系统中的指令是一样的,但是为什么我们运行自己的可执行程序时在前面要带上路径,而指令不需要?因为指令的路径系统会自动去找,指令安装的过程其实就是一个拷贝到特定路径下的过程。

 把我们的可执行程序放到指令路径下,会污染指令池,不建议那么做。

还有一种方法,将当前路径设置为系统默认路径,使用指令:

export PATH=$PATH:想要添加的路径

 

 在图中的.bash_profile以及.bashrc,加上/etc/bashrc,使得系统执行时环境变量被自动定义和导入。

我们所改变的是在内存中的环境变量,在系统重启之后一切恢复原样。

env指令可以查看所有环境变量。

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

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

相关文章

微信小程序|智能停车系统中车牌计费功能实现

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

数据结构基础篇》》约瑟夫环

数据结构开讲啦&#xff01;&#xff01;&#xff01;&#x1f388;&#x1f388;&#x1f388; 本专栏包括&#xff1a; 抽象数据类型线性表及其应用栈和队列及其应用串及其应用数组和广义表树、图及其应用存储管理、查找和排序将从简单的抽象数据类型出发&#xff0c;深入浅出…

python 基础入门

文章目录前言python 基础入门一、python环境如何搭建、开发工具pycharm如何破解01 python下载02 python 安装03 python开发工具安装(pycharm )03::01 安装pycharm03::02 多次试用二、python 常规基础01 python 规范02 python中的关键字03 python缩进04 python注释哈哈哈前言 如…

07-JVM 类加载机制?

1.JVM 类加载机制分为五个部分&#xff1a;加载&#xff0c;验证&#xff0c;准备&#xff0c;解析&#xff0c;初始化。 2.一个类型从被加载到虚拟机内存中开始&#xff0c;到卸载出内存为止&#xff0c;它的整个生命周期将会经历加载&#xff08;Loading、验证&#xff08;V…

分布式版本控制Git

从基本的环境配置与安装到Git的基本操作&#xff0c;轻松应对Git在使用时遇到的常见问题。 https://blog.csdn.net/a18307096730/article/details/124586216?spm1001.2014.3001.550202_版本控制器的方式03_svn(过时)_git04git工作流程简述05git环境配与安装06 获取本地仓库Git…

P3375 【模板】KMP字符串匹配

题目描述 给出两个字符串 s_1s1​ 和 s_2s2​&#xff0c;若 s_1s1​ 的区间 [l, r][l,r] 子串与 s_2s2​ 完全相同&#xff0c;则称 s_2s2​ 在 s_1s1​ 中出现了&#xff0c;其出现位置为 ll。 现在请你求出 s_2s2​ 在 s_1s1​ 中所有出现的位置。 定义一个字符串 ss 的 bor…

概率论【离散型二维变量与连续性二维变量(上)】--猴博士爱讲课

5.离散型二维变量与连续性二维变量&#xff08;上&#xff09; 1/8 已知二维离散型分布律&#xff0c;求??? 离散型直接看表 【做题方法参考如下】 2/8 已知二维离散型分布律&#xff0c;判断独立性 如果满足p(xy) p(x) * p(y)&#xff0c;那么相互独立 则我们只需要验证每…

C 程序设计教程(12)—— C 语言顺序结构程序设计

C 程序设计教程&#xff08;12&#xff09;—— C 语言顺序结构程序设计 该专栏主要介绍 C 语言的基本语法&#xff0c;作为《程序设计语言》课程的课件与参考资料&#xff0c;用于《程序设计语言》课程的教学&#xff0c;供入门级用户阅读。 目录C 程序设计教程&#xff08;1…

深入探索Flutter性能优化

前言 Flutter 作为目前最火爆的移动端跨平台框架&#xff0c;能够帮助开发者通过一套代码库高效地构建多平台的精美应用&#xff0c;并支持移动、Web、桌面和嵌入式平台。对于 Android 来说&#xff0c;Flutter 能够创作媲美原生的高性能应用&#xff0c;但是&#xff0c;在较…

【nvivo11plus教程】02_编码与节点

1、对文档进行编码(1)建立节点(2)使用快速编码栏进行编码(3)将整个文件编码为一个代码(4)范围编码(5)在vivo中编码(6)使用节点昵称加快编码速度2、取消、增加和查看编码(1)编码带(2)删除编码(3)查看编码邻近区(4)增加编码(5)查看编码信息3、组织节点(1)节点结构化(2)移动归类节…

leetcode-每日一题-还原排列的最少操作步数(中等,数学逻辑)

回家了很少看了&#xff0c;今天突然心血来潮做了今天的每日一题&#xff0c;还不错&#xff0c;最后是一次AC&#xff0c;说明这么长时间没看实力没有下降多少&#xff0c;哈哈哈哈&#xff0c;自恋一下&#xff0c;后面我会更新一些课设和实验作业&#xff0c;进入正题。给你…

密码学_ECC椭圆曲线加密算法

算法介绍 椭圆加密算法&#xff08;ECC&#xff09;是一种公钥加密体制&#xff0c;最初由Koblitz和Miller两人于1985年提出&#xff0c;其数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性。公钥密码体制根据其所依据的难题一般分为三类&#xff1a…

【jQuery】常用API——jQuery样式操作

一、操作 css 方法 jQuery 可以使用 css 方法来修改简单元素样式。1. 参数只写属性名&#xff0c;则是返回属性值$(this).css(color);2. 参数是属性名&#xff0c;属性值&#xff0c;逗号分隔&#xff0c;是设置一组样式&#xff0c;属性必须加引号&#xff0c;值如果是数字可以…

Go基础学习

文章目录回看下历史环境安装和开发工具&#xff1a;基础语法&#xff1a;go的注释&#xff1a;变量定义&#xff1a;简短定义模式Go的变量交换匿名变量&#xff08;空白标识符&#xff09;&#xff1a;变量的作用域&#xff1a;iota常量计数器数据类型布尔类型数值型整数浮点数…

新冠COVIN-19流感病患轨迹追溯

实验背景 冬季是流感的高发季节&#xff0c;现已知某流感病毒的传播力很强&#xff0c;政府部门也陆续公开了部分流感确诊患者&#xff08;后续简称“病患”&#xff09;的非隐私信息&#xff0c;这部分数据为相关研究人员研究该流感病毒的传播与防控提供了重要的数据支撑。 然…

Linux网站服务实操练习

作者简介&#xff1a;一名99年软件运维应届毕业生&#xff0c;正在自学云计算课程。宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。创作不易&#xff0c;动动小手…

python:打包package

简介 把模块打包成package&#xff0c;可以进行分发和安装。 packaged的打包和安装一、package层次架构二、 package的打包和安装1. 创建setup.py2. 打包package3. 安装package一、package层次架构 其中mypackage为进行打包的文件夹&#xff0c;文件夹下包含多个脚本&#xff1…

钢铁行业应用APS生产排产系统的好处

1 钢铁行业APS生产排产系统设计的主要业务流程 全局一体化计划&#xff1a;主要负责订单交期评审与应答、销产转换、主生产计划、铁水需求计划&#xff0c;该计划的最终目标是对各个分厂的日计划提出整体要求。主要对口业务部门为公司生产计划排程部门。 各个工段厂区的一体化…

SpringCloud微服务项目实战 - 4.自媒体平台(博主后台)

“我读过很多书&#xff0c;但后来大部分都忘记了&#xff0c;你说这样的阅读究竟有什么意义&#xff1f;” “当我还是个孩子时&#xff0c;我吃过很多食物&#xff0c;现在已经记不起来吃过什么了。但可以肯定的是&#xff0c;它们中的一部分已经长成我的骨头和肉。” 系列文…

LaoCat带你认识容器与镜像(三【上】)

有道是每逢佳节倍惰怠 ~&#xff0c;春节期间随缘更新吧 ~ 本章内容 Docker挂载数据卷相关。 本文实操全部基于Ubuntu 20.04 宿主机 > linux服务器本身 前边章节就介绍过Docker数据卷相关的知识点&#xff0c;也特别强调了生产环境一定要记得挂载数据卷&#xff0c;编程的小…