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

Linux进程概念

前言

博主学完linux之后,站在完整的体系上,重新回顾进程的概念,有了不少新的理解与体会。

本文将介绍简单的硬件设施,冯诺依曼体系结构引入进程概念。同时将介绍进程的底层概念PCB字段、常见查看进程状态的方式,OS指导下的进程状态,以及linux下进程的状态。

冯诺依曼体系结构

 冯诺依曼体系结构是当今最普遍的硬件结构。

各部分的描述:

输入设备:显卡、键盘、鼠标、摄像头、ssd等等。

输出设备:显示屏、扬声器、网卡等等。

存储器:就是常见的内存,在硬件上常常又叫做内存条。

中央处理器(CPU):由运算器和控制器构成。

关于这些硬件,建立一个认知

冯诺依曼体系结构种的所有硬件设置,都有存储数据的能力。只不过是大小的区别。

离CPU越近的硬件,拷贝速度越快。

 实际上,所有的IO数据都必须先被加载到内存中,再由CPU运算。

那么如果CPU直接去调用IO,必然会受到IO效率的影响。将CPU的速度下降几千倍。所以CPU之和内存打交道,所有数据必须先被加载到内存中,才能被CPU处理

这就好比木桶原理:IO设备是最短的木板,CPU是最高的木板,所以装水量取(计算速度)决于CPU。

硬件存储数据的效率:CPU>内存>输入和输出设备

所以再回过头来,什么是冯诺依曼体系结构?
冯诺依曼体系结构规定硬件的组成是:输入设备,输出设备,内存,CPU(中央处理器)。

CPU只和内存打交道,数据必须先被加载到内存才能被处理。这样的好处:可以利用较低的成本,组装出一台效率还不错的计算机

举例:描述在双方主机利用软件聊天时的数据流动


管理的概念

linux内核下必然存在大量的硬件,比如显卡、网卡、显示器等等。一套尽然有序的计算器,必然是需要一个管理者将这些零碎的部件组织起来的。就像一个学校,被分为各种小班级,对每个班级管理好了,也就能对所有的学生管理好。

先描述:

硬件属于最底层的,我们要想知道这个硬件是什么?靠的是外界对硬件属性的描述。

比如一个人,你对他的描述(男,18岁,学生)。

所以要对一个事物的管理就需要先进行描述对象。

在语言层面对事物的描述,就是一个struct结构体,结构体包含事物属性的字段。

//简单的描述一个人
struct Person(){string name ,int age,int birthday 
};

通过结构体的形式描述对象的属性就能清楚的认识一个事物

因此,就能将各个硬件设备描述起来。


再组织

然而只有描述某一个同学是不够的 ,为了管理的井然有序,这位同学必然属于某个班级,同时这位也能属于某个社团。

这一个将同学划分区域的方法,就叫做组织。

对应语言层次:无非就是用特定的数据结构,将一个数据放入。

然后就能对数据进行增删查改!

常见的组织方式:链表、哈希表、vector

管理的核心!

先描述,再组织。是一种哲学的指导思想 

操作系统管理的核心模块

  • 进程管理
  • 内存管理
  • 文件管理
  • 驱动管理

操作系统

之前谈到的管理者就是操作系统。

操作系统就是硬件资源的管理者。并且为用户提高一个稳定的运行环境。

仔细看这张图

最底层是由硬件构成,而在硬件之上是由各种驱动对硬件辨识。对硬件进行控制。驱动的好处就是能够将不同型号的硬件适配。

而在驱动层之上就是四个管理层,也就是操作系统 。所以说操作系统就是对硬件资源的统一管理。

理解为用户提供稳定的运行环境

操作系统是对底层硬件的管理。实际上,操作系统是不信任用户的,就是决定,操作系统不会暴露、公开直接管理的数据。但是如果用户依旧想访问外设呢?比如往显示屏上写数据,但是又不能直接操作外设,只能通过操作系统去替我们间接访问外设。

这一个过程就是系统调用,通过系统调用去访问硬件设备,才能保证OS内的稳定安全!

什么是操作系统?

管理硬件资源,并且为用户提供一个安全稳定的运行环境。

而在系统调用之上,还存在一层用户操作。

这一层存在的目的是:有时候系统调用并不方便,因此就会封装出第三方库,比如C语言的fopen就必然封装了 系统调用open。

因此更换这一层就会产出不同的shell外壳:ubantu、CentOS等


进程概念 

什么是进程


比如我们在磁盘上创建一个test.cc文件,通过编译链接之后生成可执行文件,这就是程序。

当我们要运行的时候,由于冯诺依曼体系结构规定,必须要先加载到内存中。而在内存中必然会存在大量的从磁盘拷贝而来的可执行程序,对这些程序的管理。就是先描述,再组织!

描述进程属性的结构就是PCB(进程控制块)。然后利用特定的双向链表将PCB组织起来,这就是进程。

进程=代码数据(可执行程序)+PCB

进程控制块

OS对内存中大量可执行程序的管理是通过先描述,再组织的方式。进程控制块PCB就是描述可执行程序的结构体。在Liunux系统下的进程控制块叫做task_struct。

task_struct包含进程的描述字段,比如:id,代码地址,数据地址,进程状态,优先级,next指针等等。所以通过task_struct就能找到对应的代码+数据。

对进程的管理就转化为对task_struct的管理。

见一见linux内核0.11下的task_struct

struct task_struct {
/* these are hardcoded - don't touch */// 进程状态long state;	/* -1 unrunnable, 0 runnable, >0 stopped */// 计数器long counter;// 进程优先级long priority;// 进程获取的信号long signal;struct sigaction sigaction[32];// 进程阻塞的信号 bit maplong blocked;	/* bitmap of masked signals */
/* various fields */// 退出状态码int exit_code;unsigned long start_code,end_code,end_data,brk,start_stack;long pid,father,pgrp,session,leader;unsigned short uid,euid,suid;unsigned short gid,egid,sgid;long alarm;long utime,stime,cutime,cstime,start_time;unsigned short used_math;
/* file system info */int tty;		/* -1 if no tty, so it must be signed */unsigned short umask;struct m_inode * pwd;struct m_inode * root;struct m_inode * executable;unsigned long close_on_exec;struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */struct desc_struct ldt[3];
/* tss for this task */struct tss_struct tss;
};


获取进程标识符

  • getpid()获取进程的pid
  • getppid()获取父进程的pid

创建一个进程方式有俩种:

  • 通过执行exe文件
  • fork创建子进程

而这些进程都是由父进程创建的,比如执行exe就是bash进程(命令行解释器)创建的子进程,

fork不用说,就是由当前进程创建的。

演示:

#include<iostream>
#include<unistd.h>
using namespace std;int main(){while(1){std::cout<<"我是一个进程!我的pid是:"<<getpid()<<" ppid:"<<getppid()<<std::endl;sleep(1);}return 0;
}

这个3395584进程就是bash


查看进程

查看进程的方式有俩种

  • ps 
  • ls /proc

创建一个简单的进程,并且运行起来

演示:

ps -ajx 查看进程的状态

通过管道 抓取指定 test进程的状态

ps -ajx | grep test


将头部信息也显示 

 ps -ajx | head -1 && ps -ajx | grep test

这时候调用grep的时候,会生成一个greptest进程,通过-v 选项可以将grep过滤

通过proc查看

实际上,进程的运行的时候,会在 /proc文件中以文件的唯一标识:也就是pid 创建一个文件,文件的内容就是pcb结构体。看一看这个文件。

ls /proc/

果然,在/porc/目录下存在一个以pid为标识的文件。看一下文件的内容:

  • exe就是可执行文件在磁盘中的位置 
  • cwd就是当前进程的位置

这就是进程在运行时候,就能确定的字段,都保存在proc的文件中。

比如调用fopen,就能在当前目录下创建新文件,就是和这个字段有关系。

如果希望修改文件的路径,可以通过chdir系统调用,修改的就是动态文件下的cwd。

这个以pid命名的文件是动态的,一旦关闭进程,这个动态文件就会自动删除。


fork创建子进程

SYNOPSIS#include <unistd.h>pid_t fork(void);

用于创建子进程

返回值

  • 失败返回-1
  • 对父进程返回子进程的pid
  • 子进程返回0

一个函数有俩个返回值是比较特殊的,后续将进行解释。

演示:

#include<iostream>
#include <unistd.h>
using namespace std;
int main(){std::cout<<"fork之前"<<std::endl;pid_t id=fork();if(id<0){std::cout<<"fork失败!"<<std::endl;}else if(id==0){while(1){std::cout<<"我是子进程,pid:"<<getpid()<<",ppid:"<<getppid()<<std::endl;sleep(1);}}else{while(1){std::cout<<"我是父进程,pid:"<<getpid()<<",ppid:"<<getppid()<<std::endl;sleep(1);}}return 0;
}     

fork之前的语句只会被执行一次,fork之后就会分流,子进程执行id==0的语句,父进程执行id>0的语言

创建子进程的目的:协助父进程完成任务。

关于fork的几个问题

fork的原理

  • 以父进程为模板,创建子进程的PCB,子进程的PCB也会指向代码+数据
  • 共享代码

为什么父进程返回子进程的pid,子进程返回0?

  • 一个父进程可能会有很多的子进程,父进程需要给特定的子进程分派任务就是依靠id来区分。
  • 而子进程只需要知道是否创建成功即可,它只有一个父进程。 

为什么会有俩个返回值?

  • 因为执行完fork的核心代码后,就有俩个执行流。俩个执行流共享代码,都会return。但是数据是不共享的,通过写时拷贝就会返回俩个值。

谁先运行?
不确定,由时间片,优先级和调度算法决定。


进程的状态

见一见操作系统的指导下的进程状态

 重点讨论一下运行状态、阻塞状态和挂起状态

运行状态

所谓的状态无非就是PCB中的一个字段status,#defind RUN=1 #define BLOCK=2 ....这样的形式,而更改进程的状态就是把status的值修改

每一个CPU都会运维一个运行队列,而运行队列就是包含PCB的指针,如果要调度某一个进程,就从运行队列中取数据。

一个共识:

只要在运行队列中的PCB都是准备好的,随时都是可以被调度的,状态都是运行状态

只不过可能是时间片结束,暂时没有被调度。

验证运行状态


阻塞状态 

阻塞状态也是OS中比较常见的状态,比如我们在访问硬件资源的时候,如果数据迟迟没有就绪,那么数据就获取不到,就必须等待。给用户的体验就像是卡住了。

产生阻塞的原因:

资源未就绪,不具备访问的条件。

详解阻塞状态

建立一个认知:

每一个设备都会存在一个等待队列。


挂起状态

如果进程访问的资源没有就绪,进程就会被阻塞等待资源的到来。而如果恰巧OS内的资源已经很少了,OS也快要挂掉了。这时候,被阻塞的资源不仅没有执行任务,反而占用内存资源。就有理由将该进程的代码+数据先移动到磁盘中。

产生的原因:

进程被阻塞了,并且OS内的资源严重不足,就会产生挂起!所以就称为阻塞挂起。

什么时候加载回来?
等到进程被OS调度的时候了,就会加载回来。


Linux内核下进程的状态

来见一见linux内核0.11下的进程状态

/*
* 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 */
};

  • R状态:对应的就是OS中的运行状态,在Linux下存在R和R+状态,都代表运行状态,不同的是R+状态表示的是以前台进程运行,在运行的时候添加 &选项表示后台进程运行。
  • S状态:休眠状态,浅度休眠,可以被信号终止。
  • D状态:深度休眠,不能被信号终止。

S和D有什么区别?
本质都是阻塞状态,D状态又叫做磁盘阻塞。当用户往程序写入重要的数据的时候,如果当OS内存不足时候,OS会杀掉进程,但是这个写入是重要内容,不能被杀掉,所以就将进程设置为D深度休眠状态。

  • T:暂停状态,通过向信号发送信号SIGSTOP将进程暂停,发送SIGCONT将进程继续运行。
  • t:追踪状态,进程被gdb调式,就会阻塞住等待gdb的指令,这个等待期间,状态就是t。
  • X:死亡状态,进程终止之后,父进程释放pcb资源,进程正真意义上的终止,就是一瞬间的状态。
  • Z状态:进程终止后,会释放代码和数据,但是PCB内的资源暂时不会被释放。是为了告知父进程读取退出的原因,而父进程迟迟没有读取PCB资源,这一过程进程处于僵尸状态。

如果进程进入僵尸状态,而父进程一直不读取pcb资源,就会造成OS内存在大量的PCB资源,就会造成内存泄漏

孤儿进程:

子进程都是由父进程创建的,子进程退出时,PCB资源由父进程读取。如果父进程提前退出,而子进程结束后,资源由谁读取?

OS内规定,如果父进程提前退出,子进程就会成为孤儿进程,由1号进程(bash)领养。


下文将介绍进程概念的重点:进程优先级,O(1)调度算法,环境变量,以及进程地址空间等等。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • vue 使用jszip,file-saver下载压缩包,自定义文件夹名,文件名打包下载为zip压缩包文件,全局封装公共方法使用。
  • Wni11 下 WSL 安装 CentOS
  • vue+el-table 可输入表格使用上下键进行input框切换
  • 【C语言】结构体超详细全讲解 (代码+万字文字+画图讲解)
  • Linux 大文件和大量小文件的复制策略
  • 常见SQL整理
  • SprinBoot+Vue药房管理系统的设计与实现
  • 存储型XSS漏洞
  • Linux系统flatpak的简单设置
  • 牛客周赛 Round 58(上)
  • 文心快码前端工程师观点分享:如何保证在企业内落地?
  • 【数学建模】2024数学建模国赛经验分享
  • 无线会议解决方案
  • 石头科技闪耀IFA百年展:斩获多项大奖,全球知名媒体一致好评
  • 10个C++绘图案例
  • [数据结构]链表的实现在PHP中
  • 【RocksDB】TransactionDB源码分析
  • Angularjs之国际化
  • HashMap ConcurrentHashMap
  • Iterator 和 for...of 循环
  • jdbc就是这么简单
  • Just for fun——迅速写完快速排序
  • mysql_config not found
  • PHP 的 SAPI 是个什么东西
  • Python十分钟制作属于你自己的个性logo
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • RxJS: 简单入门
  • spring + angular 实现导出excel
  • 开源地图数据可视化库——mapnik
  • 老板让我十分钟上手nx-admin
  • 入手阿里云新服务器的部署NODE
  • 实现简单的正则表达式引擎
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 学习ES6 变量的解构赋值
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 通过调用文摘列表API获取文摘
  • #微信小程序(布局、渲染层基础知识)
  • (06)Hive——正则表达式
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (SpringBoot)第二章:Spring创建和使用
  • (定时器/计数器)中断系统(详解与使用)
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (黑马C++)L06 重载与继承
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • ./configure、make、make install 命令
  • .cfg\.dat\.mak(持续补充)
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • .net dataexcel 脚本公式 函数源码
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • @Async 异步注解使用
  • [ IDE ] SEGGER Embedded Studio for RISC-V
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [ 云计算 | Azure 实践 ] 在 Azure 门户中创建 VM 虚拟机并进行验证
  • [2010-8-30]
  • [2016.7.Test1] T1 三进制异或