个人主页:仍有未知等待探索-CSDN博客
专题分栏:Linux
目录
一、冯诺依曼体系结构
输入设备:键盘、鼠标、摄像头、话筒、磁盘、网卡...
输出设备:显示器、声卡、磁盘、网卡...
CPU:运算器、控制器
存储器:内存
- 数据时要在计算机的体系结构中进行流动的,流动过程中,进行数据的加工处理。从一个设备到另一个设备,本质是一种拷贝!
- 数据设备间的拷贝效率决定了计算机整机的基本效率!
- 存储:距离CPU越近,效率越高,成本越高。
- 在硬件数据流动角度,在数据层面:
1)CPU不和外设直接打交道,CPU只和内存打交道。
2)外设(输入输出)的数据,不直接给cpu,而是先放入内存中。
- 程序运行,为什么要加载到内存?
冯诺依曼体系结构规定这么做!
- 程序=代码+数据:程序数据都要被cpu访问。
- 程序没有被加载到内存的时候,在哪里?
磁盘(外设)二进制文件。
二、操作系统
1、概念
操作系统是一款软件,用来进行软硬件资源管理。
广义的概念:操作系统的内核+操作系统的外壳周边程序(给用户提供使用系统的方式)。
狭义的概念:只是操作系统的内核。
2、为什么要有操作系统?
对软硬件资源进行管理(手段),为用户提供一个良好(稳定、安全、高效)的运行环境(目的)
3、理解操作系统
1.管理的本质
管理者 管理 被管理者的本质就是管理被管理者的数据。
2.管理的概念
先描述,再组织。
pl:对于校长管理学生。
先描述:学生可以通过一张表来进行记录,这张表上写了学生的各种信息 ---链表(对应存储的数据结构)
再组织:校长管理学生就可以转化为校长管理学生链表,对其进行增删改查。
3.操作系统结构图
- (系统调用层)用户不能直接对操作系统进行操作,而是通过操作系统提供的系统调用(也就是函数)进行对操作系统的使用,使操作系统处于平稳的状态。
- (操作接口层)不是所有的程序员都用系统调用。为了普通开发者能更加方便的使用操作系统,在系统调用的基础上又封装了一层用户操作接口(各种标准库、shell、指令等)。
- (驱动层)因为每个硬件的厂商可能不同,其执行的标准可能不同,导致操作系统不能直接对硬件进行管理。所以每个生产硬件的厂商都必须写自己的驱动程序,使操作系统都能对其进行管理。每个硬件都有自己的驱动程序。
- 跨平台性:不管你是什么操作系统(Linux、Windows),都能在其基础上编写各种语言的代码。原因就是他们都封装了自己的标准库。
4.为什么要有操作系统?
以人为本
对上:提供良好(高效,稳定,安全)的运行环境。
对下:进行硬件管理工作。
三、进程
1、进程的概念
进程 = 内核数据结构task_struct(PCB) + 对应的代码和数据。
task_struct(PCB) ,包括进程的所有属性和一个内存指针(指向对应的代码和属性)。
- 操作系统中,进程可以同时存在很多个!
- 将可执行程序加载到内存中,这个不是进程,只是进程的代码和数据。
- 调度运行进程,本质上就是让程序控制块task_struct进行排队。
Linux:(指令ps - 查看进程,选项 axj 详细信息)
1.进程的管理
- 对进程的管理 -> 对链表的增删改查
- 添加一个进程,就是将磁盘中的可执行程序添加到内存中,然后再进程列表中加入这个进程。
- 删除一个进程们就是将内存中的可执行程序释放掉,然后删除进程列表中该进程。
2.进程动态运行
进程task_struct在不同的队列中,进程就可以访问不同的资源。
3.进程的task_struct
a.启动
- ./XXX,本质上就是让系统创建进程并运行。
我们自己写的代码形成可执行程序 == 系统命令 == 可执行文件、在Linux运行的大部分执行操作,本质上都是运行进程!
- 每个进程都有唯一标识,叫做进程的pid。
- 用户不能直接获取进程的pid,需要用系统调用 --- pid_t getpid(void)。
因为进程的pid在内存中的task_struct是属于操作系统内部的内核数据结构。
- 在Linux中,用户可以用 [ ctrl + c ] 来终止进程。(用户层面)
- 在Linux中,用户也可以用指令 [ kill -9 pid ] 来杀掉进程。(系统调用)
b.进程创建的代码方式
- 获取当前进程的父进程的pid --- pid_t getppid(void)。
- 对于同一个进程,每次启动,对应的pid都不一样,这是正常的。
- 创建进程 --- pid_t fork(void)。
fork之后,父子数据、代码共享。创建一个进程,本质上是系统中多了一个进程、多了一个内核数据结构task_struct。
- 父进程的代码和数据来自磁盘;子进程的代码和数据默认情况继承父进程的代码和数据(只有当子进程要修改数据的时候,系统才会进行写时拷贝,拷贝出一份新的代码和数据给子进程)
- fork函数也是系统调用。
- 进程具有独立性。
为什么fork的返回值又能大于0又能等于0?
理论上,在fork函数内部,在return返回值之前,子进程就已经创建完毕了。return的时候子进程和父进程各自进行返回,所以才看上去是fork返回了两次。
c.task_struct的内容
进程的 pcb 中会记录自己对应的可执行程序的路径。
每个进程在启动的时候,会记录自己当前在那个路径下启动(进程的当前路径 cwd)
chdir:更改进程的cwd(当前路径)
2、进程的状态
1.Linux的进程状态
task_struct(PCB)中的一个属性:int status;
- R(running):运行状态。进程可能正在运行,也可能在运行队列里。
- S(sleeping):休眠状态。(1、进程在等待“资源”就绪 2、可中断睡眠)
- T(stopped):停止状态。让进程暂停,等待进一步唤醒。
- t(trancing stop):可追踪停止状态。gdb中调试打断点。
- D(disk sleep):磁盘休眠状态。是Linux系统比较特有的一种Linux状态。深度睡眠,不可被中断状态(进程自己醒来,重启-断点)
- X(dead):死亡状态。这个状态只是一个返回状态,任务列表中没有该进程。
- Z(zombie):僵尸状态。子进程退出,但是父进程没有读取到子进程的退出码。
while(1) { // 如果这个打印注释掉,进程会显示R状态;否则大部分是S状态。原因是cpu和屏幕外设的刷新速度不一样,cpu完成运算时,屏幕还在等待进行刷新,所以时S状态。 printf(""); }
2.僵尸进程、孤儿进程
僵尸进程:子进程已经运行完毕,然是需要维持自己的退出信息,在自己的进程task_struct会记录自己的退出信息,未来让父进程进行读取,如果没有父进程读取,僵尸进程会一直存在。
孤儿进程:父进程比子进程先退出,子进程就是孤儿进程。孤儿进程一般都是会被1号进程(OS)进行领养的。
孤儿进程为什么会被os认领?
要保证子进程被正常回收,否则会导致内存泄漏。
3.进程的阻塞和挂起,运行
运行:(在cpu上运行)进程在运行队列中,该进程的状态就是R状态。
一个进程一旦持有cpu,会一直运行到这个进程结束吗?
不会,cpu会给予时间片进行轮转调度的。
让多个进程一切换的方式进行调度,在一个时间段内同时得以推进代码,就叫做并发。
任何时候都同时又多个进程在真的同时运行,我们叫做并行。
阻塞态(S、D状态):等待键盘资源是否就绪,键盘上篇有没有被用户按下的按键,按键数据交给进程。
操作系统如何对硬件进行管理?
对硬件的数据进行管理的。(先描述,再组织!!!)
- 进程本身就是软件。
- 不是只有cpu才有等待队列,各种设备都有自己的等待队列。
阻塞和运行的状态变化,往往伴随着pcb被连入不同的队列中!入队列的不是进程的什么代码和数据,而是进程的task_struct。
挂起态:在磁盘中有一个swap分区,当OS内存吃紧的时候,将处于阻塞状态的进程的代码和数据放入swap分区中(唤出)。
用效率换取空间。
3、进程切换
- cpu的寄存器会保存进程的临时数据。
- cpu内部所有的寄存器中的临时数据,叫做进程的上下文。
- 进程在切换,最重要的一件事是:上下文数据的保护和恢复。
- cpu内的寄存器:寄存器本身是硬件,具有数据的存储能力,cpu的寄存器硬件只有一套。
- cpu内部的数据可以有多套,有几个进程,就有几套和进程对应的数据。
- 寄存器 != 寄存器的内容
谢谢大家!!!