2010-02-19 60 views
2

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

我试着用我的机器上的以下代码(它只有一个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 

我俯瞰东西解释我的家用机器时的结果?

+0

俗话说是“在UNIX中,你最好使用进程,而不是IPC的线程”,这并不一定是因为线程模型更糟 - 尽管它是 - 或者我被告知。但是,因为流程模型大大改善,而且更轻量化。谈到IPC时,比线程模型更容易让流程模型正确稳定。而且,如果您有写入时复制和其他细节,则优点会更有利于分叉。我只是喋喋不休,因为你听到的东西引发了这个问题 - 我想你误解了我所描述的。 – 2010-05-25 16:26:29

回答

4

一个教授曾经告诉我们在类的Windows,Linux,OS X和UNIX规模上的线程,而不是过程,所以线程可能会甚至在单个处理器上有利于您的申请,因为您的应用程序将在得到更多的时间CPU。

不一定。如果你的应用程序是唯一一个CPU密集型的东西在运行,更多的线程不会奇迹般地让更多的CPU时间可用 - 所有这些都会导致上下文切换浪费更多的CPU时间。

我想,“哇,线程吸”。但是,在四台处理器的大学服务器上重复测试接近四倍的速度。

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

+2

+1。你想要测试的是,考虑到其他一些耗费CPU资源的进程正在运行(比如说只有一个线程),你的测试程序在你的家庭计算机上有4个线程的CPU时间比1次多。两个流程的总时间可能会增加。但是(如果你的教授的主张是正确的),额外的线程将会偏向CPU时间的分配,使用更多的线程来进行处理,以便更快地完成。 – RMorrisey 2010-02-19 02:13:24

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,这是一个linked-分配内存列表中,仔细查看quantum字段,即基于priority字段的处理器时间的时间片。总是会有一个ID为0的任务,它不会睡觉,只是空闲,也许发出nops(无操作)......调度器绕着广告无声无息地旋转(直到电源被拔下),如果quantum字段决定任务运行20ms,设置start_timeend_time + 20ms,当end_time启动时,内核将cpu寄存器的状态保存为regs指针。进入链中的下一个任务,从指针加载cpu寄存器到regs并跳转到指令中,设置量程和持续时间,当持续时间达到零时,继续下一个...有效的上下文切换。这是什么给它一个在单个CPU上同时运行的错觉。

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

移动到多CPU,内核将被设置为可扩展的,调度程序会做什么,将一个task加载到一个CPU上,将另一个加载到另一个CPU(双核)上,并且都跳到指令指针指向的地方......现在内核真的在两个CPU上同时运行这两个任务。扩展到4路,同样的东西,加载到每个CPU上的附加任务,再次扩展,以n路......你得到的漂移。

正如你所看到的那样,线程不会被认为是可扩展的,因为内核在跟踪cpu运行什么的时候非常坦率地表现出了一项巨大的工作,并且最重要的是,运行哪个任务线程,这从根本上解释了为什么我认为线程不完全可扩展......线程消耗大量资源...

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

希望这会有所帮助。

+5

谁低估了,不打扰评论是违背SO的精神! – t0mm13b 2010-02-19 14:43:53

1

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

在Linux下,进程和线程基本上是完全一样的。调度程序理解所谓的“任务”,它并不关心它们是否共享地址空间。分享或不分享真的事情取决于他们是如何创建的。

是否使用线程或进程是一个关键的设计决定,而不应掉以轻心,但调度的性能可能不是一个因素(像IPC要求当然事情会改变设计疯狂)