【Linux】进程概念

1.冯诺依曼体系结构

1.存储器指的是内存 . 磁盘或U盘一般对应的是输入输出设备,是外存
2.外设 :

输入设备:鼠标,键盘,摄像头,话筒,磁盘,网卡...

输出设备:显示器,播放器硬件,磁盘,网卡
3.中央处理器CPU :

运算器:对数据进行计算任务(算数运算,逻辑运算)

控制器:对计算机硬件流程进行一定的控制
总结 :

各硬件都是独立的个体,各个硬件单元必须用"线"连接起来才能发挥作用,线即总线,cpu和内存交互的总线叫系统总线,内存和外设输入输出设备间的总线叫IO总线

1.1数据传输

数据是从输入设备输入到存储器(内存)当中,再交由cpu进行计算,完后返回传输给存储器,最后由存储器传输到输出设备.

三者的(访问/交互)时间存在巨大差距:

纳秒级别:CPU(0.1~10ns)、内存(50--200ns);

微秒级别:SSD(10~1000μs)、部分高速外设;

毫秒级别:机械硬盘(5~20ms)、低速外设(如鼠标、键盘响应)

为什么不去掉内存,让外设直接与cpu进行交互?

认为内存和cpu往返交互多了两个过程所以这样并没有提升多少效率,但并非如此.

1.若去掉内存,整机运行数据是串型的,而cpu和外设的操作时间太多数量级,导致cpu运行数据结束后得花远超计算所需的时间去等待外设数据的传输,从而大大降低了整机的运行效率

2.若加上内存运行,外设中的数据可预加载到内存中,内存可以在外设加载数据的同时进行其他数据的运算,其他运算完成后就可以处理刚加载的数据,也就是加载数据可以同时进行运算,整机的数据处理是并行结构的,整机效率就以cpu的运行效率为主

总结:

1.存储器(内存)是硬件级别的缓存空间,在冯诺依曼体系结构中处于核心地位

2.这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)

外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。一句话,所有设备都只能直接和内存打交道。

3.这就决定了一个程序要运行,必须线加载到内存中运行;并且运行的结果也会在内存中缓存起来,是否显示取决于是否立马刷新出来,如进度条小程序

事例帮助理解:(忽略网络方面的知识)

1.登录软件与别人聊天

a:你的机器是冯诺依曼体系结构,首先你的软件会加载到内存中去,发送信息是从你的输入设备键盘上读取信息到内存,再交由cpu进行打包信息的计算返回给内存,最后由内存发送信息到输出设备到网卡中(顺便在显示器上打印这样自己能看到),再通过网络将网卡中信息传输给另一台冯诺依曼机器

b:另一台冯诺依曼机器的输入设备是网卡,将你从网卡传输过来的信息加载到内存中去,再交由cpu进行解包运算返回给内存,最后传输给输出设备显示器上显示对话框.

2.文件的传输过程

比如在软件上想将文件传输给别人,也是按照冯诺依曼体系结构来传输,与上文聊天的操作基本相同,只是文件发出者的输入设备变成磁盘,接收者的输出设备变成磁盘,总结来看就是有一个磁盘发送到另一个磁盘的过程

2.操作系统

概念:

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

内核(进程管理,内存管理,文件管理,驱动管理)

其他程序(例如函数库,shell程序等等)

这是宏观上的说法,一般微观上仅指的是内核

是什么:

操作系统是一款进行管理的软件

为什么需要:

1.帮助用户管理好下面的软硬件资源

2.为了给用户提供一个稳定,高效,安全的运行环境

二者结合,通过1的手段,达成2的目的

系统调用和库函数概念:

1.操作系统内部会有各种数据.可操作系统不相信任何用户;

2.操作系统为保证自己的数据安全,也为了保证给用户能够提供服务,操作系统会以接口的方式给用户提供调用的入口,来获取操作系统内部的数据.这部分

由操作系统提供的接口,叫做系统调用

3.系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

ps:所有访问操作系统的行为,都只能通过系统调用完成.所有开发语言都建立在系统调用接口之上

总结:

库函数和系统调用,是上下层调用和被调用的关系.系统调用是内核提供的底层接口,库函数是面对开发者的易用封装,库函数的底层是否为系统调用取决于功能是否需要内核资源

2.1如何进行管理

有管理这个行为,就有管理者,执行者和被管理者,分别对应操作系统,驱动程序,软硬件资源.具有以下结论

1.管理者和被管理者是不需要见面的

2.在不见面情况下,管理者只要能够得到管理信息就可以在未来进行管理决策

3.管理者可以通过执行者拿到对应的数据

4.管理的本质:通过对数据的管理达到对人的管理

先描述,再组织

1.进行管理首先要描述对象的信息,Linux操作系统主要由C语言实现,就用struct结构体来描述,我们对人和事物的认识都是通过属性认识的

2.再将这些信息用合适的数据结构管理起来,这样管理对象就变成对某种数据结构的增删查改,本质是管理每个进程的属性

所以操作系统中注定存在大量的数据结构

3.进程

基本概念:

1.一个已经加载到内存中的程序,叫做进程

2.正在运行的程序,叫做进程

理解:

1.一个操作系统不仅仅只能运行一个进程,可以同时运行多个进程

2.操作系统必须将进程管理起来--先描述,再组织

3.任何一个进程,在加载到内存的时候,形成真正的进程时,操作系统要先创建描述进程的结构体对象 --PCB(process ctrl block)进程控制块.PCB就是进程属性的集合,也是认识和区分进程的依据,Linux中本质是一个struct结构体来存储进程相关属性信息.存在于操作系统中被维护
进程=内核PCB数据结构对象+自己的代码和数据

有PCB才能称为进程,光有代码和数据不算.就好比在学校中要有你的学生信息才能称为学生,只有人在学校不能称为学生,比如保安.
操作系统中只需对PCB进行管理,PCB中具有字段帮助操作系统找到对应的代码和数据来执行,也就是具有相关的指针信息.在操作系统中对进程进行管理,变成了对单链表进行增删查改的管理.

3.1描述进程

Linux操作系统下的PCB是task_struct,是PCB的一种,不同操作系统间叫法不同.

task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。这就是通过属性创建一个对象的过程,也就是面向对象的过程

task_ struct属性分类:

1.标示符:

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

2.状态:

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

3.优先级:

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

4.程序计数器:

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

5.内存指针:

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

7.上下文数据:

进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。

8.I/O状态信息:

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

9.记账信息:

可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。每个进程的处理时间基本要求一致这是判断cpu优劣的一个标准

其他信息

3.2Linux是如何做到管理的

task_struct结构体中里面包含进程的所有属性,要管理好进程只要管理好task_struct结构体

task_struct最基本组织进程的方式是采用双向链表组织的,但不仅仅是双向链表,因为运行多进程运行,可能采取多种数据结构共同管理.

它要处理什么工作取决于放在哪种数据结构里,数据结构底层对应着不同的算法,不同算法又对应着不同的应用场景

3.3查看进程

ps指令:查看进程状态

作用是显示进程信息的表头

a:显示所有终端上的进程。j:以作业格式显示进程信息。x:显示没有控制终端的进程。

head -1:显示前 1 行输出。

各列含义:都作为属性存储在PCB中(task_struct)

PPID:父进程 ID(Parent Process ID)。

PID:进程 ID(Process ID)。

PGID:进程组 ID(Process Group ID)。

SID:会话 ID(Session ID)。

TTY:进程所属的终端(Teletypewriter)。

TPGID:前台进程组 ID(Terminal Process Group ID)。

STAT:进程状态(Process State)。

UID:用户 ID(User ID)。

TIME:进程占用的 CPU 时间(CPU Time)。

COMMAND:进程的命令或程序名(Command)。
指令ps可以查到所有进程信息,本质是遍历双向链表将所有属性信息格式化打印出来
可以两条指令连续执行,先打印进程信息表头,再利用grep来筛选进程信息,中间&&说明两条指令都必须执行成功,也可以用';'分号来代替

所有指令会加载到内存中以进程的方式去运行,只不过这些指令运行的很快运行完直接被调度;

而grep是用来关键字过滤的,先把自己变成进程运行起来再通过执行代码进行过滤,但grep关键字里本来就有proc,所以在过滤所有进程时把自己即proc也带上了,这就是最后一行打印的原因,侧面证明了所有指令运行时都是进程,若不想显示 ,grep proc | grep -v grep反向过滤指令即可

  • kill杀进程

指令kill -9 PID 就可以结束运行一个进程,-9是强制终止进程信号,常用于处理无响应进程或紧急释放资源的情况.

ls /proc指令在系统文件夹查看进程信息

关机之后上面的进程信息会清空,但开机之后操作系统会自动帮忙创建这些目录或文件.

这上面的所有信息是Linux操作系统用文件系统的方式把内存中的进程信息可视化出来了上面的数据全是内存级的

白色字体都是普通文件,蓝色字体是目录,目录的特点全部按数字定义,数字代表每个进程的PID,所以所有在系统中默认运行的进程会在/proc目录中创建一个以PID命名的文件夹或目录,保存了该进程的大部分属性

可以通过上述Test进程的PID来查看目录
1.如果此时关闭Test进程那么目录将自动被清理,若重新启动Test进程PID会发生变化,一般不会不变.

2.利用-l查看文件具体信息可发现其中有一个exe可执行文件路径,该路径为当前运行进程对应的可执行程序所处的位置.所以进程是能找到自己的可执行程序

3.cwd(current work dir)代表当前工作目录:

进程在启动时PCB属性会记录当前所在的目录的绝对路径作为工作目录(即为当前目录),在创建文件时就会默认在已经记录的当前目录下创建,不需要手动加当前路径

3.4通过系统调用获取进程标示符

获取自己的PID

是一个系统调用接口,可用man 2 getpid在手册查看,谁调用我就获取谁

可打印出自己的进程pid,进程pid只在运行期间有效,重新启动进程时可能发生变化

获取自己的PPID

Shell 脚本命令,它会周期性地显示包含 proc 的进程信息,具体解释如下:

  1. while :; do ... done
    while :; do:这是一个无限循环,: 是一个空命令,返回值为 0,因此循环会一直执行。done:循环结束的标志。




    自己获取和打印的信息相同,证明getppid的作用,经过反复终止进程,重新启动进程发现pid一直发生变化而ppid不变


    当我们运行一个进程时,命令行解释器会把当前指令解释成bash的子进程,由子进程执行对应的命令,而一旦这个子进程出现问题并不影响我们的bash进程.

1.当我们在命令行中输入各种指令时执行的都是bash的子进程,子进程的ppid是bash的pid

2.重新登录时系统会重新创建一个bash进程,即命令行解释器的进程帮我们打印对话框

3.bash进程只负责具体命令行解释,具体执行出问题只会影响其子进程

上述就是PPID在同一终端往复执行进程时不变的原因

3.5系统调用创建进程--fork

fork有两个返回值

若proc.c中不加sleep可能会打到命令行当中
通过man fork手册查看

返回成功时,返回给父进程子进程的pid,0返回给子进程,失败了返回-1,没有子进程被创建
通过以下代码来验证返回值问题


整个程序执行过程如下,bash在命令行中执行./可执行文件,可执行文件像普通进程一样被创建出来,系统为该进程分配pid,后执行代码到fork处,再由fork一分为二为父子进程,父进程就为它本身,子进程为它的新分支,由单进程变为多进程.

理解fork函数:

1.fork做了什么事情和目的

已知进程=内核数据结构+代码和数据,fork之后创建了子进程,所以也要创建一份内核数据结构PCB即task_struct,其基本属性复制于父进程可能会修改其中一部分,但是子进程没有自己的代码和数据所以注定只能和父进程共享,编译后运行过程中代码不能修改变量和数据可以.

创建子进程就是为了让父与子执行不同的代码块所以有不同的返回值
2.为什么fork要给子进程返回0,给父进程返回子进程pid?

返回不同的返回值是为了区分让不同的执行流执行不同的代码块,一般而言fork之后的代码父子共享;一个父进程可以有多个子进程,而每个子进程只有一个父进程,所以为了让父进程区分并控制每一个子进程,就需要给父进程返回每个子进程的pid
3.一个函数是如何做到返回两次的?

fork是一个函数,它的实现一定存在操作系统内部,变量的创建会在调用进入函数内部中创建,按执行流从上往下执行,一般函数体中返回语句在最后执行到该句时函数的核心工作已完成,fork函数中也就是创建子进程的工作已成,cpu

可以调度父子进程,而fork之后父子进程代码共享所以return语句被共享,父子进程各会执行一次所以有了不同的返回值
4.了解子进程的写实拷贝

首先了解任何平台,进程在运行时是具有独立性的.子进程是没有自己的代码和数据的,在创建子进程的pcb后会指向父进程的代码和数据,代码可以被共享因为运行期间不能被修改,父子进程都只是进行读写工作,数据不能被共享因为可以被修改会影响进程的独立性.因此会给子进程拷贝一份父进程的数据,但也不会完全复制过去因为有的数据子进程根本不会访问这样白白浪费了操作系统的空间,所以只在子进程需要修改数据时单独给拷贝一份在复制品上修改,什么时候修改什么时候拷贝,要多少拷贝多少,这就叫写时拷贝
5.一个变量怎么会有不同的内容?

在调用fork函数时执行return语句时,返回的时候就是在写入数据,写入的是父进程的数据,由于return是共享代码,子进程也会执行,此时为了确保父子进程的独立性就会发生写时拷贝,单独为子进程开辟一个新变量新空间进行数据写入;

此时id这个变量在父子进程中都存在为两个变量,现阶段可以理解为不同进程中的同名变量,其实id这个变量具有两个不同值跟进程地址空间有关,后续会进行深入学习
6.父子进程被创建好,fork()之后谁先运行呢?

谁先运行由调度器决定,不是我们能决定的,调度器的本质是在特定的数据结构中执行查找的一套算法,会在多个pcb中找到合适的进程放在cpu中去运行,一般每个进程的调度时间尽量相同来公平调度.cpu负责把一个进程放到cpu上去运行,调度器来决定哪一个进程被运行

理解bash行为:

1.bash会调用fork创建一个子进程来执行当前命令;就好比我作为一个销售怕事情办不好影响我的口碑,去让别人帮自己办这件事出了问题不影响自己,自己只用等待结果就好.bash这么做是怕命令出错影响自己本身的运行,总结来说就是隔离风险,独立执行

2.bash会继续接收用户的输入,打印出提示符继续命令行解释,而子进程跑过去解释新的命令,所以所有的进程都是bash的子进程,都是在bash调用fork创建出来的

目前有两种方式创建进程:

1.指令级别创建子进程: ./运行我们程序

2.代码层面创建子进程: fork()

相关推荐
网硕互联的小客服2 分钟前
服务器无法访问公网的原因及解决方案
运维·服务器
Gappsong8742 分钟前
Rufus:Ubuntu U盘启动盘制作工具详解
linux·c++·web安全·网络安全
dessler31 分钟前
RabbitMQ-交换机(Exchange)
linux·分布式·zookeeper·云原生·kafka·rabbitmq
nVisual38 分钟前
智算中心光纤线缆如何实现自动化计算?
运维·自动化·数据中心·综合布线·机房规划
程序员编程指南2 小时前
Qt开发环境搭建全攻略(Windows+Linux+macOS)
linux·c语言·c++·windows·qt
我爱学嵌入式2 小时前
C语言第 4 天学习笔记:位运算、流程控制与输入输出
linux·c语言·笔记
寒水馨2 小时前
聊聊DevOps,开发与运维如何分工协作?
运维·ci/cd·开发·devops
西红柿煎蛋3 小时前
WSL2子系统连接USB
linux
豆是浪个3 小时前
Linux(Centos 7.6)命令详解:jobs
linux·运维·centos