进程的理解
进程
- 1、进程
- 1.1 基本概念
- 1.2 描述进程-PCB
- 1.3 最早版本task-struct(linux)
- 1.4 查看进程
- 进程标示符
- **cwd:**
- 1.5 getppid(副id)
- 2 系统调用,创建进程
- 2.1 见一见
- 2.2 理解
- 2.3 多进程创建
- 2.4 理解创建子进程
- 2.5 进程的状态
- 分时操作系统(为每个进度分配时间)
- 理解进程状态
- 3 进程优先级
- 4、进程切换
- 4.1 为什么进程切换
- 4.2 切换过程和理解
- 4.2 多进程
- 6.3 struct tss_struct
- 5、进程调度
- 6、补充知识
1、进程
1.1 基本概念
1、进程==内核数据结构(task_struct)+程序的代码和数据
2、当我们运行一个程序的时候,首先会创建一个结构体来管理这个进程的信息,然后该程序的代码与数据会加载到内存当中,当有多个进程时,会将描述这个进程的结构体(task_struct)依次连接起来,形成一个链表,cpu通过类似于队列的方式依次处理每一个进程,这个进程管理着数据域代码。
3、这种处理方式我们称为先描述,在组织。
1.2 描述进程-PCB
task_ struct内容分类
1、标示符: 描述本进程的唯一标示符,用来区别其他进程。
2、状态: 任务状态,退出代码,退出信号等。
3、优先级: 相对于其他进程的优先级。
4、程序计数器: 程序中即将被执行的下一条指令的地址。
5、内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
6、上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
7、I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
8、记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。其他信息``
所以对进程的学习,就是学习这个结构体里的内容
1.3 最早版本task-struct(linux)
1.4 查看进程
杀掉进程:
进程标示符
进程id(PID)
父进程id(PPID)
20636:进程pid
proc:process(进程的信息以文件形式存于这个目录)
ps底层就是对/proc里的进程进行文本分析
ls /proc/pid
查看该进程所有的属性
cwd:
一般我们写完代码后,执行可执行程序会在当前目录下创建exe,当前路径(cwd);
chdir:改变上述文件创建位置
/proc不是硬盘级别的文件,而是内存级别的,每次启动系统,都会刷新
1.5 getppid(副id)
2 系统调用,创建进程
2.1 见一见
2.2 理解
1、父:子==1:多
2、fork()-> 创建子进程,在子进程与父进程中代码是会共享的,但是数据各自享有
3、为什么?进程之间是互不影响的,即便是父子,
4、一个进程可以创建多个子进程,子进程也可以创建多个子进程,总的来说linux进程是一个树的结构
2.3 多进程创建
2.4 理解创建子进程
2.5 进程的状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在
Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义
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 */
};
1.R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列 里。
2.S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
3.D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
4.T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
5.X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
分时操作系统(为每个进度分配时间)
实时操作系统
理解进程状态
1、运行:在内存中一个进程运行时会创建一个运行队列(runqueue)也就是上面说的链表,只要描述进程的task_struct在内存中存在,该进程就叫运行状态,代表我已经准备好被cpu进行调度轮转
2、阻塞:每一个硬件设备也被一个结构体所管理,这个结构体内有着该设备的准备情况,例如一个程序需要键盘读入,但是我们一直不向键盘写入,那么该runqueue里所有的进程岂不是都会卡在当前这个进程,这是不被操作系统所允许的,故此设计师就在硬件的struct device中设计一个队列,专门来存放这些还没有准备好的进程,当我们再次输入后,操作系统就会将这个进程重新放入运行队列。
3、运行和阻塞的本质:是让不同的进程,出在不同的队列。
4、硬件是否准备就绪其实可以在struct device 中插入一个status即可,通过数字的变化就可以来确定硬件的准备情况
-19暂停进程
-18重新启动进程
断点本质是进程暂停
僵尸进程:子进程的代码与数据已经被回收,但是它的task_struct还在维护
孤儿进程:子进程还在运行,父进程已经消亡
3 进程优先级
进程的优先级就是获得某种资源的先后顺序,本质上是由于资源的竞争。
一个进程如何来表示优先级:(PRI+NI)(优先级数字越小,进程优先级越高)
pri默认是80,每次更改pri都会恢复默认值
4、进程切换
4.1 为什么进程切换
1、我们家用的电脑一般是采用并发的形式来轮转每一个进程,目的是让每一个进程都有被调度的可能,所以进程必须切换。
2、当该进程的时间片到了就需要进程切换,但是一个时间片完了,该进程没有执行完怎么办?我们需要该进程在任何地方都能够被重新调度切换
3、进程切换:本质是保存上下文数据,以便下次可以继续运行该进程
4.2 切换过程和理解
1、进程在运行的时候,会产生很多临时数据,都存放于cpu的寄存器当中
下图就是一个进程切换的示意图
简单来说进程执行的过程就是创建task_struct与磁盘代码数据拷贝到内存以后,cpu去执行该程序,首先将程序的第一条指令读入pc(当前执行的指令下一条指令地址),然后cpu会将该指令存于ir(正在执行的命令),pc再读入下一条指令地址的同时处理ir里面的指令,然后cpu根据pc找到下一条指令再存放于ir,pc再读入下一条指令地址的同时cpu处理ir里面的指令,循环往复,直到执行完该程序。
4.2 多进程
在切换进程时会将当前进程执行的情况存放于一个结构体(下面的tss_struct)(存放寄存器),肯定不在cpu内部(内存)
进程的切换核心->进程上下文数据的保存与恢复
切走:将相关寄存器的内容保存起来
切回:将保存的寄存器的数据,恢复到寄存器中
通过这样的方式每次进程切换时cpu就可以完全覆盖式写入。
保存上次数据的地方肯定在内存中(当前进程的PCB)
6.3 struct tss_struct
5、进程调度
1、在运行队列中并不是我们所谓的一个结构体指向下一个结构体
2、linux真实的调度算法:
在runqueue中存在一个queue,这个队列存放的是每一个进程的优先级,0-100(实时进程),100-140(我们的进程),
前面我们了解过进程的优先级共有40个数,就是出自这里,在队列中我们可以看到两张这样的表,另外还有active与expried指针以及一个bitmap(位图),进程的优先级就由上述所构成
进程的调度就是在100-140中从低到高依次调度,但是这里就会有一个问题(还有其他方面,例如新进程的创建),就是一个进程调度完后是不是会放回原队列,然后根据优先级再次插入100-140这些位置,这样可能会导致优先级特别低的进程在实际中根本不会被调度,很明显这样是不被允许的。所以聪明的大佬设计了两张表,其中一张用active来指向,另一张用expried来指向,active中全是当前目前正在运行的进程,当进程需要轮转时,就放入expried表中,这样active就只会减少,expried只会增加,当active里的进程运行完的时候,直接将这两个指针交换一下即可。
上述我们描述了进程真实的调度,我们再来谈一下bitmap的作用,首先来看bitmap有5个int的位置,一个int占4个字节,一个字节占8个比特位,刚好160个比特位,能够覆盖完这140个位置,我们用1表示有进程,0表示无进程,这样我们就可以很快的找到哪些地方存在进程
这样的方法被称为大O1调度算法
6、补充知识