日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux进程与线程的区别 详细总结(面试经验总结)

發布時間:2023/11/27 生活经验 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux进程与线程的区别 详细总结(面试经验总结) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,簡要了解一下進程和線程。對于操作系統而言,進程是核心之核心,整個現代操作系統的根本,就是以進程為單位在執行任務。系統的管理架構也是基于進程層面的。在按下電源鍵之后,計算機就開始了復雜的啟動過程,此處有一個經典問題:當按下電源鍵之后,計算機如何把自己由靜止啟動起來的?本文不討論系統啟動過程,請讀者自行科普。操作系統啟動的過程簡直可以描述為上帝創造萬物的過程,期初沒有世界,但是有上帝,是上帝創造了世界,之后創造了萬物,然后再創造了人,然后塑造了人的七情六欲,再然后人類社會開始遵循自然規律繁衍生息。。。操作系統啟動進程的階段就相當于上帝造人的階段。本文討論的全部內容都是“上帝造人”之后的事情。

第一個被創造出來的進程是0號進程,這個進程在操作系統層面是不可見的,但它存在著。0號進程完成了操作系統的功能加載與初期設定,然后它創造了1號進程(init),這個1號進程就是操作系統的“耶穌”。1號進程是上帝派來管理整個操作系統的,所以在用pstree查看進程樹可知,1號進程位于樹根。再之后,系統的很多管理程序都以進程身份被1號進程創造出來,還創造了與人類溝通的橋梁——shell。從那之后,人類可以跟操作系統進行交流,可以編寫程序,可以執行任務。。。

而這一切,都是基于進程的。每一個任務(進程)被創建時,系統會為他分配存儲空間等必要資源,然后在內核管理區為該進程創建管理節點,以便后來控制和調度該任務的執行。

進程真正進入執行階段,還需要獲得CPU的使用權,這一切都是操作系統掌管著,也就是所謂的調度,在各種條件滿足(資源與CPU使用權均獲得)的情況下,啟動進程的執行過程。

除CPU而外,一個很重要的資源就是存儲器了,系統會為每個進程分配獨有的存儲空間,當然包括它特別需要的別的資源,比如寫入時外部設備是可使用狀態等等。有了上面的引入,我們可以對進程做一個簡要的總結:

  • 進程,是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。它的執行需要系統分配資源創建實體之后,才能進行。

隨著技術發展,在執行一些細小任務時,本身無需分配單獨資源時(多個任務共享同一組資源即可,比如所有子進程共享父進程的資源),進程的實現機制依然會繁瑣的將資源分割,這樣造成浪費,而且還消耗時間。后來就有了專門的多任務技術被創造出來——線程。

線程的特點就是在不需要獨立資源的情況下就可以運行。如此一來會極大節省資源開銷,以及處理時間。

1.好了,前面的一段文字是簡要引入兩個名詞,即進程和線程。本文討論目標是解釋清楚進程和線程的區別,關于二者的技術實現,請讀者查閱相關資料。

下面我們開始重點討論本文核心了。從下面幾個方面闡述進程和線程的區別。

1).二者的相同點

2).實現方式的差異

3).多任務程序設計模式的區別

4).實體間(進程間,線程間,進線程間)通信方式的不同

5).控制方式的異同

6).資源管理方式的異同

7).個體間輩分關系的迥異

8).進程池與線程池的技術實現差別

接下來我們就逐個進行解釋。

1).二者的相同點

無論是進程還是線程,對于程序員而言,都是用來實現多任務并發的技術手段。二者都可以獨立調度,因此在多任務環境下,功能上并無差異。并且二者都具有各自的實體,是系統獨立管理的對象個體。所以在系統層面,都可以通過技術手段實現二者的控制。而且二者所具有的狀態都非常相似。而且,在多任務程序中,子進程(子線程)的調度一般與父進程(父線程)平等競爭。

其實在Linux內核2.4版以前,線程的實現和管理方式就是完全按照進程方式實現的。在2.6版內核以后才有了單獨的線程實現。

?

2).實現方式的差異

進程是資源分配的基本單位,線程是調度的基本單位

這句經典名言已流傳數十年,各種操作系統教材都可見此描述。確實如此,這就是二者的顯著區別。讀者請注意“基本”二字。相信有讀者看到前半句的時候就在心里思考,“進程豈不是不能調度?”,非也!進程和線程都可以被調度,否則多進程程序該如何運行呢!

只是,線程是更小的可以調度的單位,也就是說,只要達到線程的水平就可以被調度了,進程自然可以被調度。它強調的是分配資源時的對象必須是進程,不會給一個線程單獨分配系統管理的資源。若要運行一個任務,想要獲得資源,最起碼得有進程,其他子任務可以以線程身份運行,資源共享就行了

簡而言之,進程的個體間是完全獨立的,而線程間是彼此依存的。多進程環境中,任何一個進程的終止,不會影響到其他進程。而多線程環境中,父線程終止,全部子線程被迫終止(沒有了資源)。而任何一個子線程終止一般不會影響其他線程,除非子線程執行了exit()系統調用。任何一個子線程執行exit(),全部線程同時滅亡。

其實,也沒有人寫出只有線程而沒有進程的程序。多線程程序中至少有一個主線程,而這個主線程其實就是有main函數的進程。它是整個程序的進程,所有線程都是它的子線程。我們通常把具有多線程的主進程稱之為主線程。

從系統實現角度講,進程的實現是調用fork系統調用:

pid_t fork(void);

線程的實現是調用clone系統調用:

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...

/* pid_t *ptid, struct user_desc *tls, pid_t *ctid */);

其中,fork()是將父進程的全部資源復制給了子進程。而線程的clone只是復制了一小部分必要的資源。在調用clone時可以通過參數控制要復制的對象。可以說,fork實現的是clone的加強完整版。當然,后來操作系統還進一步優化fork實現——寫時復制技術。在子進程需要復制資源(比如子進程執行寫入動作更改父進程內存空間)時才復制,否則創建子進程時先不復制。

實際中,編寫多進程程序時采用fork創建子進程實體。而創建線程時并不采用clone系統調用,而是采用線程庫函數。常用線程庫有Linux-Native線程庫和POSIX線程庫。其中應用最為廣泛的是POSIX線程庫。因此讀者在多線程程序中看到的是pthread_create而非clone。

我們知道,庫是建立在操作系統層面上的功能集合,因而它的功能都是操作系統提供的。由此可知,線程庫的內部很可能實現了clone的調用。不管是進程還是線程的實體,都是操作系統上運行的實體。

????最后,我們說一下vfork()?。這也是一個系統調用,用來創建一個新的進程。它創建的進程并不復制父進程的資源空間,而是共享,也就說實際上vfork實現的是一個接近線程的實體,只是以進程方式來管理它。并且,vfork()的子進程與父進程的運行時間是確定的:子進程“結束”后父進程才運行。請讀者注意“結束”二字。并非子進程完成退出之意,而是子進程返回時。一般采用vfork()的子進程,都會緊接著執行execv啟動一個全新的進程,該進程的進程空間與父進程完全獨立不相干,所以不需要復制父進程資源空間。此時,execv返回時父進程就認為子進程“結束”了,自己開始運行。實際上子進程繼續在一個完全獨立的空間運行著。舉個例子,比如在一個聊天程序中,彈出了一個視頻播放器。你說視頻播放器要繼承你的聊天程序的進程空間的資源干嘛?莫非視頻播放器想要窺探你的聊天隱私不成?懂了吧!

3).多任務程序設計模式的區別

由于進程間是獨立的,所以在設計多進程程序時,需要做到資源獨立管理時就有了天然優勢,而線程就顯得麻煩多了。比如多任務的TCP程序的服務端,父進程執行accept()一個客戶端連接請求之后會返回一個新建立的連接的描述符DES,此時如果fork()一個子進程,將DES帶入到子進程空間去處理該連接的請求,父進程繼續accept等待別的客戶端連接請求,這樣設計非常簡練,而且父進程可以用同一變量(val)保存accept()的返回值,因為子進程會復制val到自己空間,父進程再覆蓋此前的值不影響子進程工作。但是如果換成多線程,父線程就不能復用一個變量val多次執行accept()了。因為子線程沒有復制val的存儲空間,而是使用父線程的,如果子線程在讀取val時父線程接受了另一個客戶端請求覆蓋了該值,則子線程無法繼續處理上一次的連接任務了。改進的辦法是子線程立馬復制val的值在自己的棧區,但父線程必須保證子線程復制動作完成之后再執行新的accept()。但這執行起來并不簡單,因為子線程與父線程的調度是獨立的,父線程無法知道子線程何時復制完畢。這又得發生線程間通信,子線程復制完成后主動通知父線程。這樣一來父線程的處理動作必然不能連貫,比起多進程環境,父線程顯得效率有所下降。

PS:這里引述一個知名的面試問題:多進程的TCP服務端,能否互換fork()與accept()的位置?請讀者自行思考。

fork()作為一個用于子函數共享父進程的資源的函數,那么在用了fork()后子進程也是能直接用accept()函數接收到申請通信的請求,所以答案是可以。

關于資源不獨立,看似是個缺點,但在有的情況下就成了優點。多進程環境間完全獨立,要實現通信的話就得采用進程間的通信方式,它們通常都是耗時間的。而線程則不用任何手段數據就是共享的。當然多個子線程在同時執行寫入操作時需要實現互斥,否則數據就寫“臟”了。

4).實體間(進程間,線程間,進線程間)通信方式的不同

進程間的通信方式有這樣幾種

A.共享內存????B.消息隊列????C.信號量????D.有名管道????E.無名管道????F.信號

G.文件????????H.socket

線程間的通信方式上述進程間的方式都可沿用,且還有自己獨特的幾種:

A.互斥量??????B.自旋鎖??????C.條件變量??D.讀寫鎖??????E.線程信號

G.全局變量

值得注意的是,線程間通信用的信號不能采用進程間的信號,因為信號是基于進程為單位的,而線程是共屬于同一進程空間的。故而要采用線程信號。

綜上,進程間通信手段有8種。線程間通信手段有13種。

而且,進程間采用的通信方式要么需要切換內核上下文,要么要與外設訪問(有名管道,文件)。所以速度會比較慢。而線程采用自己特有的通信方式的話,基本都在自己的進程空間內完成,不存在切換,所以通信速度會較快。也就是說,進程間與線程間分別采用的通信方式,除了種類的區別外,還有速度上的區別。

另外,進程與線程之間穿插通信的方式,除信號以外其他進程間通信方式都可采用。
????線程有內核態線程與用戶級線程,相關知識請參看我的另一篇博文《Linux線程的實質》。

5).控制方式的異同

進程與線程的身份標示ID管理方式不一樣,進程的ID為pid_t類型,實際為一個int型的變量(也就是說是有限的):

/usr/include/unistd.h:260:typedef __pid_t???pid_t;

/usr/include/bits/types.h:126:# define __STD_TYPE????typedef

/usr/include/bits/types.h:100:#define???__S32_TYPE??????int

在全系統中,進程ID是唯一標識,對于進程的管理都是通過PID來實現的。每創建一個進程,內核去中就會創建一個結構體來存儲該進程的全部信息:

注:下述代碼來自?Linux內核3.18.1

struct task_struct {

????????volatile long state;????/* -1 unrunnable, 0 runnable, >0 stopped */

????????void *stack;...

????????pid_t pid;

????????pid_t tgid;...};

每一個存儲進程信息的節點也都保存著自己的PID。需要管理該進程時就通過這個ID來實現(比如發送信號)。當子進程結束要回收時(子進程調用exit()退出或代碼執行完),需要通過wait()系統調用來進行,未回收的消亡進程會成為僵尸進程,其進程實體已經不復存在,但會虛占PID資源,因此回收是有必要的。

線程的ID是一個long型變量:

typedef unsigned long int pthread_t;

它的范圍大得多,管理方式也不一樣。線程ID一般在本進程空間內作用就可以了,當然系統在管理線程時也需要記錄其信息。其方式是,在內核創建一個內核態線程與之對應,也就是說每一個用戶創建的線程都有一個內核態線程對應。但這種對應關系不是一對一,而是多對一的關系,也就是一個內核態線程可以對應著多個用戶級線程。還是請讀者參看《Linux線程的實質》普及相關概念。此處貼出blog地址:

Unix / Linux 線程的實質 - cnyinlinux主頁 - OSCHINA - 中文開源技術交流社區

對于線程而言,若要主動終止需要調用pthread_exit()?,主線程需要調用pthread_join()來回收(前提是該線程沒有被detached,相關概念請查閱線程的“分離屬性”)。像線發送線程信號也是通過線程ID實現的。

6).資源管理方式的異同

進程本身是資源分配的基本單位,因而它的資源都是獨立的,如果有多進程間的共享資源,就要用到進程間的通信方式了,比如共享內存。共享數據就放在共享內存去,大家都可以訪問,為保證數據寫入的安全,加上信號量一同使用。一般而言,共享內存都是和信號量一起使用。消息隊列則不同,由于消息的收發是原子操作,因而自動實現了互斥,單獨使用就是安全的。

線程間要使用共享資源不需要用共享內存,直接使用全局變量即可,或者malloc()動態申請內存。顯得方便直接。而且互斥使用的是同一進程空間內的互斥量,所以效率上也有優勢。

實際中,為了使程序內資源充分規整,也都采用共享內存來存儲核心數據。不管進程還是線程,都采用這種方式。原因之一就是,共享內存是脫離進程的資源,如果進程發生意外終止的話,共享內存可以獨立存在不會被回收(是否回收由用戶編程實現)。進程的空間在進程崩潰的那一刻也被系統回收了。雖然有coredump機制,但也只能是有限的彌補。共享內存在進程down之后還完整保存,這樣可以拿來分析程序的故障原因。同時,運行的寶貴數據沒有丟失,程序重啟之后還能繼續處理之前未完成的任務,這也是采用共享內存的又一大好處。

總結之,進程間的通信方式都是脫離于進程本身存在的,是全系統都可見的。這樣一來,進程的單點故障并不會損毀數據,當然這不一定全是優點。比如,進程崩潰前對信號量加鎖,崩潰后重啟,然后再次進入運行狀態,此時直接進行加鎖,可能造成死鎖,程序再也無法繼續運轉。再比如,共享內存是全系統可見的,如果你的進程資源被他人誤讀誤寫,后果肯定也是你不想要的。所以,各有利弊,關鍵在于程序設計時如何考量,技術上如何規避。這說起來又是編程技巧和經驗的事情了。

7).個體間輩分關系的迥異

進程的備份關系森嚴,在父進程沒有結束前,所有的子進程都尊從父子關系,也就是說A創建了B,則A與B是父子關系,B又創建了C,則B與C也是父子關系,A與C構成爺孫關系,也就是說C是A的孫子進程。在系統上使用pstree命令打印進程樹,可以清晰看到備份關系。

多線程間的關系沒有那么嚴格,不管是父線程還是子線程創建了新的線程,都是共享父線程的資源,所以,都可以說是父線程的子線程,也就是只存在一個父線程,其余線程都是父線程的子線程。

8).進程池與線程池的技術實現差別

我們都知道,進程和線程的創建時需要時間的,并且系統所能承受的進程和線程數也是有上限的,這樣一來,如果業務在運行中需要動態創建子進程或線程時,系統無法承受不能立即創建的話,必然影響業務。綜上,聰明的程序員發明了一種新方法——池。

在程序啟動時,就預先創建一些子進程或線程,這樣在需要用時直接使喚。這就是老人口中的“多生孩子多種樹”。程序才開始運行,沒有那么多的服務請求,必然大量的進程或線程空閑,這時候一般讓他們“冬眠”,這樣不耗資源,要不然一大堆孩子的口食也是個負擔啊。對于進程和線程而言,方式是不一樣的。另外,當你有了任務,要分配給那些孩子的時候,手段也不一樣。下面就分別來解說。

進程池

首先創建了一批進程,就得管理,也就是你得分開保存進程ID,可以用數組,也可用鏈表。建議用數組,這樣可以實現常數內找到某個線程,而且既然做了進程池,就預先估計好了生產多少進程合適,一般也不會再動態延展。就算要動態延展,也能預估范圍,提前做一個足夠大的數組。不為別的,就是為了快速響應。本來進程池的目的也是為了效率。

接下來就要讓閑置進程冬眠了,可以讓他們pause()掛起,也可用信號量掛起,還可以用IPC阻塞,方法很多,分析各自優缺點根據實際情況采用就是了。

然后是分配任務了,當你有任務的時候就要讓他干活了。喚醒了進程,讓它從哪兒開始干呢?肯定得用到進程間通信了,比如信號喚醒它,然后讓它在預先指定的地方去讀取任務,可以用函數指針來實現,要讓它干什么,就在約定的地方設置代碼段指針。這也只是告訴了它怎么干,還沒說干什么(數據條件),再通過共享內存把要處理的數據設置好,這也子進程就知道怎么做了。干完之后再來一次進程間通信然后自己繼續冬眠,父進程就知道孩子干完了,收割成果。

最后結束時回收子進程,向各進程發送信號喚醒,改變激活狀態讓其主動結束,然后逐個wait()就可以了。

線程池

線程池的思想與上述類似,只是它更為輕量級,所以調度起來不用等待額外的資源。

要讓線程阻塞,用條件變量就是了,需要干活的時候父線程改變條件,子線程就被激活。

線程間通信方式就不用贅述了,不用繁瑣的通信就能達成,比起進程間效率要高一些。

線程干完之后自己再改變條件,這樣父線程也就知道該收割成果了。

整個程序結束時,逐個改變條件并改變激活狀態讓子線程結束,最后逐個回收即可。

簡述小結:

根本區別:進程是操作系統資源分配的基本單位,而線程是任務調度和執行的基本單位

在開銷方面:每個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數據空間,每個線程都有自己獨立的運行棧和程序計數器(PC),線程之間切換的開銷小。

所處環境:在操作系統中能同時運行多個進程(程序);而在同一個進程(程序)中有多個線程同時執行(通過CPU調度,在每個時間片中只有一個線程執行)

內存分配方面:系統在運行的時候會為每個進程分配不同的內存空間;而對線程而言,除了CPU外,系統不會為線程分配內存(線程所使用的資源來自其所屬進程的資源),線程組之間只能共享資源。

包含關系:沒有線程的進程可以看做是單線程的,如果一個進程內有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,所以線程也被稱為輕權進程或者輕量級進程。

引用:

Linux進程與線程的區別 - cnyinlinux主頁 - OSCHINA - 中文開源技術交流社區

進程和線程的主要區別(總結)_kuangsonghan的博客-CSDN博客_進程和線程的主要區別

總結

以上是生活随笔為你收集整理的Linux进程与线程的区别 详细总结(面试经验总结)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。