Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/firebase/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux kernel 为什么Linux内核使用循环双链表来存储进程列表?_Linux Kernel_Process Management - Fatal编程技术网

Linux kernel 为什么Linux内核使用循环双链表来存储进程列表?

Linux kernel 为什么Linux内核使用循环双链表来存储进程列表?,linux-kernel,process-management,Linux Kernel,Process Management,Linux内核将进程列表存储在循环双链接列表中,称为任务列表。背后的原因是什么?为什么使用循环双链接列表?使用此数据结构的优点是什么?创建者试图通过使用此数据结构实现什么?灵活性,因此,如果您知道例如您正在搜索的内容可能已经过时,您可以使用列表\u for\u each\u entry\u reverse宏,而不是通常的正向宏 “在遍历整个列表时使用链表非常重要,并且需要动态添加和删除元素…使用这种类型的链表提供了最大的灵活性” 而且没有代码重复 “在过去,内核中有多个链表实现。需要一个强大的链

Linux内核将进程列表存储在循环双链接列表中,称为任务列表。背后的原因是什么?为什么使用循环双链接列表?使用此数据结构的优点是什么?创建者试图通过使用此数据结构实现什么?

灵活性,因此,如果您知道例如您正在搜索的内容可能已经过时,您可以使用
列表\u for\u each\u entry\u reverse
宏,而不是通常的正向宏

“在遍历整个列表时使用链表非常重要,并且需要动态添加和删除元素…使用这种类型的链表提供了最大的灵活性”

而且没有代码重复

“在过去,内核中有多个链表实现。需要一个强大的链表实现来删除重复代码。在2.1内核开发系列中,引入了官方的内核链表实现。”


资料来源:罗伯特·洛夫。“Linux内核开发”(第三版)。p、 87-94在大多数情况下,根本不使用任务列表。内核通过线程信息结构中的指针(并通过堆栈指针到达后者)或通过当前任务的全局指针(ahem)到达任务结构

只有在需要遍历任务列表时(由于任务列表的长度可变,因此这是一种罕见且固有的低效操作,因此在大型计算机上它可以增长到数十万甚至数百万个条目),才会使用链表。在这种情况下,可以方便地从列表中的任何一点开始,然后继续,直到回到开始的位置(即循环列表)。为什么它是双重链接的,我不确定,但我想链接只会在一个已经很大的结构上增加几个字节,而且以任何一种方式遍历它的灵活性都是值得的。
编辑:事实上,正如@thrig在另一个答案中指出的,原因是内核中有一个人人都使用的链表实现(没有奇怪的错误,因为有人使用了自己的),而这恰好是一个双链表实现,正是因为它的一些用户需要灵活性。

以某种形式列出对象(如进程)的原因是,有时内核需要枚举所有这些对象,即依次遍历每个对象。这意味着必须有一种方法来查找此类型的所有对象。如果可以一次创建和删除一个对象,那么链表是最简单的解决方案

列表需要双重链接才能支持删除对象。删除对象时,代码需要更新指向该对象的所有指针。因此,对象需要包含一个指向指向它的所有其他对象的指针(或者至少需要有一个从对象本身开始的指针链)。对于单链表,从a中删除B→B→C、 除了遍历所有对象直到找到正确的对象,没有办法发现需要更新其指针的对象。使用双链接列表,从a中删除B↔B↔C、 将指针从B指向A,并将A指向B的指针改为指向C,同样地,C也是如此。

As, 有时内核需要循环所有进程 (例如,在处理
kill(-1,sig)
时, 它向每个进程发送
sig
呼叫进程有权发送信号, 过程1除外-请参阅) 我已经很久没有看这个代码了; 但是,在Unix的早期版本中,进程表是一个数组- 一些固定数量的连续
proc
结构 (有时称为“进程槽”)。例如, “查看所有流程”循环可能看起来像这样:

for (i = 0; i < PROC_MAX; i++) // This would probably really have started with i = 1 { // so as not to look at proc[0], i.e., PID 1. if (proc[i].flags & IS_AN_ACTIVE_PROCESS) do something with the process } 对于(i=0;iPROC\u MAX(可能有数千个) 处理插槽只是为了找到其中包含实际进程的插槽- 可能是一个小得多的数字 将流程放入链接列表中 允许内核对每个进程执行某些操作 无需搜索一个空洞的过程表

此外,如果所有内容都与链表联系在一起, 拥有一个动态调整大小的流程表是可行的 如果/当初始默认静态进程表已满, 只需分配更多(非连续)内存并将它们链接在一起


附言。 我相信可能有多个流程列表:

  • 一个整体,
  • 一个用于可运行(处于“运行”状态)的进程
  • 一个用于当前实际运行的进程 在(至少)一个处理器上
  • 一个用于等待事件的进程 (例如,完成I/O请求)
  • 等等
在黑暗时代(30年前),每当用户按下Enter键 (当时称为返回), 内核可能不得不进行搜索 通过1000个条目的长进程表
查找等待tty输入的进程。

因此,您的第一段说,如果您知道您感兴趣的进程,您不需要使用进程列表来访问它。  比如说,如果一个朋友告诉你他的电话号码,你不需要在电话簿上查他就可以给他打电话。  这对我来说似乎微不足道。  我是否遗漏了更深层次的含义?@G-Man:关键是,与数组相比,迭代链表的速度较慢,因此重要的是,大多数任务的访问都不是通过此数据结构进行的。负载