Linux时分处理还是线程

时间:2010-02-19 01:49:59

标签: linux performance multithreading scheduling

一位教授曾在课堂上告诉我们,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

在解释家用机器的结果时,我是否忽略了某些内容?

3 个答案:

答案 0 :(得分:24)

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

struct regs{
  int eax, ebx, ecx, edx, es, ds, gs, fs, cs, ip, flags;
  struct tss *task_sel;
}

struct thread{
    struct regs *regs;
    int parent_id;
    struct thread *next;
}
struct task{
   struct regs *regs;
   int *phys_mem_begin;
   int *phys_mem_end;
   int *filehandles;
   int priority;
   int *num_threads;
   int quantum;
   int duration;
   int start_time, end_time;
   int parent_id;
   struct thread *task_thread;
     /* ... */
   struct task *next;
}

想象一下,内核为该结构task分配内存,这是一个链表,仔细查看quantum字段,即基于{{1的处理器时间的时间片。 }}字段。总是会有一个id 0的任务,它永远不会睡觉,只是空闲,可能会发出nops(No OPerationS)...调度程序会在无穷无尽的情况下旋转直到无穷大(即当电源被拔掉时),如果{{1 }}字段确定任务运行20ms,设置priorityquantum + 20ms,当start_time启动时,内核将cpu寄存器的状态保存为{{1}指针。进入链中的下一个任务,从指针加载cpu寄存器到end_time并跳转到指令,设置量子和持续时间,当持续时间达到零时,继续下一个...上下文切换...这就是它给出了一个在单个cpu上同时运行的错觉。

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

转到多CPU,内核将被设置为可伸缩,调度程序将执行的操作,将一个regs加载到一个cpu上,将另一个加载到另一个cpu(双核),并且都跳转到指令指针所指向的位置......现在内核正在两个cpu上同时运行两个任务。扩展到4路,同样的事情,加载到每个CPU上的额外任务,再次向上扩展,到n路......你得到漂移。

正如你所看到的那样,线程不会被视为可扩展的概念,因为内核在跟踪cpu运行的内容方面是一项非常庞大的工作,而且最重要的是,运行哪些线程的任务是什么,从根本上解释了为什么我认为线程不完全可扩展......线程消耗了大量资源......

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

希望这有帮助。

答案 1 :(得分:4)

  

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

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

  

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

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

答案 2 :(得分:1)

我不确定你究竟在问什么,但这是一个可能有帮助的答案。

在Linux下,进程和线程基本上完全相同。调度程序理解称为“任务”的事情,它并不真正关心它们是否共享地址空间。共享或不共享内容实际上取决于它们的创建方式。

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