目录
一, 冯* 诺依曼体系结构
1,存储结构
编辑
二, 操作系统
1,概念
2,设计OS的目的
3,定位
4,如何理解 "管理"
5, 总结
三,进程
1. 概念
那么如何区分进程 & 程序?
2. PCB —— 描述进程
3. 组织进程
补充: cwd
4. task_struct ——PCB
(1) PID——标识符
5.父子进程
补充: fork()函数
fork函数——if分流
父子进程——各自优先级
6. 进程状态【Linux操作系统】
1. 新建:
2. 运行状态:
3. 阻塞:
4. 挂起:
6. liunx内核中的进程状态
(1) S睡眠状态(sleeping):
(2) R运行状态(running):
(3) D磁盘休眠状态(Disk sleep):
(4) T停止状态(stopped):
T 区别于 S:
(5) X死亡状态(dead):
(6) Z僵尸状态:
僵尸进程的危害
(7) 孤儿进程
7. 进程优先级
修改优先级
一, 冯* 诺依曼体系结构
1,存储结构
我们知道一个程序的执行速度,需要数据的输入输出。那么如果CPU直接与输入设备进行交互,那么会是这样的情况,输入设备输入10s的数据,CPU处理花了1ms,然后输出又花了10s,这样会导致机器性能很差;
而冯洛依曼想到了用内存与CPU打交道,这样就变成了4秒的数据输入,1ms的处理,4秒的输出变相的提高了计算机效率。
举一个例子:我与一个老表进行微信交流。他们之间的数据流动流程图,如下:
二, 操作系统
1,概念
2,设计OS的目的
3,定位
4,如何理解 "管理"
关于操作系统,银行例子:
5, 总结
操作系统就是给用户提供一个安全,稳定,简单的执行环境。
1. 操作系统管理:先描述,再组织
2. 操作系统对外服务,是通过提供:系统调用接口
三,进程
1. 概念
(其实,我们启动一个软件,本质上是创建一个进程; 并且在linux中输入一个命令,在系统层面创建了一个进程)
那么如何区分进程 & 程序?
当大量进程进入内存,CPU需要对进程优先级进行排序,而它做不到 ,它只能读数据,那么这个时候就需要对进程进行管理,那么我们如何创建进程?
1. 先描述 (通过结构体(PCB),描述进程属性块)2. 再组织 (通过数据结构,如链表,顺序表,红黑树等,将进程组织起来)
那么我们开始来管理进程:
2. PCB —— 描述进程
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
在windows中,PCB就是一个表示进程模块的结构体;
在Linux中,PCB的具体是 struct task_struct{......// 进程的所有属性}
3. 组织进程
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
那我们如何查看一个进程?
(1). 我们制作一个死循环程序,然后执行程序。
(2)再打开另一个窗口查看进程。
指令:ps axj | grep "需要查看的程序"
回到死循环程序,我们通过ctrl + c退出死循环程序,再次检测可以发现,死循环进程消失。
同样的我们可以通过输入top命令,而这就是Linux下的任务管理器
补充: cwd
输入 ls /proc // 功能是:通过文件形式来展示进程
我们随便进入一个进程,查看里面文件属性,这里就有我们要找的cwd文件:
转成详细页:ls /proc/13712 // 13712是Test进程的PID
那这些PPID,PID是什么玩意儿,下面我们会进行分析。
4. task_struct ——PCB
前面我们可以看到有这些类
- 标示符 (PID): 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。(权限决定能与不能,优先级是能后的顺序)
- 程序计数器: 程序中即将被执行的下一条指令的地址。(所以程序计数器是不断被修改的)
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。(可以通过PCB找到进程中的数据)
- 上下文数据: 进程执行时处理器的寄存器中的数据。 [休学例子,要加图CPU,寄存器]
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
(1) PID——标识符
功能: 标示符 (PID): 描述本进程的唯一标示符,用来区别其他进程。
那么我们怎么在main函数中获取特定的进程的PID呢?
getpid() // 获取本进程PID
getppid() // 获取父进程PID
需要包含系统函数头文件: #include <sys/types.h>
我们在源码中,运用getpid()函数,获取本程序的PID
结果如图:
这里除了 在程序运行时ctrl + c可以终止进程;也可以通过 输入命令 kill -9 PID 结束进程。 这里不对kill进行讲解。
5.父子进程
补充: fork()函数
功能:在fork函数调用处,创建一个子进程独立于父进程,fork之后代码父子共享。
返回值:1. 在父进程中返回子进程的PID; 2. 在子进程返回 0 ; 3. 失败返回 -1
问:为啥会有两个返回值呢?
答:fork()函数中,父子进程各自执行自己的return语句
fork函数——if分流
我们知道父子进程公用,fork函数之后的代码。可父子进程大多都是运行不同的代码,那我们如何进行分开呢?
利用fork的返回值进行if 分流:
id 在父进程里面是 子进程的PID; 在子进程里面是 0 。
关于fork函数内部逻辑:
创建一个进程,内部属性以父进程内部属性为模板。
父子进程——各自优先级
问: 父子进程中,先执行父进程,再执行子进程吗?
不一定
原因:CPU可能运行一个父进程10ms后,先不执行了,再次入运行队列,而这时子进程就在执行。关于谁先运行,不一定,这个取决于操作系统的调度器决定。
6. 进程状态【Linux操作系统】
1. 新建:
字面意思。 就是给一个程序创建有一个进程。
2. 运行状态:
task_struct在运行队列中开始排队时,就是运行状态。
3. 阻塞:
等待非CPU资源,叫做阻塞状态。
比如:我们进行scanf / cin 进行键盘输入时,在等待键盘数据录入,这就是一个阻塞状态。
4. 挂起:
当内存不足时,OS适当地置换进程的代码和数据到磁盘,此时进程叫做挂起状态。
6. liunx内核中的进程状态
(1) S睡眠状态(sleeping):
意味着进程在等待事件完成 (这里的睡眠有时候也叫做可中断睡眠——可以通过kill -2 指令,中断S,为T状态)
对应阻塞状态
输入: while :; do ps axj | head -1 && ps axj | grep 运行文件名 | grep -v grep ; sleep 1; done // 可查看最新的进程运行状况,周期为1秒
(2) R运行状态(running):
并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
以上面代码为例,将cout代码注释掉,这样就不在占用输出设备资源了,则一直都在R状态。
细节:我们发现状态栏中有S+,R+ 其中"+"又是什么意思呢? “+”这里的含义是一个前台进程,其结果会占用我们与bush的聊天界面,我们无法输入指令,可以ctrl + c 退出;同理,没有“+”的状态为后台进程,不会占用聊天框,我们可以继续输入指令。 但启动程序时要 ./程序 &,它会返回其PID,不用时kill -9 PID 杀死即可。
(3) D磁盘休眠状态(Disk sleep):
有时候也叫不可中断睡眠状态(uninterruptible sleep)——深度睡眠,在这个状态的进程通常会等待IO的结束。(不可被被动中断,kill都不能杀掉,除非拔电源)
(4) T停止状态(stopped):
可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。(比如:gdb调试)
kill -19 // 暂停进程
kill -18 // 继续进程
T 区别于 S:
睡眠进程是等待非CPU资源, 对应的是阻塞状态。暂停状态,原先是运行状态,没有等待资源,仅仅是暂停了。
(5) X死亡状态(dead):
这个状态只是一个返回状态,你不会在任务列表里看到这个状态.(当大量进程结束时,操作系统忙不过来,那这时进程就会标记为X进程,随时准备被回收)
(6) Z僵尸状态:
一个进程已经退出,但还不能被OS回收,处于一个等待被检测的状态,叫做僵尸状态。
那为啥要有这种状态? 为了状态保持,让父进程或者OS来检测。 (所谓的保持:指代码和数据被回收了,进程的PCB还保留着。)
僵尸进程的危害
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
- 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的! 因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
- 内存泄漏?是的
(7) 孤儿进程
概念:
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢? 父进程先退出,子进程就称之为“孤儿进程”孤儿进程被1号init进程领养,当然要有init进程回收喽
我们简单创建一个父进程先退出,子进程后退出的场景。发现:
7. 进程优先级
概念:
cpu资源分配的先后顺序,就是指进程的优先权(priority)。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
查看优先级,输入:
ps -al // 会显示机器所以进程
UID : 代表执行者的身份PID : 代表这个进程的代号PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号PRI :代表这个进程可被执行的优先级,其值 越小越早被CPU执行NI :代表这个进程的nice值, 优先级的修正值,范围是-20到19,共40个级别。 (每次设置nice值时,PRI都是默认值一般80)
Linux优先级具体做法:
优先级 = 老的优先级(PRI) + nice值(NI)
修改优先级
输入: top 指令; 进入top后按“r”–>输入进程PID–>输入nice值
有时需要提高权限,sudo一下即可(前提是:设置过信任用户)
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论;如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力。