進程和線程之間有什麼根本性的區別,我總感覺線程是進程的進化版?求解答

linux


Linus的這封郵件(Linux-Kernel Archive: Re: proc fs and shared pids)是我看到的對線程和進程的最好的解釋。

On Mon, 5 Aug 1996, Peter P. Eiserloh wrote:

&>

&> We need to keep a clear the concept of threads. Too many people

&> seem to confuse a thread with a process. The following discussion

&> does not reflect the current state of linux, but rather is an

&> attempt to stay at a high level discussion.

NO!

There is NO reason to think that "threads" and "processes" are separate

entities. That"s how it"s traditionally done, but I personally think it"s a

major mistake to think that way. The only reason to think that way is

historical baggage.

Both threads and processes are really just one thing: a "context of

execution". Trying to artificially distinguish different cases is just

self-limiting.

A "context of execution", hereby called COE, is just the conglomerate of

all the state of that COE. That state includes things like CPU state

(registers etc), MMU state (page mappings), permission state (uid, gid)

and various "communication states" (open files, signal handlers etc).

Traditionally, the difference between a "thread" and a "process" has been

mainly that a threads has CPU state (+ possibly some other minimal state),

while all the other context comes from the process. However, that"s just

_one_ way of dividing up the total state of the COE, and there is nothing

that says that it"s the right way to do it. Limiting yourself to that kind of

image is just plain stupid.

The way Linux thinks about this (and the way I want things to work) is that

there _is_ no such thing as a "process" or a "thread". There is only the

totality of the COE (called "task" by Linux). Different COE"s can share parts

of their context with each other, and one _subset_ of that sharing is the

traditional "thread"/"process" setup, but that should really be seen as ONLY

a subset (it"s an important subset, but that importance comes not from

design, but from standards: we obviusly want to run standards-conforming

threads programs on top of Linux too).

In short: do NOT design around the thread/process way of thinking. The

kernel should be designed around the COE way of thinking, and then the

pthreads _library_ can export the limited pthreads interface to users who

want to use that way of looking at COE"s.

Just as an example of what becomes possible when you think COE as opposed

to thread/process:

- You can do a external "cd" program, something that is traditionally

impossible in UNIX and/or process/thread (silly example, but the idea

is that you can have these kinds of "modules" that aren"t limited to

the traditional UNIX/threads setup). Do a:

clone(CLONE_VM|CLONE_FS);

child: execve("external-cd");

/* the "execve()" will disassociate the VM, so the only reason we

used CLONE_VM was to make the act of cloning faster */

- You can do "vfork()" naturally (it meeds minimal kernel support, but

that support fits the CUA way of thinking perfectly):

clone(CLONE_VM);

child: continue to run, eventually execve()

mother: wait for execve

- you can do external "IO deamons":

clone(CLONE_FILES);

child: open file descriptors etc

mother: use the fd"s the child opened and vv.

All of the above work because you aren"t tied to the thread/process way of

thinking. Think of a web server for example, where the CGI scripts are done

as "threads of execution". You can"t do that with traditional threads,

because traditional threads always have to share the whole address space, so

you"d have to link in everything you ever wanted to do in the web server

itself (a "thread" can"t run another executable).

Thinking of this as a "context of execution" problem instead, your tasks can

now chose to execute external programs (= separate the address space from the

parent) etc if they want to, or they can for example share everything with

the parent _except_ for the file descriptors (so that the sub-"threads" can

open lots of files without the parent needing to worry about them: they close

automatically when the sub-"thread" exits, and it doesn"t use up fd"s in the

parent).

Think of a threaded "inetd", for example. You want low overhead fork+exec, so

with the Linux way you can instead of using a "fork()" you write a

multi-threaded inetd where each thread is created with just CLONE_VM (share

address space, but don"t share file descriptors etc). Then the child can

execve if it was a external service (rlogind, for example), or maybe it was

one of the internal inetd services (echo, timeofday) in which case it just

does it"s thing and exits.

You can"t do that with "thread"/"process".

Linus


首先我假定你問的是操作系統級別的線程而不是用戶態的線程(就不扯python、go之類的了)

教科書式的回答:

操作系統中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調度的基本單位

這句話是什麼意思呢

1. 進程作為分配資源的基本單位

什麼是資源呢,就是內存,文件,socket等,比如你new了一塊內存,就是操作系統將一塊物理內存映射到你的進程地址空間上,這塊內存就是屬於你這個進程的,你進程內的所有線程都可以訪問這塊內存,其他進程就訪問不了。其他類型的資源也是同理。所以進程是分配資源的基本單位(而不是線程,因為同一個進程內的所有線程都可以訪問這些資源)

2. 線程作為獨立運行和獨立調度的基本單位

線程是怎麼運行和調度的呢。

先說運行,線程的運行狀態是通過CPU寄存器表示的(不太嚴謹的說),幾個比較重要的是:ip,表示下一條要執行的指令相對於當前段的偏移量,cs,表示當前代碼段的信息,那麼cs:ip就表示下一條指令的絕對地址,ss,表示堆棧的基地址,通過這個可以知道當前的調用棧是什麼情況,sp、bp,表示當前棧幀的棧頂和棧基地址,所有臨時變數都分配在棧上也就是這裡,還有一大堆通用寄存器就不詳細說了。通過這些寄存器我們可以知道1、當前運行到什麼地方了,下一步要做什麼;2、函數的調用棧,現在運行在哪個函數裡面,這個函數又是在哪個函數裡面,函數調用完成後應該返回到哪裡等。線程的運行就是cpu一條一條的執行指令(ip一條一條的變化),隨著函數的調用和返回,堆棧在增長和減少(sp、bp在變化)

再說調度,如果前面說的明白了,那麼調度就是把某個線程對應的這套寄存器換出去,換另一套進來,這就是暫停一個線程,繼續另一個線程的過程。

每一個線程對應著一套這樣的寄存器(書上應該是叫xx控制塊之類的),哪個線程的寄存器在cpu上,哪個線程就是在幹活(運行),其他的就在候著,直到被換進來(調度),因此我們說運行和調度都是指線程,進程只是給線程提供了場地和資源


首先,進程、線程的概念都是不斷發展的,因此不同資料中的定義,常常會出現混亂。

首先在傳統進程模型中,進程的內涵可分為下面兩個方面:

  • 調度、執行的基本單位:每個進程都有自己的運行狀態(state)、優先順序、寄存器等,是OS調度的基本單位。

  • 資源所有權:包括程序(program text)、數據(data )、文件(open file)等資源。一個進程擁有對這些資源的所有權,OS則提供保護功能,避免不同進程之間的資源衝突。

既然是兩個獨立的功能,可不可以把它們分離呢?這就出現了線程(thread)的概念:

  • 執行與調度的基本單位:thread

  • 資源所有權:process

在這種模型下,內核調度的基本單位是線程而非進程,進程只需要負責管理資源,這些資源則由同一進程下的線程共享。

線程的出現帶來了以下便利:

  • 創建、終止、切換thread的開銷要比process小的多
  • 由於共享地址空間,線程通信比進程通信高效得多

為什麼說創建thread的開銷小呢?因為同一進程下新建thread,只需要設置PC、(通用)寄存器、棧(注意也是per thread的!)以及線程狀態(堵塞、等待…)即可,相較新建進程:

—— Modern Operating Systems

顯然,進程相關的開銷要大得多。

由於那時,操作系統內核沒有提供對線程的支持,線程以用戶級線程(user-level threads)的形式存在。在這種模型下,操作系統渾然不知線程的存在,仍然以進程為單位進行調度:

而隨著現代Unix like、windows NT等系統內核對線程的支持,內核級線程(kernel-level thread)模型出現:

此外還有混合型實現,這裡就不展開了。用戶級線程和內核級線程各有千秋,很難用幾句話概括,簡單來說:

  1. 用戶級線程相關開銷小得多(不需要user mode到kernel mode的轉換)

  2. 用戶級線程堵塞會使同進程所有線程堵塞(一堵堵一堆),因為操作系統會把整個進程都堵塞掉。
  3. 同一進程下的各個用戶級線程無法同時運行在多個cpu上(因為內核根本不知道有線程這東西)
  4. 內核級線程開銷大(切換時需要user mode 到kernel mode,再回到user mode),但克服了上述用戶級線程的2、3兩個主要缺點。

總結一下,在經典的進程、線程模型中(支持kernel-level threads的現代 Unix):

  • 進程是資源的容器,包含(一個或)多個線程。
  • 內核調度的基本單位是線程、而非進程。

  • 同一進程下的各個線程共享資源(address space、open files、signal handlers,etc),但寄存器、棧、PC等不共享

基本上,教材上見到的都是上面這種模型。

-------------------------下面說一下Linux中令人困惑的的進程、線程----------------------------

warning:下面的內容不小心寫多了,如果不是對linux特別感興趣,可以點個贊離開了:)

1.在Linux中,內核用 task_struct 這一種數據結構代表傳統意義上的進程、線程(everything is simply a runnable task),而不是為二者分別定義數據結構(如大多數操作系統)。single-threaded 進程使用一個task_struct表示,multi-threaded 進程就使用多個task_struct表示。

2.2000年,Linux引入了一個新的系統調用(system call)——clone,使進程與線程的邊界變得模糊。簡單的說,這個函數相比傳統的fork,能夠通過參數,使原本per process的資源變成per thread:

重新放一下上面的圖幫助理解:

在Linux中,無論是進程還是線程,根本上都是通過clone一個已存在task(進程|線程)實現的,二者的差別就在於clone時Flags的傳遞,當CLONE_VM 位設置時(共享地址空間),創建線程;當該位未設置時,創建進程。

fork——進程創建,通過調用 0共享的clone 實現的

pthread_create——線程創建,則是通過調用 最多共享的clone實現的。

3.在Linux中,共享內存的task可以被看做是同一進程下的不同線程。從內核的角度來講,每一個task都具有一個PID(Process IDentifier),注意這裡,同一進程下的線程擁有不同的PID!。此外,每一個task還具有TGID(Task Group ID),同一進程下的線程TGID相同,這個TGID即是我們通常意義下的PID。

(其實在計算機眼裡只是個名字罷了,這個task的credentials更合適的叫法當然是TID,不過,PID這個概念早就深入人心了,so...)

小實驗,使用

$ htop

命令查看進程,結果如下:

重點是PID、TGID兩列,再注意內存的使用量,發現了沒?各項內存使用數據都相同,說明是同一進程下的線程,而他們的PID不同,TGID相同。

而POSIX標準規定,同一進程下的所有線程PID應相同,這也和我們通常理解的PID概念相符(畢竟是process ID!)。事實上,另外一個Linux常用的命令ps下,同一進程下的線程PID相同:

$ ps

(LWP stands for light weight process,輕量級進程,就是線程了)

沒錯!top、ps這兩個Linux常用的命令,對PID這一在Linux下令人困惑的概念竟然不是不一致的:

查看二者的文檔,果然如此:

ps 文檔:

pid PID a number representing the process ID (alias tgid).

top文檔:

PID The task"s unique process ID, which periodically wraps, though never restarting at zero. In kernel terms, it is a dispatchable entity defined by a task_struct.

矛盾嗎?不矛盾。其實不過是因為二者一個用的是用戶視角(user view),一個用的是內核視角(kernel view)罷了。

(from stackoverflow)

4.Linux線程的實現

以內核2.6為界,Linux前後採用兩種線程實現方式,由GNU C Library(glibc)提供:

  • LinuxThreads(glibc 2.4後不再支持)

  • NPTL (Native POSIX Threads Library)

在第一種實現方式下,存在許多與POSIX不兼容的特性,比如上面提到的同進程中的線程不共享PID的問題,而在NPTL中,這一問題得到了解決,當然也存在少量不兼容。

一些參考資料:

1. MODERN OPERATING SYSTEM,Andrew S.Tanenbaum

2.《操作系統—精髓與設計原理》,William Stallings,陳向群譯

3. Linux man pages

4. http://stackoverflow.com/questions/807506/threads-vs-processes-in-linux

5. Linux - Threads and Process


計算機操作系統裡面有兩個重要概念:並發和隔離。想想操作系統從最初很簡單的功能發展到現在這麼強大就容易理解這兩個概念了。

並發是為了盡量讓硬體利用率高,不然計算機不可能按照摩爾定律不停發展,個人也不可能用上這麼便宜的電腦。代碼要並發執行不一定要有線程,線程只是一個操作系統上的概念,是為了讓代碼好寫和好理解而已。很多單片機的操作系統其實沒用線程的概念的,彙編語言也沒線程的概念。就和程序設計裡面引入設計模式差不多。線程相當於一個執行指針加了一些寄存器狀態的結構體而已,有了線程概念,代碼本身就只用考慮在哪個線程執行就可以了,而不用像沒線程的系統裡面要自己用彙編去保持寄存器狀態,然後分配時間片等一堆事情。操作系統的一個作用就是簡化上層應用的開發,線程這個概念的引入就做到了這一點。沒有線程的世界,你可以想想要在8086那種cpu用彙編寫個同時做多件事情的程序就知道了。最近還流行一個概念叫纖程,這個和線程很相似,只是纖程是為了在應用層面做到高並發,而線程是為了在系統層面做到並發。

隔離也是並發之後要解決的重要問題,並發執行就表示有多段代碼在跑,不能一段代碼出錯了整個系統就崩了,所以必須隔離。而隔離就涉及在多大範圍內隔離了,因為代碼是無狀態的,有狀態的是資源,這個隔離的範圍一般和資源有關係。計算機的資源一般是共享的,隔離要能保障崩潰了這些資源能夠被回收,不影響其他代碼的使用,否則還不如藍屏重啟。所以說一個操作系統只有線程沒有進程也是可以的,只是這樣的系統會經常崩潰而已,操作系統剛開始發展的時候和這種情形很像。

那總結一下:線程和並發有關係,進程和隔離有關係。線程基本是為了代碼並發執行引入的概念,因為要分配cpu時間片,暫停後再恢復要能夠繼續和沒暫停一樣繼續執行;進程相當於一堆線程加上線程執行過程中申請的資源,一旦掛了,這些資源都要能回收,不影響其他程序。

人類社會也和這個有點像,例如將地球資源和生產資料想像成硬體,人想像成線程,為了提高效率,最好的方式是流水線,分時佔用資源,這樣就是有人白天上班有人晚上上班,整個社會一直都在運動。但由於每個人都不一樣,資源在全球也不均,這相當於每個程序不一樣,有的程序耗cpu,有的程序io密集,而且程序一般是為了解決一個問題,這個相當於一群人有個共同的願景,將一堆人和一堆資源組合在一起,相當於一個國家了,隔離的作用就是要保證一個國家崩潰不至於地球沒了。進程通訊啥的就相當於跨國交易了。

計算機的很多思想其實也是從現實中來的,聯想一下當時的環境,就容易為什麼出這個概念以及這個概念是為了解決什麼。其實你在當時的環境下也會產生這兩種概念的想法,只是牛逼的人能夠抽象化了而已。這也是為什麼將很多文章的引言當做歷史書看,然後將你放在那個階段去思考就容易理解了。


謝謝 @samar邀請。

一直以來,個人覺得最容易理解多線程的方式就是:同一個程序裡面的多個函數同時/並行運行。這樣,就很容易明白多線程的特點,以及跟多進程的區別。

題主覺得多線程是多進程的進化版,雖然不知道題主是從哪些方面來看的,但是我覺得可以這樣理解。事實上,linux最開始只有多進程沒有多線程,後來在多進程基礎上,打破進程間的空間隔離,「進化」出了多線程。


一般來說線程是調度單位,進程是線程集。按照概念來說線程,進程區別很明顯,但在實際的系統中,往往有很多交集。如調度來說,有的系統線程最為基本調度單位,進程不參與調度。有的系統進程也進行調度,先調度到大的進程,再調度進程裡面的線程,這要看不同的系統實現。

另外進程可以只有一個線程,這時候交集就更多了。另外,從歷史上來說,進程調度發展更早,線程是後入的,尤其是unix系統,進程是作為普遍的一個內核資源,而線程不一定存在於內核,用戶級別的線程有的甚至是靠進程實現的,有的是靠用戶態代碼實現的。所以很多時候線程,進程並不能區分的非常明顯。

拋開歷史因素,按照目前主流系統,進程是區別用戶空間的,不同進程一般用戶空間不同,當然也有例外,如有的系統會有系統進程,這些進程用戶空間不確定,服務於普通進程,服務那個,就用哪個的普通進程的用戶空間。而同一個進程的線程,用戶空間是相同的。當然也有系統級線程,這些線程用戶空間不定。

以前線程往往作為最基本的一個調度單位,現在內核除了線程外還出現了其他調度單位,如軟中斷,tasklet等。這些調度單位往往出於實時性考慮,尤其是互聯網的大力發展,網卡性能越來越高,單純依靠線程跟中斷,性能會非常低劣。以前當前執行的代碼必屬於一個進程(擁有一個用戶空間),必屬於一個線程,現在情況也是如此,但是會有許多軟中斷,tasklet附屬於線程運行著,而這些代碼可能跟當前線程一點關係也沒有。比如用戶的一個ui進程正在畫圖時候,可能被打斷用來處理網路。這個打斷跟中斷差不多,不同的是中斷處理時間一般很短,而軟中斷,tasklet處理時間就比較長,而且可以嵌套。表現來說雖然當前線程是ui線程,就是停在那裡不畫圖了。後面幹了一堆比畫圖更重要的事情。

進程也好,線程也好,沒有哪個更先進,都是用來解決問題的,軟中斷,tasklet,系統線程也如此。


前面已經有從具體的概念來介紹線程與進程區別了,這些就省略了。

從三個問題開始:

1.有沒有一種進程,其執行沒有線程?

2.有沒有一種線程,其執行不需要進程環境?

3.有沒有一段運行代碼,其執行不在線程環境內?

這三個問題都是針對樓主「進化」而提出的:

1.有!我猜在早期的操作系統實現,還沒有發展出多線程的時候,可能一個進程就只有一個「線程」執行;

2.有!內核線程的執行理論上可以不需要進程環境;

3.有!中斷的常式代碼執行就不屬於任何進程或線程,但其可能在任何進程和線程context下執行。

從這三個答案,可以大概看出來,進程與線程雖然有非常強的聯繫,但不能把線程看成進程的「進化」,因為在實際的操作系統實現中,進程和線程是可以分離的。

再從操作系統的代碼角度來談上面的觀點:

1、進程和線程在操作系統內核里都有相應的數據結構表示,不論是windows還是linux,線程的數據結構與線程的數據結構都不是繼承關係(c語言可以模擬c++的繼承),而是進程數據結構中有一個指針,指向一個線程列表;

2、現在所有的進程執行,其實都是線程執行。實際的實現,大概是操作系統只對線程進行調度,當調試的線程不屬於當前進程環境的話,那麼就切換進程空間就好了;也就是說,操作系統調度線程的時候,並不會參考進程的「意見」。

另外 @valyria我並不是特別贊同linus的COE觀點,當線程成為CPU執行的單元之後,「進程執行」就成了一個不存在的概念了,所有的「進程執行」都要依賴線程完成,這在概念上既是清楚的,在代碼實現上也是符合工程思想的。

當然也可能我無法站到linus的高度來看待進程與線程的概念,也就無法想像一個帶著頁表的線程或者一個帶著很多寄存器上下文的進程。


謝邀。

進程有自己的地址空間,兩個進程是內存隔離的。eg:a進程0x40000處的內存值跟b進程0x40000處的內存值可以不同。

線程是實際執行的基本單位,用於分配cpu執行時間,每個線程有自己的寄存器狀態,拓展後可能還有一些tls,用於保存線程相關的變數。


為了把這關係說的好理解,我這先舉個農夫種地的例子:

我黨建國後,廣大農民朋友在黨光榮偉大地領導下,紛紛翻身做了主人,千島湖山泉地兒里的農夫也不例外。這不,為了響應黨和國家的號召,當地政府就為他配置了5塊農田,農夫心裡感慨呀:親們,我胡漢三的好日子就快來了!可不曾想這接下來的幾年,社會不是大生產就是自然災害的,農夫的日子過得依舊是那個清苦的緊嘞。

就這樣到了1978年,農夫偶然間聽到一個喜訊,說我黨的經濟方針要變了,要搞改革開放,要走市場經濟,這兩眸子里頓時就淚眼汪汪了,趕緊屁顛屁顛的去市場做調研,回來就得出個結果:蘿蔔、土豆、辣椒、茄子、西紅柿好賣,可以種些出來去換點閑錢給娃兒他娘添身衣裳,娃兒他娘這些年跟著不容易啊。不過農夫心裡又一想,這玩意兒不能多種,多種有風險,一家人可都要吃飯呢,5塊田裡拿出一塊就好,其他的該種啥還種啥!於是第二年春天,農田3號就被農夫光榮選中,劃分成5片地兒,分別種上了這五貨。過了個把月,農夫又進了城打探了下行情,這一打探可不得了:西紅柿要跌!農夫心裡苦啊,想著種西紅柿的那塊地兒要虧了,眼下只有兩條路可以走了:要麼全給它鏟掉,趕緊種上其他貨;要麼就再等兩個月候著番茄紅了、摘了,再種其他貨,興許會有轉機,不過下一季這塊地兒搞死搞殘也不得種西紅柿了。在農夫天天巴巴候著的眼神下,地里東西終於熟了,農夫好歹是收回了老本,娃兒他娘的衣服也給買了身,只是農夫低頭看了眼自己身上:呵,皇帝的新裝,湊合著吧!好壞這塊地這會兒是清乾淨了,可以種其他東西了。

下面用進程、線程的思想解釋下:

例中農夫的5塊農田就相當於計算機為操作系統提供的資源(如內存);農夫的角色就相當於一個處理機(CPU);製造閑錢的過程就相當於一個進程,這個進程的起點是需要先拿一塊農田作為資源,也就是說農夫先是為製造閑錢這項進程去申請5塊自有資源中的農田3號作為進程的大本營;而農田3中每一個區的作物種植就相當於線程,這個線程所使用的資源其實不是它的,是屬於農田3號這個進程的,因為這個區域塊可以在不向操作系統額外申請資源的情況下,用來種植其他作物,因此線程只有進程資源的使用權,而不是擁有權。那麼究竟這塊區域在什麼時候可以用來種植其他作物呢?

① 這塊地兒(不是田)原本就是空的;

② 這塊地兒原本種著的作物(線程)被農夫(CPU)強行剷除了;

③ 這塊地兒原本種著的作物(線程)成熟被摘除清理(運行完畢)後又空出來了。

--------------------------------------

啰嗦了這麼多,不知道算不算是解釋清楚了,如果尚有疑問,歡迎交流,我也是最近在跟《操作系統》這本書較勁兒!


進程是車間,線程就是車間里流水線上的工人。可以共享機器


看操作系統,也看編程語言,有些是等同的概念,有些是子集的概念,windows和Linux的不一樣,C和Python的也不一樣。


首先,進程是資源分配的最小單位,線程是CPU調度的最小單位;二者都有task_struct(支持調度),線程可以說是微型進程,線程在創建時共享主線程的內存,文件(clone函數)等等;主線程和子線程的代碼段是放在一起的,因為線程主要就是執行一個線程函數,所以反映在內存中就是線程棧,主線程棧還是在原來的地方,而子線程棧在堆上面的共享存儲區;還有一些調度上的區別~推薦看深入理解linux內核,還有輸出一個多線程的內存布局直觀看下(在/proc/進程號/下有個文件)~


進程與線程的一個簡單解釋

以工廠為例說明,生動形象,一個工廠裡面有很多車間,一個車間裡面有很多工人。

那將計算機比作工廠,進程比作車間,線程比作工人。

操作系統的設計,因此可以歸結為三點:

(1)以多進程形式,允許多個任務同時運行;

(2)以多線程形式,允許單個任務分成不同的部分運行;

(3)提供協調機制,一方面防止進程之間和線程之間產生衝突,另一方面允許進程之間和線程之間共享資源。


進程與線程之間的區別及聯繫

一、定義:
1、進程:進程是一個具有獨立功能的程序關於某個數據集合的以此運行活動。是系統進行資源分配和調度的獨立單位,也是基本的執行單元。是一個動態的概念,是一個活動的實體。它不只是程序的代碼,還包括當前的活動。
進程結構特徵:由程序、數據和進程式控制制塊三部分組成。具有獨立性、並發性、非同步性和動態性的特點。
(1)、進程的概念主要有兩點:
第一,進程是一個實體。每一個進程都有它自己的地址空間,一般情況下,包括文本區域(text region)--存儲處理器執行的代碼,數據區域(data region)--存儲程序執行期間的一些數據變數,堆棧(stack region)--存儲動態分配的內存和本地變數及指令。
第二:進程是一個「執行中的程序」。程序是一個沒有生命的實體,只有在運行時處理器才會賦予它生命,才能成為一個活動的實體,我們稱其為「進程」。
每一個進程都會有一個獨一無二的編號,被稱為進程標識碼,簡稱PID(Process,identifier),它是一個取值為1-32768.但是init是一個特殊的進程。所謂的init進程,是一個內核啟動的用戶級進程,也是系統上運行的所有其他進程的父進程,他會觀察子進程,並在需要的時候啟動,停止,重新啟動它們,init進程主要完成系統各項的配置。linux系統中,init從根文件夾系統目錄里的/etc/inittab文件里獲取信息。是所有進程的發起者和控制者,內核啟動後,便由init進程來進行各項配置。
(2)、進程的三種狀態:
就緒(Ready)狀態:當進程分配到除CPU以外的必要資源後,只要再獲得CPU,便可以立即執行,進程這時的狀態為就緒狀態。在一個系統中處於就緒狀態的進程可能有多個,通常將它們排成一個隊列,稱為就緒隊列。
阻塞(Blocked)狀態:正在執行的進程由於發生某事件或接受某消息無法繼續執行時,便放棄處理機而處於暫停狀態,也即進程的執行收到阻塞,把這種暫停狀態稱為阻塞狀態,有時也稱為等待狀態和封鎖狀態。通常使進程處於阻塞的原因有:請求I/O,申請緩衝空間。也會產生一個相應的阻塞隊列。
運行(Running)狀態:進程已獲得CPU,其程序正在執行。在單處理機系統中,只有一個進程處於執行狀態,在多處理機系統中,則有多個進程處於執行狀態;

其關係如下圖所示:

2、線程:線程是進程中執行運算的最小單位,是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源(程序計數器,一組寄存器和棧),但它可與同屬一個進程的其他線程共享進程所擁有的全部資源。
線程優點:
(1)易於調度。
(2)提高並發性。通過線程可方便有效地實現並發性。進程可創建多個線程來執行同一個程序的不同部分。
(3)開銷少。創建線程比創建進程要快,所需開銷少,佔用的資源也少;
(4)充分發揮多處理器的功能。通過創建多線程進程,每個線程在一個處理器上運行,從而實現應用程序的並發性,是每個處理器都得到充分的運行。
二、進程與線程的區別:
(1)調度:線程作為處理器調度和分配的基本單位,而進程是作為擁有資源的基本單位
(2)並發性:不僅進程之間可以並發執行,同一個進程的多個線程之間也可並發執行
(3)擁有資源:進程是擁有資源的一個獨立單位,有自己獨立的地址空間;線程不擁有系統資源,但可以訪問隸屬於進程的資源,共享進程的地址空間.
(4)系統開銷:在創建或撤消進程時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於創建或撤消線程時的開銷。
三、進程和線程的關係:
(1)二者均可並發執行.
(2)線程是指進程內的一個執行單元,也是進程內的可調度實體。一個程序至少有一個進程,一個進程至少有一個線程,一個線程只屬於一個進程.
(3)資源分配給進程,同一一進程的所有線程共享該進程的所有資源。
(4)處理機分給線程,即真正在處理機上運行的是線程。
(5)線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。線程是指進程內的一個執行單元,也是進程內的可調度實體.進程和線程的主要差別在於它們是不同的操作系統資源管理方式。進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響。而線程只是一個進程中的不同執行路徑,線程有自己的堆棧和局部變數,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變數的並發操作,只能用線程,不能用進程。
四、、進程間通信的方式?
(1)管道(pipe)及有名管道(named pipe):管道可用於具有親緣關係的父子進程間的通信,有名管道除了具有管道所具有的功能外,它還允許無親緣關係進程間的通信。
(2)信號(signal):信號是在軟體層次上對中斷機制的一種模擬,它是比較複雜的通信方式,用於通知進程有某事件發生,一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一致的。
(3)消息隊列(message queue):消息隊列是消息的鏈接表,它克服了上兩種通信方式中信號量有限的缺點,具有寫許可權得進程可以按照一定得規則向消息隊列中添加新信息;對消息隊列有讀許可權得進程則可以從消息隊列中讀取信息。
(4)共享內存(shared memory):可以說這是最有用的進程間通信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據得更新。這種方式需要依靠某種同步操作,如互斥鎖和信號量等。
(5)信號量(semaphore):主要作為進程之間及同一種進程的不同線程之間得同步和互斥手段。

(6)套接字(socket):這是一種更為一般得進程間通信機制,它可用於網路中不同機器之間的進程間通信,應用非常廣泛。

參考資料:進程與線程之間的區別及聯繫


剛剛上完操作系統這門課,趁熱回答下。

樓主其實只要掌握這三張圖,就可以說對processs和thread理解的很到位了,足夠面試了。

圖1:進程是大框,裡面塞著好幾個線程。一個進程用ProcessControlBlock上的一個entry記錄其基本信息(pid,state,priority等),進程會被操作系統分配一個內存邏輯地址空間,即其擁有一段內存空間供使用。

線程是進程內負責執行一項任務的單元,這個單元用ThreadControlBlock上的一個entry記錄其基本信息(tid,state,priority,counter,register info等),這個單元有著自己的stack來用於任務執行。

因為線程都在進程的大框里,因此共享了邏輯地址空間中存下的數據,代碼,文件引用等。(java虛擬機中,所有線程共享的heap屬於裝數據,method area屬於裝代碼和靜態變數)

圖2: 進程狀態

進程有這些狀態。線程也一樣。重要的是明白blocked:等待事件/結果

suspend:被調到disk上的虛擬內存里歇著了

圖3:進程與線程狀態的聯繫和區別

進程和線程的狀態是相互聯繫,而又不同的。

--------手機打的....有贊的評論的話繼續更新:)


其實最根本的區別是系統開銷和穩定性的區別


看了各位大神的解釋,我突然有了一個奇妙的想法(大神繞道):

進程與線程之間的區別可以作如下比喻:

在一塊很大的草地(CPU)上,有很多個佔地面積不同的農場(進程),每個農場里有不同種的動物(線程),動物使用了本農場的草、水。場地等資源。也就是說,CPU分配資源的單位是進程,而線程才是進行資源調度的基本單位。

希望可以幫到初學的人.


早期的概念,進程包含了一個獨立的虛擬地址空間,進程和進程之間只能靠IPC通信。一個進程可以包含若干個線程,這些線程共享同一個虛擬地址空間、文件描述符等等資源,線程之間靠全局變數即可相互通信(安全性另說)。

最近幾年的硬體和操作系統設計,在實現層面模糊了兩者的區別,統一調度執行了。


那個有個好書可以推薦下, csapp 雖然, 這個裡面有講


我記得有這麼個說法,進程是程序運行的最小單位,線程是計算資源分配的最小單位


推薦閱讀:

Linux中進程具有父子層次結構,Windows中沒有進程層次,這兩種設計各有什麼優劣?
NT 之後操作系統內核是否就毫無長進了?
Linux 內核為什麼沒有第三方的版本或分支?
不懂彙編可以學 Linux 內核嗎?

TAG:操作系統 | 操作系統內核 |