内核的进程管理与调度

时间:2021-02-19 15:46:51

内核的进程管理与调度

不同操作系统下,进程管理与调度的实现和机制有很大不同。对于上层应用(特别是跨平台)的开发,进程管理与调度显得不那么重要;但是,对于贴近操作系统的底层开发,就非常有必要了解Linux中进程的管理与调度

1.进程与线程的本质与实现

  • Linux中,进程就是执行中的程序及其相关的资源与信息(如文件描述符、当前状态等);而线程就是一种特殊的进程,所谓的多线程程序,其实就是多个共享资源的进程。所以,Linux不区分进程与线程,内核以进程为单位进行调度

内核的进程管理与调度

  • 内核通过一个叫task list(任务队列)的双向链表来维护进程。链表中的节点类型为task_struct俗称进程描述符,里面描述了进程的方方面面如:打开的文件fd,进程代码的地址空间,进程状态state,进程号pid
    内核的进程管理与调度

2.进程的创建

  • 进程之间存在继承关系,每个进程都是由其父进程调用fork()拷贝出来的,而所有进程的祖先,是一个PID为1的init进程,有关init进程详见内核启动阶段kernel_init(init)进程分析
  • 在应用层,我们可以调用API方便的创建进程与线程,在内核中,具体实现过程可以分为两步:“拷贝+加载”
    • 父进程调用fork(),拷贝出一个子进程,即在task list链表中新增了一个节点,该子进程继承了父进程绝大部分内容(除了一些没必要继承的东西,如PID、信号等)
    • 接着调用exec()来将新的可执行代码载入该子进程
  • 此外,我们也可以在内核中创建内核进程,虽然这不是一种值得提倡的做法。内核进程和普通进程一样,可以被调度也可以被抢占,只是它只能在内核中由其他内核进程创建
//调用kthread_run接口来创建内核进程,并立即执行
//该接口是宏,本质是调用kthread_create函数来创建进程,再调用wake_up_process执行进程
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
//调用kthread_stop来杀死内核进程
int kthread_stop(struct task_struct *k)

3.进程的状态

进程描述符结构体中的元素state,描述的是进程状态,它可能有5种值

  • TASK_RUNNING:进程正在运行或准备就绪(等待被调度)
  • TASK_INTERRUPTIBLE:进程正在睡眠(发生阻塞),可被信号唤醒
  • TASK_UNINTERRUPTIBLE:进程正在睡眠(发生阻塞),不可被信号唤醒,只能自己醒过来
  • __TASK_STOPPED:进程处于僵死态(刚被杀死),相关资源已被释放,但是父进程暂未将PID、进程描述符等内容释放
  • __TASK_TRACED:被其他进程追踪(被调试)
    内核的进程管理与调度

  • 值得注意的是,若父进程比子进程先退出,那么子进程可能会成为“孤儿进程”,无法正常退出(处于僵死态而无人释放)。此时,系统会自动为“孤儿进程”指定其他进程作为“养父”,保证了不会出现“孤儿进程”

4.内核的调度系统

多任务系统可以分为抢占式和非抢占式。大部分操作系统(包括Linux)都采用了抢占式,所谓抢占式,就是:调度器可以随意挂起运行中的进程,转而去执行另一个更重要的进程

进程的调度需求

  • 进程主要分为两类:I/O消耗型和处理器消耗型

    • I/O消耗型进程:此类进程大部分时间花在了I/O上,它们总是需要被执行,但是需要的运算量却不大,没有I/O时便处于阻塞状态。人机交互、信号处理等属于此类程序
    • 处理器消耗型进程:此类进程需要进行大量的计算,它们最好不停的在运行,除非被强占。视频解码、科学计算等属于此类程序
  • Linux2.6.23中,采用的调度算法为CFS(完全公平调度算法)。该算法会智能的判断进程的情况来进行调度,可以说真正的达到了完全公平调度

进程优先级

Linux中采用两种优先级,它们都能进行手工设置

  • nice值:nice值是Unix的标准化概念,范围为-20到19,默认为0。nice值越大,优先级越低(类似于人越nice,就越好欺负)。输入ps -el,标记NI的那一列便是nice值。我们可以使用如下API设置nice值
nice()
  • 实时优先级:实时优先级是Unix的标准化概念,范围为0到99。该属性是实时进程专有的,实时优先级越大,优先级越高。输入ps -eo state,pid,rtprio,comm,标记RTPRIO的那一列便是实时优先级,若显示“-”,则说明它不是实时进程。我们可以使用如下API设置实时优先级
sched_setparam()

内核抢占

  • Linux2.6之后支持了内核抢占,也就是说,内核代码可能随时被打断,除非那段代码持有了锁