1.进程的定义
从系统允许多个程序同时进入CPU那一天开始,我们才有了进程,进程的出现,解决了程序并发执行时对系统资源共享的描述问题,同时顺路解决了程序执行时动态特征的描述问题。
进程:一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位
进程四要素:
1.有一段程序供其执行,该程序不一定是一个进程独享,也可以和其他进程共享。
2.有进程专用的内核空间堆栈。
3.在内核中有一个名为“进程控制块”的task_struct,内核通过结构对进程进行调度控制。
4.有独立的用户空间。有独立的用户空间的是进程,有共享的用户空间的是用户线程,没有用户空间的是内核线程。
2.操作系统是怎么组织进程的
进程控制块PCB(Process Control Block)是进程存在和运行的唯一标志,在Linux中用task_struct这个结构体来表示。这个结构体中有很多数据项。
2.1进程状态
task_struct中用一个长整形state volatile long state; 表示进程的状态。
在linux中有四种基本的进程状态:
(1)就绪态(TASK_RUNNING):包括了运行态的进程。这是为了方便管理,因为任意时刻处于就绪态的进程最多只有一个。
(2)等待(睡眠)态:又被分为两种
i.浅度睡眠态(TASK_INTERRUPTIBLE): 在两种情况下被唤醒:
1.当等待的资源满足时。
2.其它进程通过信号或时钟中断唤醒。
ii.深度睡眠态(TASK_UNINTERRUPTIBLE):只能等到等待的资源满足时才被唤醒,而不能被其它进程唤醒
(3)暂停状态(TASK_STOPPED):收到以下几种信号,进程进入暂停状态:
i.SIGSTOP------------------停止进程执行
ii。SIGTSTP-----------------从终端发来信号停止进程
iii。SIGTTIN------------------来自键盘的中断
iv。SIGTTOU----------------后台进程请求输出。
(4)僵死状态(TASK_ZOMBIE):进程已结束且释放大部分资源,但尚未释放其PCB
2.2进程标识符
struct task_struct{ ... struct list_head tasks; ... char comm[TASK_COMM_LEN];//可执行程序名 ... };
2.5哈希表
进程链表是将所有的进程连接到一个链表上去,所以查找一个进程的时间复杂度是O(N),效率很低。为此,使用哈希表来提高查找的效率。
#define pid_hashfn(nr, ns) \ hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)
#define hash_long(val, bits) hash_32(val, bits)
static inline u32 hash_32(u32 val, unsigned int bits) { /* On some cpus multiply is faster, on others gcc will do shifts */ u32 hash = val * GOLDEN_RATIO_PRIME_32; /* High bits are more random, so use them. */ return hash >> (32 - bits); }
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ #define GOLDEN_RATIO_PRIME_32 0x9e370001UL
2.6就绪队列
task_struct定义了一个连接到就绪队列的域run_list,同样,内核中有一个就绪队列头runqueue_head。
struct sched_rt_entity { struct list_head run_list; .... }; struct task_struct { .... struct sched_rt_entity rt; ...... };
2.7等待队列
typedef struct __wait_queue wait_queue_t; struct __wait_queue { unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 void *private; wait_queue_func_t func; struct list_head task_list; };
等待队列列头:
struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t;
3.进程状态如何转换
在第二条中已经将进程状态做了分类,详细转换看大佬画的下图。
下附一张自己画的简图
4.进程是如何调度的
4.1进程调度优先级:
intprio, static_prio, normal_prio; unsignedint rt_priority; conststruct sched_class *sched_class; structsched_entity se; structsched_rt_entity rt;
4.2优先级定义:
#defineMAX_USER_RT_PRIO 100 #defineMAX_RT_PRIO MAX_USER_RT_PRIO #defineMAX_PRIO (MAX_RT_PRIO+ 40) #defineDEFAULT_PRIO (MAX_RT_PRIO + 20)
实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大静态优先级越低。
4.3调度策略:
#defineSCHED_NORMAL 0 #defineSCHED_FIFO 1 #defineSCHED_RR 2 #defineSCHED_BATCH 3 /* SCHED_ISO:reserved but not implemented yet */ #defineSCHED_IDLE 5 /* Canbe ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */ #defineSCHED_RESET_ON_FORK 0x40000000
SCHED_NORMAL用于普通进程,通过CFS调度器实现。
SCHED_BATCH用于非交互的处理器消耗型进程。
SCHED_IDLE是在系统负载很低时使用。
SCHED_FIFO:先入先出调度算法。
SCHED_RR:时间片轮流调度算法。
4.4调度时机
主动式:
当进程等待资源停止运行的时候,会处于睡眠状态,这时候直接调用schedule()请求调度,让出cpu。
例:
current->state= TASK_INTERRUPTIBLE
schedule();
使用指向当前进程状态的指针,将state改为可中断睡眠状态,然后调用schedule(),这样cpu就会调度其他资源执行。
抢占式调度:
首先,抢占的含义,当我们一个进程A在执行的时候,B进程在执行一项更加重要的任务,这时候就需要把cpu的资源让给B,如果A不能像上面一样主动地让出,那么B就去抢占cpu的资源。
4.5调度步骤
第一步:清理当前运行中的进程的一些资源。
第二步:根据调度策略选择一个运行的进程。
第三步:设置新的进程运行环境,例如堆栈,sp等。
第四步:进程上下文切换,退出A,切到B。
5.自己对该操作系统进程模型的看法
6.参考资料:
https://blog.csdn.net/deep_l_zh/article/details/48346287
https://www.cnblogs.com/jacklu/p/5317406.html
https://blog.csdn.net/kklvsports/article/details/52268085
https://www.zhihu.com/question/35484429