Linux文件共享(五)——線程共享文件
注:轉載請注明出處 作者:lvyilong316
4.1 Linux線程實現
在談論線程之間共享文件之前,我想首先簡單的介紹下linux線程的實現。最初的進程定義都包含程序、資源及其執行三部分,其中程序通常指代碼,資源在操作係統層麵上通常包括內存資源、IO資源、信號處理等部分,而程序的執行通常理解為執行上下文,包括對cpu的占用,後來發展為線程。在線程概念出現以前,為了減小進程切換的開銷,操作係統設計者逐漸修正進程的概念,逐漸允許將進程所占有的資源從其主體剝離出來,允許某些進程共享一部分資源,例如文件、信號,數據內存,甚至代碼,這就發展出輕量進程的概念。Linux內核在2.0.x版本就已經實現了輕量進程,應用程序可以通過一個統一的clone()係統調用接口,用不同的參數指定創建輕量進程還是普通進程。在內核中,clone()調用經過參數傳遞和解釋後會調用do_fork(),這個核內函數同時也是fork()、vfork()係統調用的最終實現:在do_fork()中,不同的clone_flags將導致不同的行為,對於LinuxThreads,它使用(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)參數來調用clone()創建"線程",表示共享內存、共享文件係統訪問計數、共享文件描述符表,以及共享信號處理方式。(其中前三個標誌對應共享的數據結構分別為task_struct中的mm,fs,files)。
(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)。也就是說,輕量級進程與父進程共享內存地址空間。
(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計數,與父進程共享該結構。也就是說,子進程與父進程的信號處理方式完全相同,而且可以相互更改。
4.2 線程打開文件分析
有了以上分析,我們就可以畫出線程間對應的打開文件數據結構的關係,如下圖所示。
我們看到,線程間所有文件結構都為共享資源,不但“文件表項”(file對象)是共享的,就連“文件描述符表”(files_struct結構)也是共享的。
總結:線程的創建僅僅增加的是files和fs的引用計數,“文件打開計數”(file對象的引用計數)並沒有增加,所以任何一個線程對打開的文件執行close操作,文件都將關閉(文件打開計數為1的情況)。但是如果線程不進行打開文件的關閉,則文件直到進程結束時才會關閉,這就是使用多線程實現tcp服務器時,服務線程必須要顯示調用close的原因,否則永遠不會發送FIN終止鏈接(因為主線程一直處於監聽不會結束)。
最後更新:2017-04-03 12:56:03