Linux内核中大量使用了队列,这里仅列举它在进程调度中的几处应用。Linux内核中的队列是以双链表的形式连接起来的,include/linux/list.h中定义了队列并提供了一些接口,详细的介绍可以参考**[1]**中的附录。

Linux中的进程有如下几个主要状态:

进程状态

说明

TASK_RUNNING

进程正在运行或将要被运行。

TASK_INTERRUPTIBLE

进程正在睡眠,等待某个条件的完成。

TASK_UNINTERRUPTIBLE

深度睡眠,不会被信号打扰。

TASK_STOPPED

进程运行被停止。

TASK_TRACED

进程被调试程序停止,被另一个进程跟踪。

两个额外的状态是EXIT_ZOMBIE和EXIT_DEAD,表示进程处于僵死状态还是真正死亡。处于僵死状态的进程会等待其父进程的收养(否则就会被init进程收养),而真正死亡的进程会被直接删除。

状态为TASK_RUNNING的进程都会被放入运行队列(runqueue)中,这是通过task_struct(定义在include/linux/sched.h)中的run_list成员来链接的。不过,为了让内核每次都能选取优先级最合适的进程,Linux为每个优先级构建了一个queue。这是通过struct prio_array来实现的,struct prio_array的定义在kernel/sched.c,大致如下:

struct prio_array

int nr_active;

unsigned long bitmap[BITMAP_SIZE];

struct list_head queue[MAX_PRIO];

};

queue成员就是队列数组。每个CPU有各自的runqueue,每一个runqueue又有包含两个prio_array,一个是活动队列,一个是时间片耗尽的队列。当运行队列空时,内核便会交换两个队列的指针,原来的耗尽队列就成了新的活动队列!这和prio_array中的bitmap是决定调度算法为O(1)的关键。

状态为TASK_STOPPED,EXIT_ZOMBIE或EXIT_DEAD的进程不会被放入专门的队列中,它们直接通过pid或者通过父进程的孩子队列来访问。

TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程会被放入等待队列。不同的是,每个等待事件都会有一个等待队列,该队列中的进程等待同一事件的完成。(“事件”一个动态的过程,不好通过具体的结构来定义一个“事件”。这里等待一个事件就是查看某个条件是否为真,比如某个标志变量是否为真。)wait_queue_head_t的定义在include/linux/wait.h中,如下:

typedef struct _ _wait_queue_head {

spinlock_t lock;

struct list_head task_list;

}wait_queue_head_t;

wait_queue_t的定义如下:

typedef struct _ _wait_queue {

unsigned int flags;

struct task_struct * task;

wait_queue_func_t func;

struct list_head task_list;

}wait_queue_t;

进入等待状态的接口有两类:

prepare_to_wait*()/finish_wait()

wait_event*()

其实wait_event*()内部也是调用prepare_to_wait*(),把它放入一个循环中。而且wait_event*()在事件完成时会自动调用finish_wait()。决定使用哪个要看情况而定。(sleep_on*()是遗弃的接口,现在已经不再使用,虽然还支持。)等待队列中的进程有两种,一种是exclusive的进程,另一种是nonexclusive的进程。所谓exclusive是指唤醒的进程等待的资源是互斥的,每次只唤醒一个(唤醒多个也可以,不过最后还是只有一个会被唤醒,其余的又被重新添加到等待队列中,这样效率会大打折扣)。一般,等待函数会把进程设为nonexclusive和uninterruptible,带“interruptible”的会专门指定状态为interruptible;而带“timeout”的会在超时后退出,因为它会调用schedule_timeout();带“exclusive”的则会把进程设为exclusive。

唤醒的接口虽然只有wake_up*(),但它内部也分成好几种。带“interruptible”的唤醒函数只会唤醒状态是TASK_INTERRUPTIBLE的进程,而不带的则会唤醒TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程;所有唤醒函数都会把等待同一事件的nonexclusive进程全部唤醒,或者把其中一个exclusive的进程唤醒;而带“nr”的则会唤醒指定个数的exclusive的进程,带“all”的会把全部exclusive进程唤醒。带“sync”会忽略优先级的检查,高优先级的进程可能会被延迟。最后,持有自旋锁时只能调用wait_up_locked()。

进程管理是Linux内核中最关键的部分,它的性能几乎直接决定着内核的好坏,其中使用的一些设计和算法非常复杂。这里仅仅是介绍了队列在其中的使用情况,更深入的探索还有待继续去探索。

参考资料

Linux Kernel Development, Second Edition, Robert Love, Sam Publishing.

Understanding the Linux Kernel, 3rd Edition, Daniel P. Bovet, Marco Cesati, O’Reilly.

The Linux Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures, Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski, Prentice Hall PTR.

linux进程运行队列,Linux进程调度中队列的使用相关推荐

  1. linux进程管理机制,linux进程管理,linux进程管理机制

    linux进程管理,linux进程管理机制 一.基本介绍 1.在 LINUX 中,每个执行的程序(代码)都称为一个进程.每一个进程都分配一个 ID 号 2.每一个进程,都会对应一个父进程,而这个父进程 ...

  2. msgget();msgsnd();msgrcv();msgctl(); 消息队列 Linux进程间的通信方式之消息队列

    Linux进程间的通信方式 ----消息队列. 消息队列和共享内存类似 消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息. 消息队列存在于系统内核中,消息的数量受系统限制. 我们来看 ...

  3. Linux进程通信之System V消息队列

    System V消息队列是Open Group定义的XSI,不属于POSIX标准.System V IPC的历史相对很早,在上个世70年代后期有贝尔实验室的分支机构开发,80年代加入System V的 ...

  4. linux 进程 结构体,Linux中进程控制块PCB-------task_struct结构体结构

    Linux中task_struct用来控制管理进程,结构如下: struct task_struct { //说明了该进程是否可以执行,还是可中断等信息 volatile long state; // ...

  5. python做Linux进程运行,Python实现在Linux系统下更改当前进程运行用户

    在上一篇文章中,我们讲了如何在linux上用python写一个守护进程.主要原理是利用linux的fork函数来创建一个进程,然后退出父进程运行,生成的子进程就会成为一个守护进程.细心观察的可能会发现 ...

  6. linux 进程与锁,linux 中的进程与锁

    ############################################################################### #################### ...

  7. 内网linux服务器安装运行环境,linux下如何在内网中安装docker_网站服务器运行维护...

    docker的镜像和仓库是什么_网站服务器运行维护 docker镜像就像是一个linux的文件系统,这个文件系统里面包含了可以运行在linux内核的程序以及相应的数据.仓库是集中存储镜像的地方. 一. ...

  8. Linux进程管理(4):进程调度

    进程调度的本质是让进程更好地分时复用处理器资源.概况地说,进程调度包括调度策略和进程切换二个重要话题. 一.几个重要概念 1)时间片/优先级/抢占调度 所有的调度器都是围绕这3个概念进程设计,调度器之 ...

  9. Linux进程实践(1) --Linux进程编程概述

    进程 VS. 程序 什么是程序? 程序是完成特定任务的一系列指令集合. 什么是进程? [1]从用户的角度来看:进程是程序的一次执行过程 [2]从操作系统的核心来看:进程是操作系统分配的内存.CPU时间 ...

最新文章

  1. Linux编译mybatis,使用mybatis assembly插件打成tar包,在linux系统中运行服务-Go语言中文社区...
  2. html没有css还有用吗,如果css足够强大了,你还会用编程的方式实现去实现css已有的功能吗?_html/css_WEB-ITnose...
  3. javascript里你绝对用的上的字符分割函数--原创
  4. 扫描器scanner的源代码
  5. 闭包/Block当成函数参数实现回调
  6. 要求将数组中的0项去掉,将不为0的值存入一个新的数组,
  7. spring MVC中页面添加锚点
  8. linux alsa声卡命令,Linux ALSA声卡驱动之一:ALSA架构简介
  9. 编辑距离 dp_使用动态编程(DP)编辑距离
  10. 【Kafka】kafka console received unknown topic or partition error listOffset request
  11. jquery获取加载的html内容,jquery 获取ajax加载的html中部分内容
  12. 年仅 5 岁的 Rust 如何成为最受欢迎的编程语言?
  13. win10 基础之上安装 Linux-Manjaro-Deepin 连夜采坑,快速整理下
  14. 关于管理的十个经典故事
  15. Matable实现利用互相关函数求相位差
  16. 解决git clone出现“error: RPC failed; result= 18,HTTP code = 20018. 00 KiB/s”报错的五个网址(个人用)
  17. 计算机的输入法如何使用简短描述,应用电脑(1)第一章 计算机组成与中文输入法...
  18. linux运行魔力宝贝,魔力宝贝私服架设详细简易教程
  19. 大数据难学吗?如何学习大数据?(附学习路线)
  20. 防汛可视化:雨季来临,如何做好防汛措施?

热门文章

  1. ble 连接成功后找不到服务_闷声发大财的BLE芯片龙头
  2. java参数注解pam_吃透Java基础十:注解
  3. java保存登录信息_java – 保存登录详细信息(首选项)android
  4. UC浏览器电脑版怎么开启免费WiFi
  5. QQ浏览器怎么把文件添加到私密空间 怎么使用私密空间
  6. MySQL建表字段长度的限制、汉字和字母占字节数
  7. java实现 XSS攻击防护
  8. java实现简单二叉树
  9. 云服务器centos怎么还原系统还原,云服务器centos怎么还原系统还原
  10. android设置错误页面,Android ViewPager设置当前项目/页面时抛出IndexOutOfBounds异常