目录

1. 冯诺依曼体系结构

2. 操作系统(Operator System)

2-1 概念

2-2 设计OS的⽬的

2-3 核⼼功能

2-4 如何理解 "管理"

2-5 系统调⽤和库函数概念

3. 进程

3-1 基本概念与基本操作

3-2 描述进程-PCB

3-3 task_struct

3-4 查看进程的方式

4.父子进程

4-1:问题引入

4-2 通过系统调⽤创建进程-fork初识

4-3 一个函数fork,怎么会有两个返回值?

5.操作系统上看进程状态

5-1 广义的进程状态

补充知识:

5-2.linux进程的状态

5-3 理解僵尸进程、孤儿进程


1. 冯诺依曼体系结构

  我们常⻅的计算机,如笔记本。我们不常⻅的计算机,如服务器,⼤部分都遵守冯诺依曼体系。

截⾄⽬前,我们所认识的计算机,都是由⼀个个的硬件组件组成

• 输⼊单元:包括键盘,⿏标,扫描仪,写板等

• 中央处理器(CPU):含有运算器和控制器等

 • 输出单元:显⽰器,打印机等

关于冯诺依曼,必须强调⼏点:

• 这⾥的存储器指的是内存

• 不考虑缓存情况,这⾥的CPU能且只能对内存进⾏读写,不能访问外设(输⼊或输出设备)

 • 外设(输⼊或输出设备)要输⼊或者输出数据,也只能写⼊内存或者从内存中读取。

 • ⼀句话,所有设备都只能直接和内存打交道。

2. 操作系统(Operator System)

2-1 概念

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

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

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

2-2 设计OS的⽬的

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

• 对上,为⽤⼾程序(应⽤程序)提供⼀个良好的执⾏环境

为什么要有操作系统?

    1.如果要让用户自己去对底层软硬件资源进行操作,成本太高了,因此由OS对下进行软硬件资源的管理,从而确保操作过程是稳定、安全、高效的,进行良好的工作!

    2.OS对上给用户提供一个稳定、安全、高效的运行环境。

注意:用户不能直接对硬件进行操作,必须经过操作系统之手!

2-3 核⼼功能

• 在整个计算机软硬件架构中,操作系统的定位是:⼀款纯正的“搞管理”的软件

2-4 如何理解 "管理"

Linux操作系统管理的本质在于“先描述,再组织”,拿OS管理硬件来说:

先描述:将每个硬件的所有属性封装起来,形成一个结构体或类对象。

再组织:将这些结构体放到一个数据结构里(拿链表来说),那么对某个硬件的操作,就变成了对链表的增删查改操作了!

注意:对于任何计算机对象,管理的原则都是先描述再组织!

2-5 系统调⽤和库函数概念

• 在开发⻆度,操作系统对外会表现为⼀个整体,但是会暴露⾃⼰的部分接⼝,供上层开发使⽤, 这部分由操作系统提供的接⼝,叫做系统调⽤。

• 系统调⽤在使⽤上,功能⽐较基础,对⽤⼾的要求相对也⽐较⾼,所以,有⼼的开发者可以对部 分系统调⽤进⾏适度封装,从⽽形成库,有了库,就很有利于更上层⽤⼾或者开发者进⾏⼆次开 发。

总结:系统调用是操作系统为用户开放的接口,但由于使用成本高,用户对系统调用进行了封装,形成了库函数;

3. 进程

3-1 基本概念与基本操作

课本概念:进程是一个正在运行中的程序;

而从内核来说:进程 = 内核数据结构 (task_struct)+ 代码和数据;

3-2 描述进程-PCB

基本概念:

  • 进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct

task_struct其实是PCB的⼀种;

  • 在Linux中描述进程的结构体叫做task_struct。

  • task_struct是Linux内核的⼀种数据结构,它会被装载到RAM(内存)⾥并且包含着进程的信息。

进程的管理:

  从图中可以看出,当可执行程序运行后,代码数据会被加载到内存中,而关于进程的各种属性,以及指向代码数据的指针都被封装在task_struct中,然后将这个结构体放入链表中管理起来,这不就是前面说的“先描述再组织”吗?

  一个task_struct,就能找到进程的各种属性以及代码数据,所以我们说进程就=内核数据结构+代码和数据;

3-3 task_struct

内容分类:

组织进程

可以在内核源代码⾥找到它。所有运⾏在系统⾥的进程都以task_struct链表的形式存在内核⾥。

3-4 查看进程的方式

①在/proc文件里查看,这里有所有进程的信息

如:要获取PID为1的进程信息,你需要查看 /proc/1 这个⽂件夹

进入某个进程文件后,会有exe和cwd两项,exe代表可执行文件所在的路径,cwd代表当前工作路径(可以通过chdir函数改变工作路径),可以用ls-l查看路径:

②通过指令ps -ajx 也可以调出所有进程的信息,如果想查找特定进程,可按照如下管道的方式进行

上图中可以查找到进程的pid,pid是用来唯一标识进程的标识符,此外还可以通过系统调用函数getpid来打印pid值。ppid可以通过getppid获取;

4.父子进程

4-1:问题引入

当我们的操作系统启动后,就会有一个bash进程开始执行了,观察图中代码:myproc程序每次运行,都会打印出相同的ppid,也就是父进程都一样,那么可以得出结论:在命令行中,执行命令、执行程序的本质都是bash进程创建子进程,由子进程来执行我们的代码

4-2 通过系统调⽤创建进程-fork初识

①见一见子进程创建

②快速解释fork函数

  fork函数能创建一个子进程,这样就会有父进程和子进程,对父进程的返回值是子进程的pid,对子进程返回的是0,创建失败返回-1.

子进程创建出来后,父子进程的代码是共享的,但是数据是各自私有的!

原因:进程之间具有很强的独立性,多个进程之间是互不影响的,即使是父子进程;

注意:子进程创建后,为了方便管理,也会有相应的task_struct来管理子进程;子进程刚创建时,和父进程共享一份代码(共享同一物理内存页),但是一旦某一进程修改了数据,就会多拷贝一份代码数据,以保持数据独立性;

  说的再具体一些,fork采用的是写实拷贝机制,当多个进程通过 fork 系统调用创建子进程时,父进程和子进程会共享同一块物理内存页面,操作系统不会立即复制内存页;当子进程或父进程尝试修改共享内存页中的数据时,系统会检测到写操作,此时才会触发真正的复制机制,系统为需要修改的内存页创建一个新的副本,并将该副本映射到当前进程的地址空间中!

③创建多进程

运行结果:

4-3 一个函数fork,怎么会有两个返回值?

  fork函数在执行return之前,要完成创建子进程的过程,这个主体功能包括从父进程拷贝一个task_struct、调整新task_struct的部分属性,将该struct链接到进程列表中,此时子进程已经创建完毕!

  那么在执行return语句的时候就已经有父子两个进程了,根据进程数据的独立性,此时两个进程分别创建返回值的临时变量,因此返回值变量实际是两个不同进程中的独立变量,因此可以返回不同的值;

注意:fork之后,父子进程谁先运行,是由OS的调度器自主决定的!

5.操作系统上看进程状态

广义上讲,进程的状态是这样的:

5-1 广义的进程状态

补充知识:

1.时间片:

  Linux/Windows民用级别的操作系统都是分时系统,调度任务追求公平。

  CPU执行进程的代码,不是把进程代码执行完毕,才开始执行下一个,而是给每个进程分配一个时间片,基于时间片进行轮转调度!

2.并行和并发:

并发:多个进程在同一个cpu下采用进程切换的方式,在一段时间内,让多个进程都得以推进开发

并行:多个进程在多个cpu下,分别同时进行运行;

3.对运行、阻塞、挂起的理解

(1)运行:一台电脑一定有多个进程在运行,也就是内存中会有多个task_struct,这些结构体会被放到运行队列中等待cpu调度,那么, 当一个进程被创建出来,被放到CPU的运行队列中后,即便没有真正运行起来,我们也可以说是运行状态了!

(2)阻塞:

 因为OS对各外设的管理是基于“先描述,再组织”的,每一个外设都可以用一个struct来描述属性,再用指针链接下一个外设struct,所以对外设的管理本质上是内存中对链表的增删查改

   当运行到某一个进程时,如果遇到像IO操作这样依赖外设的情况时,为了保证其它进程能够正常如期排到,会将该进程出队列,转移到对应外设的队列中等待,这个过程叫做阻塞。当外设检测到IO执行完毕,会通知操作系统将该进程再入到运行队列中。

(3)挂起:如果内存严重不足,由于在阻塞时进程的数据仍占据内存,OS会将处于阻塞状态的进程的数据全部换出到磁盘的swap分区,当阻塞状态解除时再换入回运行队列。

5-2.linux进程的状态

1.R:运行状态,进程正在运行或准备好运行(在运行队列中等待 CPU 时间片)

2.S:可中断睡眠(浅睡眠):进程正在等待某个事件,但可以被信号唤醒。例如,调用 sleep()read() 等系统调用时,进程会进入这种状态。

3.D:不可中断睡眠(深睡眠):进程正在等待某个事件,但不能被信号唤醒。例如,进程正在等待磁盘 I/O 操作完成时,会进入这种状态。

4.T:停止状态:进程做法非法但不致命的操作,被OS暂停了;

5.t:追踪停止状态:当进程被追踪的时候,断点停下;

6.Z:僵尸状态:进程已经完成,但父进程尚未读取其状态信息。这种状态的进程不能被唤醒,也不能被调度器调度。

7.X:死亡状态:进程已经完全终止,所有资源都被释放,进程的 PCB 也被移除。这种状态的进程不会出现在进程列表中。

8.P:挂起状态:进程被挂起并等待某些条件满足后继续执行。这种状态的进程不会被调度器调度

5-3 理解僵尸进程、孤儿进程

  1.进程退出后,会把退出信息保存在task_struct中。

  僵尸进程是指已经完成执行完毕(代码数据被释放了),但其父进程尚未读取其状态信息的进程。在这种状态下,进程的资源(如文件描述符、内存等)已经被释放,但进程的 PCB(进程控制块)仍然保留在系统中,直到父进程读取其状态信息。

问题:虽然僵尸进程不会占用 CPU 时间,但它的 PCB 仍然占用系统资源,若不回收,就一直僵尸,会造成内存泄漏!

解决方式:父进程应该在子进程结束时调用 wait()waitpid() 来读取子进程的状态信息,从而释放子进程的 PCB。

 2. 如果子进程还存在,父进程却挂掉了,那么子进程会被系统进程“领养”,这就是孤儿进程;

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐