• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html, 作者:鄭彥興系統調用mmap()通過映射一個普通文件實現共享內存。系統V則是通過映射特殊文件系統shm中的文件實現進程間的共享內存通信。也就是說,每個共享內存區域對應特殊文件系統shm中的一個文件(這是通過shmid_kernel結構聯系起來的),后面還將闡述...  閱讀全文

            posted @ 2012-12-18 14:03 鑫龍 閱讀(238) | 評論 (0)編輯 收藏

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/, 作者:鄭彥興一、信號燈概述信號燈與其他進程間通信方式不大相同,它主要提供對進程間共享資源訪問控制機制。相當于內存中的標志,進程可以根據它判定是否能夠訪問某些共享資源,同時,進程也可以修改該標志。除了用于訪問控制外,還可用于進程同步。信號燈有以下兩種類型:二值信號燈:最簡單的信號燈形...  閱讀全文

            posted @ 2012-12-18 14:02 鑫龍 閱讀(344) | 評論 (0)編輯 收藏

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/, 作者:鄭彥興消息隊列(也叫做報文隊列)能夠克服早期unix通信機制的一些缺點。作為早期unix通信機制之一的信號能夠傳送的信息量有限,后來雖然 POSIX 1003.1b在信號的實時性方面作了拓廣,使得信號在傳遞信息量方面有了相當程度的改進,但是信號這種通信方式更像"即時"的通信方...  閱讀全文

            posted @ 2012-12-18 14:01 鑫龍 閱讀(317) | 評論 (0)編輯 收藏

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html, 作者:鄭彥興一、信號及信號來源信號本質信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來等待信號的到達,事實上,進程也不知道信號到底什么時候到達。信號是進程間通...  閱讀全文

            posted @ 2012-12-18 14:00 鑫龍 閱讀(273) | 評論 (0)編輯 收藏

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html,作者:鄭彥興一、信號生命周期從信號發送到信號處理函數的執行完畢對于一個完整的信號生命周期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:信號誕生;信號在進程中注冊完畢;信號在進程中的注銷完畢;信號處理函數執行完...  閱讀全文

            posted @ 2012-12-18 14:00 鑫龍 閱讀(244) | 評論 (0)編輯 收藏

                 摘要: 轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/, 作者:鄭彥興1、 管道概述及相關API應用1.1 管道相關的關鍵概念管道是Linux支持的最初Unix IPC形式之一,具有以下特點:管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);單獨構成一種獨立...  閱讀全文

            posted @ 2012-12-18 13:59 鑫龍 閱讀(316) | 評論 (0)編輯 收藏

            轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/index.html,作者:鄭彥興

            linux下的進程通信手段基本上是從Unix平臺上的進程通信手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力 AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟件發布中心)在進程間通信方面的側重點有所不同。前者對Unix早期的進程間通信手 段進行了系統的改進和擴充,形成了“system V IPC”,通信進程局限在單個計算機內;后者則跳過了該限制,形成了基于套接口(socket)的進程間通信機制。Linux則把兩者繼承了下來,如圖 示:


             

            其中,最初Unix IPC包括:管道、FIFO、信號;System V IPC包括:System V消息隊列、System V信號燈、System V共享內存區;Posix IPC包括: Posix消息隊列、Posix信號燈、Posix共享內存區。有兩點需要簡單說明一下:1)由于Unix版本的多樣性,電子電氣工程協會(IEEE)開 發了一個獨立的Unix標準,這個新的ANSI Unix標準被稱為計算機環境的可移植性操作系統界面(PSOIX)。現有大部分Unix和流行版本都是遵循POSIX標準的,而Linux從一開始就遵 循POSIX標準;2)BSD并不是沒有涉足單機內的進程間通信(socket本身就可以用于單機內的進程間通信)。事實上,很多Unix版本的單機 IPC留有BSD的痕跡,如4.4BSD支持的匿名內存映射、4.3+BSD對可靠信號語義的實現等等。

            圖一給出了linux 所支持的各種IPC手段,在本文接下來的討論中,為了避免概念上的混淆,在盡可能少提及Unix的各個版本的情況下,所有問題的討論最終都會歸結到 Linux環境下的進程間通信上來。并且,對于Linux所支持通信手段的不同實現版本(如對于共享內存來說,有Posix共享內存區以及System V共享內存區兩個實現版本),將主要介紹Posix API。

            linux下進程間通信的幾種主要手段簡介:

            1. 管道(Pipe)及有名管道(named pipe):管道可用于具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;
            2. 信號(Signal):信號是比較復雜的通信方式,用于通知接受進程有某種事件發生,除了用于進程間通信外,進程還可以發送信號 給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數 是基于BSD的,BSD為了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
            3. 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字 節流以及緩沖區大小受限等缺點。
            4. 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
            5. 信號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
            6. 套接口(Socket):更為一般的進程間通信機制,可用于不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。

            下面將對上述通信機制做具體闡述。

            附1:參考文獻[2]中對linux環境下的進程進行了概括說明:

            一般來說,linux下的進程包含以下幾個關鍵要素:

            • 有一段可執行程序;
            • 有專用的系統堆棧空間;
            • 內核中有它的控制塊(進程控制塊),描述進程所占用的資源,這樣,進程才能接受內核的調度;
            • 具有獨立的存儲空間

            進程和線程有時候并不完全區分,而往往根據上下文理解其含義。


            參考資料

            • UNIX環境高級編程,作者:W.Richard Stevens,譯者:尤晉元等,機械工業出版社。具有豐富的編程實例,以及關鍵函數伴隨Unix的發展歷程。

            • linux內核源代碼情景分析(上、下),毛德操、胡希明著,浙江大學出版社,提供了對linux內核非常好的分析,同時,對一些關鍵概念的背景進行了詳細的說明。

            • UNIX網絡編程第二卷:進程間通信,作者:W.Richard Stevens,譯者:楊繼張,清華大學出版社。一本比較全面闡述Unix環境下進程間通信的書(沒有信號和套接口,套接口在第一卷中)。

            posted @ 2012-12-18 13:58 鑫龍 閱讀(382) | 評論 (0)編輯 收藏

            轉自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/(楊沙洲 (pubb@163.net)國防科技大學計算機學院)  

            一.基礎知識:線程和進程

            按照教科書上的定義,進程是資源管理的最小單位,線程是程序執行的最小單位。在操作系統設計上,從進程演化出線程,最主要的目的就是更好的支持SMP以及減小(進程/線程)上下文切換開銷。

            無論按照怎樣的分法,一個進程至少需要一個線程作為它的指令執行體,進程管理著資源(比如cpu、內存、文件等等),而將線程分配到某 個cpu上執行。一個進程當然可以擁有多個線程,此時,如果進程運行在SMP機器上,它就可以同時使用多個cpu來執行各個線程,達到最大程度的并行,以 提高效率;同時,即使是在單cpu的機器上,采用多線程模型來設計程序,正如當年采用多進程模型代替單進程模型一樣,使設計更簡潔、功能更完備,程序的執 行效率也更高,例如采用多個線程響應多個輸入,而此時多線程模型所實現的功能實際上也可以用多進程模型來實現,而與后者相比,線程的上下文切換開銷就比進 程要小多了,從語義上來說,同時響應多個輸入這樣的功能,實際上就是共享了除cpu以外的所有資源的。

            針對線程模型的兩大意義,分別開發出了核心級線程和用戶級線程兩種線程模型,分類的標準主要是線程的調度者在核內還是在核外。前者更利 于并發使用多處理器的資源,而后者則更多考慮的是上下文切換開銷。在目前的商用系統中,通常都將兩者結合起來使用,既提供核心線程以滿足smp系統的需 要,也支持用線程庫的方式在用戶態實現另一套線程機制,此時一個核心線程同時成為多個用戶態線程的調度者。正如很多技術一樣,"混合"通常都能帶來更高的 效率,但同時也帶來更大的實現難度,出于"簡單"的設計思路,Linux從一開始就沒有實現混合模型的計劃,但它在實現上采用了另一種思路的"混合"。

            在線程機制的具體實現上,可以在操作系統內核上實現線程,也可以在核外實現,后者顯然要求核內至少實現了進程,而前者則一般要求在核內 同時也支持進程。核心級線程模型顯然要求前者的支持,而用戶級線程模型則不一定基于后者實現。這種差異,正如前所述,是兩種分類方式的標準不同帶來的。

            當核內既支持進程也支持線程時,就可以實現線程-進程的"多對多"模型,即一個進程的某個線程由核內調度,而同時它也可以作為用戶級線 程池的調度者,選擇合適的用戶級線程在其空間中運行。這就是前面提到的"混合"線程模型,既可滿足多處理機系統的需要,也可以最大限度的減小調度開銷。絕 大多數商業操作系統(如Digital Unix、Solaris、Irix)都采用的這種能夠完全實現POSIX1003.1c標準的線程模型。在核外實現的線程又可以分為"一對一"、"多對 一"兩種模型,前者用一個核心進程(也許是輕量進程)對應一個線程,將線程調度等同于進程調度,交給核心完成,而后者則完全在核外實現多線程,調度也在用 戶態完成。后者就是前面提到的單純的用戶級線程模型的實現方式,顯然,這種核外的線程調度器實際上只需要完成線程運行棧的切換,調度開銷非常小,但同時因 為核心信號(無論是同步的還是異步的)都是以進程為單位的,因而無法定位到線程,所以這種實現方式不能用于多處理器系統,而這個需求正變得越來越大,因 此,在現實中,純用戶級線程的實現,除算法研究目的以外,幾乎已經消失了。

            Linux內核只提供了輕量進程的支持,限制了更高效的線程模型的實現,但Linux著重優化了進程的調度開銷,一定程度上也彌補了這 一缺陷。目前最流行的線程機制LinuxThreads所采用的就是線程-進程"一對一"模型,調度交給核心,而在用戶級實現一個包括信號處理在內的線程 管理機制。Linux-LinuxThreads的運行機制正是本文的描述重點。

            二.Linux 2.4內核中的輕量進程實現

            最初的進程定義都包含程序、資源及其執行三部分,其中程序通常指代碼,資源在操作系統層面上通常包括內存資源、IO資源、信號處理等部 分,而程序的執行通常理解為執行上下文,包括對cpu的占用,后來發展為線程。在線程概念出現以前,為了減小進程切換的開銷,操作系統設計者逐漸修正進程 的概念,逐漸允許將進程所占有的資源從其主體剝離出來,允許某些進程共享一部分資源,例如文件、信號,數據內存,甚至代碼,這就發展出輕量進程的概念。 Linux內核在2.0.x版本就已經實現了輕量進程,應用程序可以通過一個統一的clone()系統調用接口,用不同的參數指定創建輕量進程還是普通進 程。在內核中,clone()調用經過參數傳遞和解釋后會調用do_fork(),這個核內函數同時也是fork()、vfork()系統調用的最終實 現:

            <linux-2.4.20/kernel/fork.c> int do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size)

            其中的clone_flags取自以下宏的"或"值:

            <linux-2.4.20/include/linux/sched.h> #define CSIGNAL 0x000000ff /* signal mask to be sent at exit */ #define CLONE_VM 0x00000100 /* set if VM shared between processes */ #define CLONE_FS 0x00000200 /* set if fs info shared between processes */ #define CLONE_FILES 0x00000400 /* set if open files shared between processes */ #define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ #define CLONE_PID 0x00001000 /* set if pid shared */ #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ #define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ #define CLONE_THREAD 0x00010000 /* Same thread group? */ #define CLONE_NEWNS 0x00020000 /* New namespace group? */ #define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD)

            在do_fork()中,不同的clone_flags將導致不同的行為,對于LinuxThreads,它使用(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)參數來調用clone()創建"線程",表示共享內存、共享文件系統訪問計數、共享文件描述符表,以及共享信號處理方式。本 節就針對這幾個參數,看看Linux內核是如何實現這些資源的共享的。

            1.CLONE_VM

            do_fork()需要調用copy_mm()來設置task_struct中的mm和active_mm項,這兩個 mm_struct數據與進程所關聯的內存空間相對應。如果do_fork()時指定了CLONE_VM開關,copy_mm()將把新的 task_struct中的mm和active_mm設置成與current的相同,同時提高該mm_struct的使用者數目 (mm_struct::mm_users)。也就是說,輕量級進程與父進程共享內存地址空間,由下圖示意可以看出mm_struct在進程中的地位:


             

            2.CLONE_FS

            task_struct中利用fs(struct fs_struct *)記錄了進程所在文件系統的根目錄和當前目錄信息,do_fork()時調用copy_fs()復制了這個結構;而對于輕量級進程則僅增加 fs->count計數,與父進程共享相同的fs_struct。也就是說,輕量級進程沒有獨立的文件系統相關的信息,進程中任何一個線程改變當前 目錄、根目錄等信息都將直接影響到其他線程。

            3.CLONE_FILES

            一個進程可能打開了一些文件,在進程結構task_struct中利用files(struct files_struct *)來保存進程打開的文件結構(struct file)信息,do_fork()中調用了copy_files()來處理這個進程屬性;輕量級進程與父進程是共享該結構的,copy_files() 時僅增加files->count計數。這一共享使得任何線程都能訪問進程所維護的打開文件,對它們的操作會直接反映到進程中的其他線程。

            4.CLONE_SIGHAND

            每一個Linux進程都可以自行定義對信號的處理方式,在task_struct中的sig(struct signal_struct)中使用一個struct k_sigaction結構的數組來保存這個配置信息,do_fork()中的copy_sighand()負責復制該信息;輕量級進程不進行復制,而僅 僅增加signal_struct::count計數,與父進程共享該結構。也就是說,子進程與父進程的信號處理方式完全相同,而且可以相互更改。

            do_fork()中所做的工作很多,在此不詳細描述。對于SMP系統,所有的進程fork出來后,都被分配到與父進程相同的cpu上,一直到該進程被調度時才會進行cpu選擇。

            盡管Linux支持輕量級進程,但并不能說它就支持核心級線程,因為Linux的"線程"和"進程"實際上處于一個調度層次,共享一個 進程標識符空間,這種限制使得不可能在Linux上實現完全意義上的POSIX線程機制,因此眾多的Linux線程庫實現嘗試都只能盡可能實現POSIX 的絕大部分語義,并在功能上盡可能逼近。

            三.LinuxThread的線程機制

            LinuxThreads是目前Linux平臺上使用最為廣泛的線程庫,由Xavier Leroy (Xavier.Leroy@inria.fr)負責開發完成,并已綁定在GLIBC中發行。它所實現的就是基于核心輕量級進程的"一對一"線程模型,一 個線程實體對應一個核心輕量級進程,而線程之間的管理在核外函數庫中實現。

            1.線程描述數據結構及實現限制

            LinuxThreads定義了一個struct _pthread_descr_struct數據結構來描述線程,并使用全局數組變量__pthread_handles來描述和引用進程所轄線程。在 __pthread_handles中的前兩項,LinuxThreads定義了兩個全局的系統線程:__pthread_initial_thread 和__pthread_manager_thread,并用__pthread_main_thread表征 __pthread_manager_thread的父線程(初始為__pthread_initial_thread)。

            struct _pthread_descr_struct是一個雙環鏈表結構,__pthread_manager_thread所在的鏈表僅包括它一個元素,實際 上,__pthread_manager_thread是一個特殊線程,LinuxThreads僅使用了其中的errno、p_pid、 p_priority等三個域。而__pthread_main_thread所在的鏈則將進程中所有用戶線程串在了一起。經過一系列 pthread_create()之后形成的__pthread_handles數組將如下圖所示:


            圖2 __pthread_handles數組結構 

            新創建的線程將首先在__pthread_handles數組中占據一項,然后通過數據結構中的鏈指針連入以__pthread_main_thread為首指針的鏈表中。這個鏈表的使用在介紹線程的創建和釋放的時候將提到。

            LinuxThreads遵循POSIX1003.1c標準,其中對線程庫的實現進行了一些范圍限制,比如進程最大線程數,線程私有數 據區大小等等。在LinuxThreads的實現中,基本遵循這些限制,但也進行了一定的改動,改動的趨勢是放松或者說擴大這些限制,使編程更加方便。這 些限定宏主要集中在sysdeps/unix/sysv/linux/bits/local_lim.h(不同平臺使用的文件位置不同)中,包括如下幾 個:

            每進程的私有數據key數,POSIX定義_POSIX_THREAD_KEYS_MAX為128,LinuxThreads使用 PTHREAD_KEYS_MAX,1024;私有數據釋放時允許執行的操作數,LinuxThreads與POSIX一致,定義 PTHREAD_DESTRUCTOR_ITERATIONS為4;每進程的線程數,POSIX定義為64,LinuxThreads增大到 1024(PTHREAD_THREADS_MAX);線程運行棧最小空間大小,POSIX未指定,LinuxThreads使用 PTHREAD_STACK_MIN,16384(字節)。

            2.管理線程

            "一對一"模型的好處之一是線程的調度由核心完成了,而其他諸如線程取消、線程間的同步等工作,都是在核外線程庫中完成的。在 LinuxThreads中,專門為每一個進程構造了一個管理線程,負責處理線程相關的管理工作。當進程第一次調用pthread_create()創建 一個線程的時候就會創建(__clone())并啟動管理線程。

            在一個進程空間內,管理線程與其他線程之間通過一對"管理管道(manager_pipe[2])"來通訊,該管道在創建管理線程之前 創建,在成功啟動了管理線程之后,管理管道的讀端和寫端分別賦給兩個全局變量__pthread_manager_reader和 __pthread_manager_request,之后,每個用戶線程都通過__pthread_manager_request向管理線程發請求, 但管理線程本身并沒有直接使用__pthread_manager_reader,管道的讀端(manager_pipe[0])是作為 __clone()的參數之一傳給管理線程的,管理線程的工作主要就是監聽管道讀端,并對從中取出的請求作出反應。

            創建管理線程的流程如下所示: 
            (全局變量pthread_manager_request初值為-1)


            圖3 創建管理線程的流程 

            初始化結束后,在__pthread_manager_thread中記錄了輕量級進程號以及核外分配和管理的線程 id,2*PTHREAD_THREADS_MAX+1這個數值不會與任何常規用戶線程id沖突。管理線程作為pthread_create()的調用者 線程的子線程運行,而pthread_create()所創建的那個用戶線程則是由管理線程來調用clone()創建,因此實際上是管理線程的子線程。 (此處子線程的概念應該當作子進程來理解。)

            __pthread_manager()就是管理線程的主循環所在,在進行一系列初始化工作后,進入while(1)循環。在循環中, 線程以2秒為timeout查詢(__poll())管理管道的讀端。在處理請求前,檢查其父線程(也就是創建manager的主線程)是否已退出,如果 已退出就退出整個進程。如果有退出的子線程需要清理,則調用pthread_reap_children()清理。

            然后才是讀取管道中的請求,根據請求類型執行相應操作(switch-case)。具體的請求處理,源碼中比較清楚,這里就不贅述了。

            3.線程棧

            在LinuxThreads中,管理線程的棧和用戶線程的棧是分離的,管理線程在進程堆中通過malloc()分配一個THREAD_MANAGER_STACK_SIZE字節的區域作為自己的運行棧。

            用戶線程的棧分配辦法隨著體系結構的不同而不同,主要根據兩個宏定義來區分,一個是 NEED_SEPARATE_REGISTER_STACK,這個屬性僅在IA64平臺上使用;另一個是FLOATING_STACK宏,在i386等少 數平臺上使用,此時用戶線程棧由系統決定具體位置并提供保護。與此同時,用戶還可以通過線程屬性結構來指定使用用戶自定義的棧。因篇幅所限,這里只能分析 i386平臺所使用的兩種棧組織方式:FLOATING_STACK方式和用戶自定義方式。

            在FLOATING_STACK方式下,LinuxThreads利用mmap()從內核空間中分配8MB空間(i386系統缺省的最 大棧空間大小,如果有運行限制(rlimit),則按照運行限制設置),使用mprotect()設置其中第一頁為非訪問區。該8M空間的功能分配如下 圖:


            圖4 棧結構示意 

            低地址被保護的頁面用來監測棧溢出。

            對于用戶指定的棧,在按照指針對界后,設置線程棧頂,并計算出棧底,不做保護,正確性由用戶自己保證。

            不論哪種組織方式,線程描述結構總是位于棧頂緊鄰堆棧的位置。

            4.線程id和進程id

            每個LinuxThreads線程都同時具有線程id和進程id,其中進程id就是內核所維護的進程號,而線程id則由LinuxThreads分配和維護。

            __pthread_initial_thread的線程id為 PTHREAD_THREADS_MAX,__pthread_manager_thread的是2*PTHREAD_THREADS_MAX+1,第一 個用戶線程的線程id為PTHREAD_THREADS_MAX+2,此后第n個用戶線程的線程id遵循以下公式:

            tid=n*PTHREAD_THREADS_MAX+n+1

            這種分配方式保證了進程中所有的線程(包括已經退出)都不會有相同的線程id,而線程id的類型pthread_t定義為無符號長整型(unsigned long int),也保證了有理由的運行時間內線程id不會重復。

            從線程id查找線程數據結構是在pthread_handle()函數中完成的,實際上只是將線程號按PTHREAD_THREADS_MAX取模,得到的就是該線程在__pthread_handles中的索引。

            5.線程的創建

            在pthread_create()向管理線程發送REQ_CREATE請求之后,管理線程即調用 pthread_handle_create()創建新線程。分配棧、設置thread屬性后,以pthread_start_thread()為函數入 口調用__clone()創建并啟動新線程。pthread_start_thread()讀取自身的進程id號存入線程描述結構中,并根據其中記錄的調 度方法配置調度。一切準備就緒后,再調用真正的線程執行函數,并在此函數返回后調用pthread_exit()清理現場。

            6.LinuxThreads的不足

            由于Linux內核的限制以及實現難度等等原因,LinuxThreads并不是完全POSIX兼容的,在它的發行README中有說明。

            1)進程id問題

            這個不足是最關鍵的不足,引起的原因牽涉到LinuxThreads的"一對一"模型。

            Linux內核并不支持真正意義上的線程,LinuxThreads是用與普通進程具有同樣內核調度視圖的輕量級進程來實現線程支持 的。這些輕量級進程擁有獨立的進程id,在進程調度、信號處理、IO等方面享有與普通進程一樣的能力。在源碼閱讀者看來,就是Linux內核的 clone()沒有實現對CLONE_PID參數的支持。

            在內核do_fork()中對CLONE_PID的處理是這樣的:

            if (clone_flags & CLONE_PID) { if (current->pid) goto fork_out; }

            這段代碼表明,目前的Linux內核僅在pid為0的時候認可CLONE_PID參數,實際上,僅在SMP初始化,手工創建進程的時候才會使用CLONE_PID參數。

            按照POSIX定義,同一進程的所有線程應該共享一個進程id和父進程id,這在目前的"一對一"模型下是無法實現的。

            2)信號處理問題

            由于異步信號是內核以進程為單位分發的,而LinuxThreads的每個線程對內核來說都是一個進程,且沒有實現"線程組",因此,某些語義不符合POSIX標準,比如沒有實現向進程中所有線程發送信號,README對此作了說明。

            如果核心不提供實時信號,LinuxThreads將使用SIGUSR1和SIGUSR2作為內部使用的restart和cancel 信號,這樣應用程序就不能使用這兩個原本為用戶保留的信號了。在Linux kernel 2.1.60以后的版本都支持擴展的實時信號(從_SIGRTMIN到_SIGRTMAX),因此不存在這個問題。

            某些信號的缺省動作難以在現行體系上實現,比如SIGSTOP和SIGCONT,LinuxThreads只能將一個線程掛起,而無法掛起整個進程。

            3)線程總數問題

            LinuxThreads將每個進程的線程最大數目定義為1024,但實際上這個數值還受到整個系統的總進程數限制,這又是由于線程其實是核心進程。

            在kernel 2.4.x中,采用一套全新的總進程數計算方法,使得總進程數基本上僅受限于物理內存的大小,計算公式在kernel/fork.c的fork_init()函數中:

            max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8

            在i386上,THREAD_SIZE=2*PAGE_SIZE,PAGE_SIZE=2^12(4KB),mempages=物理內 存大小/PAGE_SIZE,對于256M的內存的機器,mempages=256*2^20/2^12=256*2^8,此時最大線程數為4096。

            但為了保證每個用戶(除了root)的進程總數不至于占用一半以上物理內存,fork_init()中繼續指定:

            init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2; init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;

            這些進程數目的檢查都在do_fork()中進行,因此,對于LinuxThreads來說,線程總數同時受這三個因素的限制。

            4)管理線程問題

            管理線程容易成為瓶頸,這是這種結構的通病;同時,管理線程又負責用戶線程的清理工作,因此,盡管管理線程已經屏蔽了大部分的信號,但一旦管理線程死亡,用戶線程就不得不手工清理了,而且用戶線程并不知道管理線程的狀態,之后的線程創建等請求將無人處理。

            5)同步問題

            LinuxThreads中的線程同步很大程度上是建立在信號基礎上的,這種通過內核復雜的信號處理機制的同步方式,效率一直是個問題。

            6)其他POSIX兼容性問題

            Linux中很多系統調用,按照語義都是與進程相關的,比如nice、setuid、setrlimit等,在目前的LinuxThreads中,這些調用都僅僅影響調用者線程。

            7)實時性問題

            線程的引入有一定的實時性考慮,但LinuxThreads暫時不支持,比如調度選項,目前還沒有實現。不僅LinuxThreads如此,標準的Linux在實時性上考慮都很少。

            四.其他的線程實現機制

            LinuxThreads的問題,特別是兼容性上的問題,嚴重阻礙了Linux上的跨平臺應用(如Apache)采用多線程設計,從而 使得Linux上的線程應用一直保持在比較低的水平。在Linux社區中,已經有很多人在為改進線程性能而努力,其中既包括用戶級線程庫,也包括核心級和 用戶級配合改進的線程庫。目前最為人看好的有兩個項目,一個是RedHat公司牽頭研發的NPTL(Native Posix Thread Library),另一個則是IBM投資開發的NGPT(Next Generation Posix Threading),二者都是圍繞完全兼容POSIX 1003.1c,同時在核內和核外做工作以而實現多對多線程模型。這兩種模型都在一定程度上彌補了LinuxThreads的缺點,且都是重起爐灶全新設 計的。

            1.NPTL

            NPTL的設計目標歸納可歸納為以下幾點:

            • POSIX兼容性
            • SMP結構的利用
            • 低啟動開銷
            • 低鏈接開銷(即不使用線程的程序不應當受線程庫的影響)
            • 與LinuxThreads應用的二進制兼容性
            • 軟硬件的可擴展能力
            • 多體系結構支持
            • NUMA支持
            • 與C++集成

            在技術實現上,NPTL仍然采用1:1的線程模型,并配合glibc和最新的Linux Kernel2.5.x開發版在信號處理、線程同步、存儲管理等多方面進行了優化。和LinuxThreads不同,NPTL沒有使用管理線程,核心線程 的管理直接放在核內進行,這也帶了性能的優化。

            主要是因為核心的問題,NPTL仍然不是100%POSIX兼容的,但就性能而言相對LinuxThreads已經有很大程度上的改進了。

            2.NGPT

            IBM的開放源碼項目NGPT在2003年1月10日推出了穩定的2.2.0版,但相關的文檔工作還差很多。就目前所知,NGPT是基于GNU Pth(GNU Portable Threads)項目而實現的M:N模型,而GNU Pth是一個經典的用戶級線程庫實現。

            按照2003年3月NGPT官方網站上的通知,NGPT考慮到NPTL日益廣泛地為人所接受,為避免不同的線程庫版本引起的混亂,今后將不再進行進一步開發,而今進行支持性的維護工作。也就是說,NGPT已經放棄與NPTL競爭下一代Linux POSIX線程庫標準。

            3.其他高效線程機制

            此處不能不提到Scheduler Activations。這個1991年在ACM上發表的多線程內核結構影響了很多多線程內核的設計,其中包括Mach3.0、NetBSD和商業版本 Digital Unix(現在叫Compaq True64 Unix)。它的實質是在使用用戶級線程調度的同時,盡可能地減少用戶級對核心的系統調用請求,而后者往往是運行開銷的重要來源。采用這種結構的線程機 制,實際上是結合了用戶級線程的靈活高效和核心級線程的實用性,因此,包括Linux、FreeBSD在內的多個開放源碼操作系統設計社區都在進行相關研 究,力圖在本系統中實現Scheduler Activations。

            轉自:http://blog.chinaunix.net/uid-20556054-id-3075951.html

            posted @ 2012-12-18 13:57 鑫龍 閱讀(559) | 評論 (0)編輯 收藏

                 摘要: 實驗環境:CPU: 雙核 Intel(R) Xeon(R) CPU 5130  @ 2.00GHz,內存:1G系統:Red Hat Enterprise Linux ES release 4 (Nahant Update 4)內核:2.6.9-42.ELsmp實驗程序:singlethread.c#include <stdio.h>#include <...  閱讀全文

            posted @ 2012-12-18 13:56 鑫龍 閱讀(593) | 評論 (0)編輯 收藏

            (注:這篇文章轉自網絡,雖然Linux從內核2.6開始,多線程已使用NPTL技術,但是這篇文章對我們理解多線程技術還是挺有用的)

            Linux內核對多進程和多線程的支持方式:

                    線程機制支持并發程序設計技術,在多處理器上能真正保證并行處理。而在linux實現線程很特別,linux把所有的線程都當作進程實現。linux下線 程看起來就像普通進程(只是該進程和其他進程共享資源,如地址空間)。上述機制與Microsoft windows或是Sun Solaris實現差異很大。

                    Linux的線程實現是在核外進行的,核內提供的是創建進程的接口do_fork()。內核提供了兩個系統調用__clone()和fork(),最終都 用不同的參數調用do_fork()核內API。 do_fork() 提供了很多參數,包括CLONE_VM(共享內存空間)、CLONE_FS(共享文件系統信息)、CLONE_FILES(共享文件描述符表)、 CLONE_SIGHAND(共享信號句柄表)和CLONE_PID(共享進程ID,僅對核內進程,即0號進程有效)。當使用fork系統調用產生多進程 時,內核調用do_fork()不使用任何共享屬性,進程擁有獨立的運行環境。當使用pthread_create()來創建線程時,則最終設置了所有這 些屬性來調用__clone(),而這些參數又全部傳給核內的do_fork(),從而創建的”進程”擁有共享的運行環境,只有棧是獨立的,由 __clone()傳入。

                     即:Linux下不管是多線程編程還是多進程編程,最終都是用do_fork實現的 多進程編程,只是進程創建時的參數不同,從而導致有不同的共享環境。Linux線程在核內是以輕量級進程的形式存在的,擁有獨立的進程表項,而所有的創 建、同步、刪除等操作都在核外pthread庫中進行。pthread 庫使用一個管理線程(__pthread_manager() ,每個進程獨立且唯一)來管理線程的創建和終止,為線程分配線程ID,發送線程相關的信號,而主線程pthread_create()) 的調用者則通過管道將請求信息傳給管理線程。

            很多朋友都說使用多線程的好處是資源占用少,其隱含之意就是說進程占用資源比線程多,對吧?但實際上Linux下多進程是否就真的點用很多資源呢? 暫且不說進程是否比線程占用資源多,就進程占用資源的多少情況而言,Linux確實是做得相當節省的。產生一個多進程時肯定是要產生的一點內存是要復制進 程表項,即一個task_struct結構,但這個結構本身做得相當小巧。其它對于一個進程來說必須有的數據段、代碼段、堆棧段是不是全盤復制呢?對于多 進程來說,代碼段是肯定不用復制的,因為父進程和各子進程的代碼段是相同的,數據段和堆棧段呢?也不一定,因為在Linux里廣泛使用的一個技術叫 copy-on-write,即寫時拷貝。copy-on-write意味著什么呢?意味著資源節省,假設有一個變量x在父進程里存在,當這個父進程創建 一個子進程或多個子進程時這個變量x是否復制到了子進程的內存空間呢?不會的,子進程和父進程使用同一個內存空間的變量,但當子進程或父進程要改變變量x 的值時就會復制該變量,從而導致父子進程里的變量值不同。父子進程變量是互不影響的,由于父子進程地址空間是完全隔開的,變量的地址可以是完全相同的

                      Linux的”線程”和”進程”實際上處于一個調度層次,共享一個進程標識符空間,這種限制使得不可能在Linux上實現完全意義上的POSIX線程機 制,因此眾多的Linux線程庫實現嘗試都只能盡可能實現POSIX的絕大部分語義,并在功能上盡可能逼近。Linux進程的創建是非常迅速的。內核設計 與實現一書中甚至指出Linux創建進程的速度和其他針對線程優化的操作系統(Windows,Solaris)創建線程的速度相比,測試結果非常的好, 也就是說創建速度很快。由于異步信號是內核以進程為單位分發的,而LinuxThreads的每個線程對內核來說都是一個進程,且沒有實現”線程組”,因 此,某些語義不符合POSIX標準,比如沒有實現向進程中所有線程發送信號,README對此作了說明。LinuxThreads中的線程同步很大程度上 是建立在信號基礎上的,這種通過內核復雜的信號處理機制的同步方式,效率一直是個問題。LinuxThreads 的問題,特別是兼容性上的問題,嚴重阻礙了Linux上的跨平臺應用(如Apache)采用多線程設計,從而使得Linux上的線程應用一直保持在比較低 的水平。在Linux社區中,已經有很多人在為改進線程性能而努力,其中既包括用戶級線程庫,也包括核心級和用戶級配合改進的線程庫。目前最為人看好的有 兩個項目,一個是RedHat公司牽頭研發的NPTL(Native Posix Thread Library),另一個則是IBM投資開發的NGPT(Next Generation Posix Threading),二者都是圍繞完全兼容POSIX 1003.1c,同時在核內和核外做工作以而實現多對多線程模型。這兩種模型都在一定程度上彌補了LinuxThreads的缺點,且都是重起爐灶全新設 計的。

                      綜上所述的結論是在Linux下編程多用多進程編程少用多線程編程

                     IBM有個家伙做了個測試,發現切換線程context的時候,windows比linux快一倍多。進出最快的鎖(windows2k的 critical section和linux的pthread_mutex),windows比linux的要快五倍左右。當然這并不是說linux不好,而且在經過實際 編程之后,綜合來看我覺得linux更適合做high performance server,不過在多線程這個具體的領域內,linux還是稍遜windows一點。這應該是情有可原的,畢竟unix家族都是從多進程過來的,而 windows從頭就是多線程的。

            如果是UNIX/linux環境,采用多線程沒必要。

            多線程比多進程性能高?誤導!

            應該說,多線程比多進程成本低,但性能更低

            在UNIX環境,多進程調度開銷比多線程調度開銷,沒有顯著區別,就是說,UNIX進程調度效率是很高的。內存消耗方面,二者只差全局數據區,現在內存都很便宜,服務器內存動輒若干G,根本不是問題。

            多進程是立體交通系統,雖然造價高,上坡下坡多耗點油,但是不堵車。

            多線程是平面交通系統,造價低,但紅綠燈太多,老堵車。

            我們現在都開跑車,油(主頻)有的是,不怕上坡下坡,就怕堵車。

            高性能交易服務器中間件,如TUXEDO,都是主張多進程的。實際測試表明,TUXEDO性能和并發效率是非常高的。TUXEDO是貝爾實驗室的,與UNIX同宗,應該是對UNIX理解最為深刻的,他們的意見應該具有很大的參考意義

            注:

            關于那個critical section和pthread_mutex_t,critical section本質上是一個自旋鎖,短期鎖當然快,要比也該是和pthread_spinlock_t比。

            mutex和critical section的確是不能比的。一個涉及到內核,一個沒有涉及到內核。


            轉自http://www.soft-bin.com/html/2010/07/09/%E5%A4%9A%E8%BF%9B%E7%A8%8Bvs%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%8C%E4%B8%80%E4%B8%AA%E9%95%BF%E6%9C%9F%E7%9A%84%E4%BA%89%E8%AE%BA.html,貌似他也是轉別人的。 

            轉自:http://blog.chinaunix.net/uid-20556054-id-3068371.html

            posted @ 2012-12-18 13:55 鑫龍 閱讀(417) | 評論 (0)編輯 收藏

            僅列出標題
            共20頁: First 8 9 10 11 12 13 14 15 16 Last 
            久久国产精品无码一区二区三区 | 亚洲一区精品伊人久久伊人| 88久久精品无码一区二区毛片| 99久久99这里只有免费的精品| 成人免费网站久久久| 亚洲国产成人久久综合碰碰动漫3d | 一本色道久久综合狠狠躁篇| 久久天天躁狠狠躁夜夜96流白浆| 久久精品国产亚洲网站| 一本一本久久A久久综合精品| 久久精品国产亚洲AV大全| 国产三级观看久久| 99久久99久久精品免费看蜜桃| 久久久WWW成人免费精品| 久久国产欧美日韩精品| 一本久久a久久精品综合香蕉| 国产Av激情久久无码天堂| 国产精品久久久久国产A级| 久久久久97国产精华液好用吗| 久久99亚洲网美利坚合众国| 亚洲七七久久精品中文国产| 久久se精品一区二区影院 | 亚洲AV成人无码久久精品老人| 久久精品国产亚洲AV影院| 久久精品一区二区三区中文字幕| 久久久久久午夜成人影院 | 四虎国产精品成人免费久久| 欧美精品一区二区久久| 无码人妻少妇久久中文字幕| 亚洲天堂久久精品| 日本精品久久久久中文字幕8| 99久久人妻无码精品系列蜜桃| 亚洲精品乱码久久久久久中文字幕 | 久久亚洲国产成人精品无码区| 国产国产成人精品久久| 久久国产精品99国产精| 久久丫精品国产亚洲av| 日本欧美久久久久免费播放网| 看久久久久久a级毛片| 国产成人久久AV免费| 91精品国产高清久久久久久io |