【问题标题】:Does Linux Time Division Processes Or ThreadsLinux时分进程或线程
【发布时间】:2011-01-18 14:41:11
【问题描述】:

一位教授曾经在课堂上告诉我们,Windows、Linux、OS X 和 UNIX 在线程而不是进程上扩展,因此即使在单处理器上线程也可能使您的应用程序受益,因为您的应用程序将在 CPU 上占用更多时间。

我在我的机器(只有一个 CPU)上尝试了以下代码。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t xs[10];

void *nop(void *ptr) {
    unsigned long n = 1UL << 30UL;
    while(n--);
    return NULL;
}

void test_one() {

    size_t len = (sizeof xs) / (sizeof *xs);
    while(len--)
        if(pthread_create(xs+len, NULL, nop, NULL))
            exit(EXIT_FAILURE);

    len = (sizeof xs) / (sizeof *xs);
    while(len--)
        if(pthread_join(xs[len], NULL))
            exit(EXIT_FAILURE);

}

void test_two() {

    size_t len = (sizeof xs) / (sizeof *xs);
    while(len--) nop(NULL);

}

int main(int argc, char *argv[]) {

    test_one();
//  test_two();
    printf("done\n");
    return 0;
}

两个测试在速度方面是相同的。

real    0m49.783s
user    0m48.023s
sys     0m0.224s

real    0m49.792s
user    0m49.275s
sys     0m0.192s

这个我想,“哇,线程很烂”。但是,在具有四个处理器的大学服务器上重复测试,速度接近四倍。

real    0m7.800s
user    0m30.170s
sys     0m0.006s

real    0m30.190s
user    0m30.165s
sys     0m0.004s

我在解释家用机器上的结果时是否忽略了某些东西?

【问题讨论】:

  • 俗话说“在 UNIX 中,你最好使用进程而不是线程来实现 IPC”,这不一定是因为线程模型更糟糕 - 尽管它是 - 或者我被告知.但是,因为流程模型要好得多,而且更轻量级。在 IPC 方面,获得正确且稳定的进程模型比线程模型容易得多。而且,当你有写时复制和其他细节时,优势有利于分叉。我只是鹦鹉学舌,因为你听到的一些事情引发了这个问题 - 我认为你听错了我所​​描述的内容。

标签: linux performance multithreading scheduling


【解决方案1】:

为了深入了解任务/线程...让我们看看这个玩具内核代码...

结构注册{ int eax、ebx、ecx、edx、es、ds、gs、fs、cs、ip、标志; 结构 tss *task_sel; } 结构线程{ 结构 regs *regs; 诠释父ID; 结构线程*下一个; } 结构任务{ 结构 regs *regs; int *phys_mem_begin; int *phys_mem_end; int *文件句柄; int 优先级; int *num_threads; 诠释量子; 持续时间; 整数开始时间,结束时间; 诠释父ID; 结构线程 *task_thread; /* ... */ 结构任务*下一个; }

假设内核为该结构task 分配内存,这是一个链表,仔细查看quantum 字段,它是基于priority 字段的处理器时间的时间片。总会有一个 id 为 0 的任务,它从不休眠,只是空闲,可能发出 nops(无操作)......调度程序围绕着令人恶心的方向旋转,直到无穷大(即拔掉电源时),如果 quantum字段确定任务运行20ms,设置start_timeend_time + 20ms,当end_time启动时,内核将cpu寄存器的状态保存到regs指针中。进入链中的下一个任务,从指向regs 的指针加载 cpu 寄存器并跳转到指令中,设置量程和持续时间,当持续时间达到零时,继续下一个......实际上是上下文-切换......这就是让它在单个 cpu 上同时运行的错觉。

现在查看thread 结构,它是线程的链表...在task 结构中。内核为该任务分配线程,为该线程设置 cpu 状态并跳转到线程中......现在内核必须管理线程以及任务本身......再次在任务和线程之间进行上下文切换...

继续使用多 CPU,内核将被设置为可扩展的,调度程序会做什么,将一个 task 加载到一个 cpu 上,将另一个加载到另一个 cpu(双核)上,并且两者都跳转到指令指针指向的位置……现在内核真正在两个 cpu 上同时运行两个任务。扩展到 4 路,同样的事情,额外的任务加载到每个 CPU 上,再次扩大到 n 路......你得到了漂移。

正如您所看到的那样,线程不会被视为可扩展的概念,因为坦率地说,内核在跟踪哪些 cpu 正在运行什么以及最重要的是,哪些任务正在运行哪些线程方面有一项艰巨的工作,这从根本上解释了为什么我认为线程不完全可扩展...线程消耗大量资源...

如果您真的想看看发生了什么,请查看 Linux 的源代码,特别是在调度程序中。别等了,忘掉2.6.x内核版本吧,看史前版本0.99,调度器会更容易理解和更容易阅读,当然,它有点旧,但值得一看,这将有助于你理解为什么并希望我的回答也是,为什么线程不可扩展......并展示了玩具操作系统如何使用基于进程的时间划分。我一直努力避免涉及现代 cpu 的技术方面,这些方面可以做的比我所描述的要多......

希望这会有所帮助。

【讨论】:

  • 谁投了反对票却懒得发表评论,这违背了SO的精神!
【解决方案2】:

一位教授曾在课堂上告诉我们,Windows、Linux、OS X 和 UNIX 是在线程而不是进程上扩展的,因此即使在单处理器上,线程也可能会使您的应用程序受益,因为您的应用程序会在 CPU 上占用更多时间。

不一定。如果您的应用是唯一运行 CPU 密集型的应用程序,那么更多线程不会神奇地提供更多 CPU 时间可用 - 所有这将导致更多 CPU 时间浪费在上下文切换中。

这个我想,“哇,线程很烂”。但是,在具有四个处理器的大学服务器上重复测试,速度接近四倍。

那是因为有四个线程,它可以使用所有四个处理器。

【讨论】:

  • +1。您要测试的是,鉴于其他一些 CPU 密集型进程正在运行(例如只有 1 个线程),您的测试程序在 4 个线程上获得的 CPU 时间比在家用机器上使用 1 个线程时要多。两个过程的总时间可能会整体增加;但是(如果您的教授的说法是正确的)额外的线程将使 CPU 时间的分配偏向具有更多线程的进程,以便它更快地完成。
【解决方案3】:

我不确定你到底在问什么,但这里有一个可能会有所帮助的答案。

在 Linux 下,进程和线程本质上是一模一样的。调度程序理解称为“任务”的东西,它并不真正关心它们是否共享地址空间。分享或不分享东西真的取决于它们是如何创建的。

是否使用线程或进程是一个关键的设计决策,不应掉以轻心,但调度程序的性能可能不是一个因素(当然,像 IPC 要求这样的东西会极大地改变设计)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-16
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    • 2011-02-04
    • 2023-03-08
    相关资源
    最近更新 更多