- 1.进程和程序的区别
- 2. 调研进程的调度算法.
- 3. 调研task_struct结构体, 理解结构体中的各个字段的含义.
- 4. 使用代码模拟实现僵尸进程, 孤儿进程的场景.
进程和程序的区别:
程序:完成特定任务的一系列指令集合。
进程:(每个进程都有自己的状态和独立的地址空间。)
从用户角度看: 进程是程序的一次动态执行过程。
分时系统: 时间片轮转。
从操作系统: 进程是操作系统分配资源的基本单位(最小单位)
进程和程序的区别:
程序: 数据+代码
进程: 数据+代码+堆栈+PCB
PCB: 进程控制块 (Process.Control.Block)——Linux操作系统下的PCB是:task_struct
(每个进程有一个PCB表示)———用链表存储
并发:多个进程在一个cpu下采用进程切换的方式,在一段时间之内让多个进程得以推进。
并行:多个进程在多个CPU下分别.同时进行。
1. 进程是动态的,程序是静态的;
2. 进程的生命周期是短暂的,而程序相对永久;
3. 进程有重要的数据结构PCB;
4. 一个进程只能对应一个程序
而一个程序可以对应多个进程。
进程的调度算法:
1.时间片轮转调度算法(RR) : 给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,执行完成后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。优点是兼顾长短作业;缺点是平均等待时间较长,上下文切换较费时。适用于分时系统。
2.先来先服务调度算法(FCFS) :根据进程到达的先后顺序执行进程,不考虑等待时间和执行时间,会产生饥饿现象。属于非抢占式调度,优点是公平,实现简单;缺点是不利于短作业。
3.优先级调度算法 (HPF):在进程等待队列中选择优先级最高的来执行。
4.多级反馈队列调度算法 :将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,先按优先级调度,优先级相同的,按时间片轮转。优点是兼顾长短作业,有较好的响应时间,可行性强,适用于各种作业环境。
5.高响应比优先调度算法 :根据“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”这个公式得到的响应比来进行调度。高响应比优先算法在等待时间相同的情况下,作业执行的时间越短,响应比越高,满足段任务优先,同时响应比会随着等待时间增加而变大,优先级会提高,能够避免饥饿现象。优点是兼顾长短作业,缺点是计算响应比开销大,适用于批处理系统。
task_struct结构体: (PCB的一种)
它定义在linux-2.6.38.8/include/linux/sched.h文件中
- 在Linux中描述进程的结构体叫做task_struct
- task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)中并包含着进程的信息。
task_struct都可能包含哪些成员信息?
task_struct都可能包含哪些成员信息?
1.进程状态,记录进程在等待,运行,或死锁
2.调度信息,由哪个调度函数调度,怎样调度等
3.进程的通讯状态
4.因为要插入进程树,必须有联系父子兄弟的指针,当然是tast_struct型
5.时间信息,比如计算好执行的时间 以便cpu分配
6.标号,决定进程归属
7.可以读写打开的一些文件信息
8.进程上下文和内核上下文
9.处理器上下文
10.内存信息
因为每一个PCB都是这样的,只有这些结构,才能满足一个进程的所有要求,
进程状态:
volatile long state;
int exit_state;
state成员的可能取值如下:
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
系统中的每个进程都必然处于以上所列进程状态中的一种。
对上述信息进行简要描述:
TASK_RUNNING : 表示进程要么正在执行,要么正要准备执行。
TASK_INTERRUPTIBLE : 表示进程被阻塞(睡眠),直到某个条件变为真。条件一旦达成,进程的状态就被设置为TASK_RUNNING。
TASK_UNINTERRUPTIBLE: 意义与TASK_INTERRUPTIBLE类似,除了不能通过接受一个信号来唤醒以外。
__TASK_STOPPED: 表示进程被停止执行。
__TASK_TRACED : 表示进程被debugger等进程监视。
EXIT_ZOMBIE : 表示进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息。
EXIT_DEAD :表示进程的最终状态。
EXIT_ZOMBIE和EXIT_DEAD也可以存放在exit_state成员中。
代码模拟实现僵尸进程,孤儿进程的场景:
通过系统获取进程标识符:
- 进程(id):PID
- 父进程(id): PPID
僵尸进程:
1. 什么是僵尸进程?
当子进程退出并且父进程没有等待它(没有使用wait()调用),没有读取到子进程的退出时的返回代码时就会产生僵尸进程。即只要子进程退出,父进程继续运行,但父进程没有读取子进程的状态,此时子进程进入Z状态(僵尸状态)。
2.僵尸进程的危害:
- (1)退出状态本身就是要用数据去维护的,也属于进程信息,所以保存在task_struct(PCB)中,那么z状态一直不退出,PCB就一直要维护
- (2)当一个父进程创建了很多子进程,但却一直没有回收,这样就会造成内存资源的浪费。(因为数据结构本身就要占用内存)
- (3)如果父进程不去回收子进程,还会造成内存泄漏
3.怎样避免僵尸进程
- (1)父进程通过wait和waitpid等函数等待子进程的结束,这样会导致父进程挂起
- (2)倘若父进程非常忙,那么可以用singal函数为SIGCHLD安装handler(因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收)
- (3) 如果父进程不关心子进 程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
- (4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还是需要自己做。
我们可以用top指令来查看有几个僵尸进程:
top:动态查看进程的变化
参数:
-d:后面直接加秒数,表示显示整个进程界面更新到的秒数,如top -d 3每三秒更新一次
-b:以批次的方式执行top
-n:与-b搭配,表示需要几次top输出的结果
-p:指定某个特定的PID进行检测
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
return 1;
}else if(pid > 0){
printf("parent : %d\n", getpid());
sleep(30);
}else{
printf("child : %d\n",getpid());
sleep(5);
}
}
ps -ef | grep a.out
这个指令不能用上键复制,只能手动复制粘贴或者自己敲出来
孤儿进程:
什么是孤儿进程?
孤儿进程就是在其父进程执行完成或被终止后仍然继续运行的一类进程。这些孤儿进程将会被init进程(进程号为1号)所收养,并由init进程对它们完成状态收集工作。因为有init进程会循环的wait()已经退出的子进程,所以孤儿进程并不会产生什么危害。
代码实现:
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
pid_t pid=fork();
if(pid < 0)//创建失败
{
perror("fork error\n");
}
printf("\n");
if(pid == 0)//子进程
{
printf("pid:%d ppid:%d\n",getpid(),getppid());
printf("I'm the child process\n");
printf("I will sleep five seconds\n");
sleep(5);
printf("\n");
printf("pid:%d ppid:%d\n",getpid(),getppid());
printf("child process is exited.\n");
}
else//父进程
{
printf("pid:%d ppid:%d\n",getpid(),getppid());
printf("I am father process\n");
printf("I will sleep one seconds.\n");
sleep(1);
printf("father process is exites.\n");
}
return 0;
}