文章继续上次的展开讲解
3. 进程
a. 描述进程 -- PCB
概念:
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
注意:
操作系统内核都是由 C语言 编写的,PCB 相当于 C语言 里面的结构体
(一) task_struct 结构体
- task_struct 是一种特殊的 PCB ,它是属于 linux操作系统的
- task_struct 是 Linux内核 的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
(二) task_ struct内容分类 (包含各种属性)
- 标示符: 描述本进程的唯一标示符,用来区别其他进程
- 状态: 任务状态,退出代码,退出信号等
- 优先级: 相对于其他进程的优先级 (决定 CPU 先执行哪个程序 , 数值越低,优先级越高)
- 程序计数器: 程序中即将被执行的下一条指令的地址
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据(方便后续恢复寄存器中存储的数据)
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
- 其他信息
b. 组织进程
进程是通过数据结构一个个组织起来的(linux内核是采用链表)
c. 查询进程
- ls /proc
查询进程
d. stack_struct 各种属性
(一)标识符
- PID 进程id
- PPID 父进程id
(通过系统调用获得标识符)代码举例
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
cout << "PID: " << getpid() << " PPID: " << getppid() << endl;
return 0;
}
通过指令获得标识符
(二)进程状态
- R 运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里
- S 睡眠状态(sleeping): 意味着进程在等待事件完成,相当于堵塞状态(这里的睡眠有时候也叫做可中断睡眠 )【比如是等待键盘的输入,等到有数据的时候,又可以回到运行队列里】
注意:
遇到 挂起状态时(内存空间不够的时候),操作系统可能会处理 S状态的进程,把它们的数据和代码存到磁盘里面,再从 PCB 里面删除 (D状态下的不会动)
- D 磁盘休眠状态(Disk sleep)(有时候也叫不可中断睡眠状态),在这个状态的进程通常会等待IO的结束
- T 停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行
- X 死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态(数据,代码都会清理,并且进程从 PCB 里面移除)
- Z 僵死状态 (zombie)-- 僵尸进程:
一般来说,要经历 X 状态,就要先经历 Z 状态(当进程退出且父进程没有接收到子进程退出时返回的代码时,就会产生僵尸进程)
注意:
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
2. 如果父进程先结束运行,它的子进程的 PPID 就要发生更改(即父进程发生更改,为了新的父进程可以得到子进程退出时返回的代码,一般来说,它的新的父进程就是操作系统)
3. 孤儿进程
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养(即操作系统)
(三) 优先级
优先级决定进程的调度先后顺序
PRI 越高,优先级越低
注意:
- UID : 使用者身份信息
- PRI :优先级
- NI :优先级修正数值
- 优先级 PRI(new) = PRI (old) 【默认80】 + nice (NI)
- nice值的范围 : -20 ~ 19
查看进程优先级的命令
- top
- 进入top后按“r”–>输入进程PID–>输入nice值
e. 通过系统调用创建进程 -- fork
代码举例
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
pid_t id = fork();
if(id == 0)
{
while(1)
{
sleep(1);
//子进程
printf("我是子进程 : 我的 PID : %d , 我的 PPID : %d\n ",getpid(),getppid());
}
}
else if(id > 0)
{
while(1)
{
sleep(1);
//父进程
printf("我是父进程 : 我的 PID : %d , 我的 PPID : %d\n ",getpid(),getppid());
}
}
else
{
printf("创建进程失败\n");
}
return 0;
}
注意:
父进程和子进程的代码和数据是共享的,但是它们俩又是独立的个体,所以如果子进程修改了父进程的数据,此时会发生写实拷贝,同一个变量存两个不同的值
如:
返回得到的 id 就发生了写实拷贝
(id > 0 , 走父进程执行的代码 ; id = 0 ,走子进程执行的代码 ; id < 0 ,则创建子进程失败)
f. 其它概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进
注意:
- 进程切换是基于时间片,时间一到,进程没有结束,通过运行队列维护的两个指针数组使每个进程一段时间切换(如果第一个进程时间到了,将它从running数组移到waiting数组)
- 程序计数器pc,eip,记录当前进程正在执行指令的一行指令的地址
- CPU 里面的寄存器保存的是进程相关数据,又可以叫做进程的上下文
- 进程从 CPU 上离开的时候,需要保存好自己的上下文,甚至带走
- 进程被切换:保存上下文(临时数据储存到PCB里面),回复上下文