Linux下標準IO、文件IO,進程、線程、進線程間通信

本人整理不易,如有差錯請評論指出,及時論證修改,不傳播錯誤知識點。

1、標準IO與文件IO

對於操作系統而言,I/O操作可以分爲兩類
 一類是帶緩存的IO,又稱爲標準IO(C標準庫中提供了標準IO庫,即stdio),它實現了跨平臺的用戶緩存解決方案。
另一類是Unix/Linux下的文件IO,又稱直接IO,即文件訪問機制不經過操作系統內核的緩存,數據直接在磁盤和應用程序地址空間進行傳輸。
 相對而言,直接IO效率更高
https://blog.csdn.net/yang2011079080010/article/details/53759759

標準IO:
https://blog.csdn.net/weixin_45756796/article/details/106711406 <自己的博客>

思考:緩存,在什麼位置?具體多大?

文件IO:
Linux中的文件主要分爲六大類: “ls -l" 進行查看
普通文件      -
目錄文件      d
管道文件      p
設備文件      c
套接字文件	 s
符號鏈接文件  l

文件描述符
一個 Linux 進程可以打開成百上千個文件,爲了表示和區分已經打開的文件,
Linux 會給每個文件分配一個編號(一個 ID),這個編號就是一個整數,被稱爲文件描述符

1.就是一個整形
2.每個進程默認打開 0、1、2 三個文件描述符, 新的文件描述符都是從 3 開始分配
3.一個文件描述符被回收後可以再次被分配 (文件描述符並不是遞增的)
4.每個進程單獨維護了一個文件描述符的集合

文件IO與標準IO:
文件IO又叫低級磁盤IO,遵行POSIX相關標準(POSIX表示可移植操作系統接口規範),任何兼容POSIX標準的系統都支持文件IO;
標準IO又叫高級磁盤IO,遵行ANSI C標準,只要開發環境中有標準C庫,標準IO即可使用;

注意:linux使用的是glibc,它是標準IO的超集,既包含了ANSI C中定義的函數,也包含了POSIX中定義的函數,因此標準IO和文件IO都可使用

文件IO中用文件描述符表示一個打開的文件,可以訪問不同的文件,如:普通文件、設備文件、管道文件等等
標準IO中用FILE(流)表示一個打開的文件,通常只用來訪問普通文件。

凡是通過直接IO(文件IO)方式進行數據傳輸,數據均直接在用戶地址空間的緩衝區和磁盤之間進行傳輸,完全不需要真緩存的支持

思考:用戶地址空間的緩衝區是什麼?
和磁盤間怎樣進行數據的傳輸?
真緩存是什麼?

文件訪問方式:
1、標準訪問文件的方式:   
  寫->用戶空間地址的應用緩存->內核地址空間頁緩存->物理磁盤—>內核地址空間頁緩存->用戶空間地址的應用緩存->讀

2、直接IO方式
  寫->用戶空間地址的應用緩存->物理磁盤—>用戶空間地址的應用緩存->讀
  
3、同步訪問文件的方式————<流程與標準訪問時一樣的>
  數據的讀取和寫入都是同步操作的,他與標準訪問文件的方式不同的是,只有當數據被成功寫到磁盤時才返回給應用程序成功的標誌。

4、異步訪問文件的方式————<流程與標準訪問時一樣的>
  當訪問數據的線程發出請求之後,線程會接着去處理其他事情,而不是阻塞等待,當請求的數據返回後繼續處理下面的操作。
  這種訪問文件的方式可以明顯的提高應用程序的效率,但是不會改變訪問文件的效率。

5、內存映射
  指操作系統將內存中的某一塊區域與磁盤中的文件關聯起來,當要求訪問內存中的一段數據時,轉換爲訪問文件的某一段數據。
  這種方式的目的同樣是減少數據從內核空間緩存到用戶空間緩存的數據複製操作,因爲這兩個空間的數據是共享的。


	https://blog.csdn.net/en_joker/article/details/98596310 <訪問方式>
	https://blog.csdn.net/mg0832058/article/details/5890688 <內存映射>

2、linux下快速搜索文件

1. find <指定目錄> <指定條件> <指定動作>————最強大,但慢,根據文件的屬性進行查找,如文件名,文件大小,所有者,所屬組,是否爲空,訪問時間,修改時間等
2. locate + 文件名————搜索的是數據庫,比find快
3. whereis + 文件名(可執行的二進制文件)
4. which + 命令(查看命令在系統中的位置)
5. type + 命令(查看命令是否是系統自帶的命令)
6. grep + r + 字符串 + 大致路徑(根據字符串查詢)
7. 寫函數 lseek();

3、 文件、目錄的權限問題

chmod 777 具體文件名————————修改具體文件的權限
chmod 777 *.c——————————————修改所有的.c文件權限
權限具體:  -rw-rw-r--   *** —— 111 ( 4+2+1=7 )
			最前面那個 - 代表的是類型(普通文件)
			中間那三個 rw- 代表的是所有者(user) 
			然後那三個 rw- 代表的是組羣  (group)
			最後那三個 r-- 代表的是其他人(other)

4、文件鎖

文件鎖主要是用於多個程序共同操作一個文件的情況,鎖分爲:1.建議性鎖  2.強制性鎖
1. 建議性鎖,要求每個相關程序在訪問文件時候都先檢查是否有鎖存在,並尊重已有鎖,不建議,因爲無法保證都檢查
2. 強制性鎖,由內核執行,當一個文件被上鎖,進行寫入操作的時候,內核將阻止其他任何程序對該文件進行讀寫操作。

對文件上鎖的函數有lockf()和fctnl();
lockf()是建議性鎖,而fctnl()包含建議性鎖、也保護強制性鎖,還包含記錄鎖。
記錄鎖又分讀取鎖和寫入鎖,讀取鎖又稱共享鎖,允許同時多個程序對文件的同一位置建立讀取鎖;
						  寫入鎖又叫排斥鎖,在任何時刻只能允許一個程序對文件的某一部分建立寫入鎖(互斥鎖)

5、進程與線程

1.程序:程序是靜態的,是保存在磁盤上的指令的有序集合,沒有任何執行的概念  ——————(就是一堆代碼)

2.任務:一個任務是一個程序的運行,一個任務包含一個或多個完成獨立功能的子任務,這個子任務就是進程或線程。 ——(啓動360殺毒軟件)

3.進程:進程是指一個具有獨立功能的程序在某個數據集合上的一次動態執行過程,是操作系統及進行資源分配和調度的基本單位。
		注意:一次任務的運行可以激活多個進程。進程是一個程序的執行的過程,是動態的,包括程序的創建、調度、消亡

4.進程具有併發性、動態性、交互性、獨立性等主要特性
	1.併發性:指的是系統中多個進程可以同時併發執行,相互之間不受干擾。——馬路上兩個汽車各跑各的,互不影響
	2.動態性:指的是進程都有完整的生命週期,而且在進程的生命週期內,進程的狀態時不斷變化的,進程具有動態的地址空間
	3.交互性:指的是進程在執行過程中可能會與其他進程發生直接或間接地通信,如:進程同步、進程互斥
	4.獨立性:指的是進程是一個相對萬層的資源分配和調度的基本單位,各個進程的地址空間是相互獨立的
			  只有採用某些特定的通信機制才能實現進程間的通信
			  
5.從系統來看,進程是程序執行時相關資源的總稱

6.內核將所有的進程都放在雙向循環鏈表中(進程鏈表),鏈表的每一項都是task_struct,稱爲進程控制塊的機構,該結構包含一個進程相關的所有信息
	
	task_struct結構圖中最重要的兩個域:state()——進程狀態, pid()——進程標識符
	1.state————進程狀態:
		1. 運行狀態:進程當前正在運行,或正在運行隊列中等待調度
	
		2. 可中斷的阻塞狀態: 進程處於阻塞(睡眠)狀態,正在等待被某些事件的發生或能夠佔用某些資源。(其實就是卡住了)
		在這種狀態下,進程可以被信號中斷,接收到信號或者被顯示地喚醒呼叫之後,進程將變爲運行狀態。
		
		3. 不可中斷的阻塞狀態:	類似可中斷的阻塞,只是不再處理信號,把信號傳遞到該狀態下的進程並不能改變它的狀態。
		只有在它所等待的事件發生,進程才能顯示的被喚醒呼叫喚醒。
	
		4.暫停狀態: 進程的執行被暫停,當進程收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信號就會被暫停。
	
		5.僵死狀態: 子進程運行結束,父進程未退出,並且沒有使用wait函數族等系統調用來回收子進程的退出狀態。
				 殭屍進程與孤兒進程:殭屍進程由父進程回收資源,孤兒進程由init進程回收資源。
				 
		6.消亡狀態: 這是進程的最終狀態,父進程調用wait函數族回收後,子進程徹底由系統刪除,不可見。
	
	2.pid()————進程標識符:
		Linux內核通過唯一的進程標識符Pid來標識每個進程。系統中可以創建的進程數目有限。
		
		最大進程數: ulimit -u,查看最大軟限制進程數        本機16007
		
		cat /proc/sys/kernel/pid_max#查系統支持的最大進程數,一般會很大,相當於理論值,本機32768
		
		最大線程數限制爲1024
		
		線程的stack所佔用的內存,可用命令ulimits -s查看,一般是8192KB(8M))。
		
		cat /proc/sys/kernel/threads-max #查系統支持的最大線程數,一般會很大,相當於理論值,本機32015
		
		因此理論上講,用戶空間大小/棧大小=最大線程數。3072M/8M=384
		
7.進程的創建、執行和終止
		1.進程的創建和執行
		Linux中進程的創建分爲兩部分:fork() 和 exec函數族
		
		fork()通過複製當前進程創建一個子進程,exec()函數族負責讀取可執行文件並將其載入地址空間運行。
		
		fork()創建的子進程與父進程的區別僅僅在不同的 PID、PPID(父進程號 )、以及某些資源和統計量。
		fork()函數返回兩個值,父進程返回子進程的進程號,子進程返回0,因此可以利用返回值區別到底是父進程還是子進程。
		(原因:子進程是父進程的複製品,繼承了父進程的絕大數信息,子進程獨有的只有自己的進程號、資源使用和計時器)
		
		Linux在fork()創建進程時,一開始並沒有給子進程分配資源,而是需要在子進程寫入數據時候才分配
			在此之前只是以只讀的方式共享父進程的資源,這樣可以使Linux擁有更快的執行力。
			
		2.進程的終止
		進程終止,系統必須保證進程所佔用的資源回收,並通知父進程
		Linux首先把終止的進程設置爲僵死狀態(子進程結束、父進程未退出、且未回收子進程資源)
			此時子進程無法執行,只能爲父進程提供信息,父進程調用wait函數族回收子進程的退出狀態,子進程的資源被釋放

8、進程的內存結構
	Linux系統採用虛擬內存管理技術,使得每個進程都有獨立的地址空間。
	
	該地址空間大小爲 4G,線性,利用這種虛擬地址不但更安全(用戶無法直接訪問物理內存),而且比實際物理內存更大
	
	進程空間分配:
	4G的進程地址空間被分成 2 部分:1.用戶空間(0-3GB)     2.內核空間(3-4GB)
	通常情況下用戶進程只能訪問用戶空間的虛擬地址,只有用戶進程使用系統調用時纔可以訪問內核空間
	用戶空間隨着進程的變化而變化,內核空間是固定的。每個進程的用戶空間是完全獨立、互不干擾的。
	
	用戶空間包括以下幾個功能區域:
	1. 只讀段————————包含程序代碼和只讀數據
	2. 數據段————————存放全局變量和靜態變量
	3. 棧————————————存放函數的參數值、局部變量的值、返回地址等
	4. 堆————————————存放動態分配的數據,一般由程序員動態分配和釋放。malloc();
	5. 共享庫的內存映射區域,這是Linux動態連接器和其他共享代碼的映射區域。
	
9、線程
	進程是系統中程序執行和資源分配的基本單位。線程是輕量級的進程。
	一個進程可以擁有多個線程,其中每個線程共享該進程所有的資源。
	線程指的是共享相同地址空間的多個任務,線程共享同一片通信空間。
	
	線程間的通信用全局變量,主線程死了,子線程也會死,空間將被釋放,與進程不一樣。
	
	exit()和_exit():
	這個兩個函數都是用來終止進程的,當程序執行到 exit() 和 _exit()時,進程會無條件地停止剩下的所有操作,清除數據結構,終止運行
	
	_exit()函數的作用是直接使進程停止運行,清除器使用的內存空間,清除其在內核中各種數據結構;
	exit()函數則是在在此基礎上,執行退出前加了很多工序。
	
	exit()和_exit()最大的區別在於exit()函數在終止進程前要檢查該進程打開了哪些文件,並將文件緩衝區的內容寫會文件。
	對應每個打開的文件,在內存中都有一片緩衝區。
	
	若想保證數據的完整性,最好使用exit()函數。
	
	wait() 和 waitpid():(阻塞與非阻塞)
	wait()函數用於使父進程阻塞(調用wait()的進程爲父進程),直到一個子進程結束或該父進程接收特定指令爲止。
		如果該進程沒有子進程,或者他的子進程已經結束,wait()立刻返回 -1;————————阻塞
	waitpid()函數作用於wait()一樣,但並不一定等待第一個終止的子進程。——————————非阻塞	

10、Linux守護進程
	守護進程也就是常說的Daemon進程,是Linux中的後臺服務進程,常常在系統啓動時開始執行,在系統關閉時終止。
	守護進程不收終端關閉的影響,不會因爲用戶、終端或者其他變化而受到影響。

6、進程間通信

1、Linux中使用較多的進程間通信方式:
	1. 無名管道(pipe)與有名管道(fifo):
		無名管道可用於具有親緣關係的進程間通信(fork()建立的父子間進程通信)
		有名管道除了具有和無名管道相似的功能外,它還允許 無親緣關係的進程進行通信
		管道類似水管,存在兩端,且,管道只存在於內存中,因此管道也是內存。
		
	2. 信號(signal):
		信號是在軟件層次上對中斷機制的一種模擬,用於通知進程某時間的發生。
	
	3. 消息隊列(message queue):
		消息隊列是消息的鏈接表,克服了信號和管道中信息量有限的缺點
	
	4. 共享內存(shared memory):
		最有效的進程間通信方式。使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程對共享內存中數據的更新
		共享內存需要依靠某種同步機制:互斥鎖、信號量等
		
	5. 信號量(semaphore):
		主要作爲進程間以及統一進程的不同線程之間的同步和互斥手段
		
	6. 套接字(socket):
		它可用於網絡中不同主機間的進程間通信。
		
	注意:網絡就是一種特殊的管道,讀和寫都可判斷網絡中對方走沒走,讀走了管道會破裂,寫走了管道會阻塞或讀出0;
	
	
2、詳細介紹進程間六大通信方式:
	1. 管道:分有名管道和無名管道
		管道類似水管,存在兩端,它把一個程序的輸出直接連接到另外一個程序的輸入。
			1.1 無名管道(pipe):
				1. 只能用於具有親緣關係的進程間通信(父子進程、兄弟進程)
				2. 是單工模式(左——>右,右——>左),具有固定的讀端和寫端,要麼進程只能讀管道,要麼進程智能寫管道。
				3. 管道是特殊的文件,具有文件描述符,對於它的讀寫可以使用read()、write()等函數,但它不屬於任何文件系統。
				4. 只存在於內存中,所以必須通過繼承的方式制定另一個管道。
				
			1.2 有名管道(fifo):
				1. 可以使互不相關的兩個進程間實現彼此通信
				2. 擁有實體,在文件系統中可見。建立了管道後,兩個進程就可以把它當做普通文件進行讀寫操作。
				3. 有名管道遵從先進先出的原則,有名管道的讀從開始出返回數據,寫是將數據追加到末尾,且不支持lseek()定位
				4. 有名管道實際上只起標記作用,無名管道是介質。
				
			1.3 無名管道的創建、關閉
				1. 無名管道是基於文件描述符的通信方式,當創建管道時,會創建兩個文件描述符,fd[0]固定讀,fd[1]固定於寫管道
				2. 管道的關閉只需要用close()關閉兩個文件描述符就可。
				3. 用pipe()創建的管道處於同一進程,但管道屬於進程間通信,因此還需要fork()一個進程,該進程會繼承父進程的管道
					當父進程和子進程都有管道後,無名管道是單工模式,因此需要關閉各自一個不用的讀/寫端描述符。當關閉後就自動連接了
				
			1.4 有名管道的創建、關閉
				1. 有名管道的創建可用mkfifo()函數,也可以在命令行使用“mknod <有名管道名> ”來創建
				2. 創建成功後便可與普通文件一樣使用open()、write()、read()函數,需要注意讀管道用O_RDONLY,寫用O_WRONLY
				3. 對於讀進程:當FIFO中沒有數據,讀端進程一直阻塞有數據,或者FIFO寫端被關閉
				4. 對於寫進程:只要FIFO中有空間,數據就可以被寫入,若空間不足,寫進程阻塞,直到寫入。
			
			注意:
				1. 只有讀端存在,向管道寫入數據在有意義。
				2. 向管道寫入數據,如果管道緩衝區已滿,寫操作將阻塞,只要緩衝區有空間,進程將試圖向管道寫數據
				3. 父子進程在運行時,並不保證先後次序
				
	2. 信號:
		信號是在軟件層上對中斷機制的一種模擬;是進程間通信機制中唯一的異步通信機制;
		1. 信號是異步的,一個進城不必通過任何操作來等待信號的到達。
			同步:按照一定的先後順序
			異步:沒有嚴格的先後順序,上一次的結果可以等下次來看
			https://blog.csdn.net/qq_37960603/article/details/83384635
		
		2. 信號事件的產生由硬件來源(如按下鍵盤)和軟件來源,常用的信號相關參數:kill()、raise()、alarm()、setitimer()、sigqueue()
		3. 進程可以以三種方式來響應一個信號:
			3.1 忽略信號
				不對信號做任何處理:SIGKILL 和 SIGSTOP 這兩個信號不能被忽略,也不能自定義處理
			
			3.2 捕捉信號:
				定義信號捕捉函數,當信號發生時,執行相應的處理函數(想一想Qt中的信號和槽)
			
			3.3 執行默認操作:
				linux對每種信號都規定了默認操作
		4. 信號相關的函數,包括信號的發送與設置:
			4.1 發送信號的函數: kill() 、raise()
			4.2 設置信號的函數: signal()、sigaction()
			4.3 其他函數: alarm()、pause()
			
			信號的發送:
				1. kill():可以發送信號給進程或進程組,不止可以終止進程,也可以發送其他信號(向指定的進程發送信號)
				2. raise():只允許進程向自己本身發送信號。
				區別: kill(pid_t pid, int sig)      raise(int sig)    sig爲信號類型,比如:SIGKILL,SIGSTOP
			信號的設置:
				1. signal(): 捕捉信號類型和進行指定信號的處理函數。不支持信號傳遞、主要用於前32種非實時信號的處理
				   void (* signal( int signum, void( * handler)(int)))(int):   sugnum 爲要關聯的某個信號, 內void()關聯函數
				   signal 只是關聯函數,並不等待。
				2.sigaction():  檢查或修改與指定信號相關聯的處理動作(可同時操作)
				int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
							signum:指出要捕捉的信號類型, act: 新的信號處理方式, oldact: 輸出先前信號的處理方式

	3.	信號量:
		信號量是用來解決進程\線程之間同步與互斥問題的一種機制,包括一個稱爲信號量的變量和
		在該信號量下等待資源的進程等待隊列,以及對信號量進行的兩個原子操作( P V 操作)
		
		信號量(Semaphore)由一個值和一個指針組成,指針指向等待該信號量的進程。
		信號量的值表示相應資源的使用情況。信號量S≥0時,S表示可用資源的數量。
		
		任務間的同步、互斥關係存在的根源在於臨界資源。
		
		臨界資源: 在同一時刻只允許有限個(常爲 1 )任務可以訪問(讀)或修改(寫)的資源,包括硬件資源和軟件資源
		
		訪問臨界資源的代碼被稱爲臨界區。
		
		信號量值指的是當前可用的資源的數量,若 = 0,則意味着當前沒有可用的資源
		
		P V 操作:( P 分配資源,V釋放資源)
				P: 如果有可用資源,信號量值 > 0,則佔用一個資源(信號量 - 1),如果沒有,則阻塞
				V: 如果在該信號量的等待隊列中有任務在等待資源,則喚醒一個阻塞任務,若沒有任務等待它,則釋放一個資源。
				INIT_VAL(S)————對信號量S進行初始化, P(S) 進行P操作,V(S)進行V操作
				
		最簡單的信號量只有 0 1兩種值, 這種信號量被稱爲二值信號量。
		
		信號量編程:
			1. 創建信號量或獲得系統已存在的信號量
				調用semget()函數。不同進程通過使用同一個信號量鍵值來獲得同一個信號量
			2. 初始化信號量
				使用semctl()函數的SETVAL操作,常將二維信號量初始化爲1
			3. 信號量的 P V操作
				調用semop()函數,進程間同步與互斥的核心操作
			4. 如果不需要信號量,則從系統刪除
				調用semclt()函數的IPC_RMID操作。已經被刪除的不能再次刪除。
	4.	共享內存:
		在Linux中,每個進程都有屬於自己的進程控制塊(PCB)和地址空間(Addr Space),並且都有一個與之對應的頁表,
		頁表負責將進程的虛擬地址與物理地址進行映射,通過內存管理單元(MMU)進行管理。
		兩個不同的虛擬地址通過頁表映射到物理空間的同一區域,它們所指向的這塊區域即共享內存。
		
		共享內存不提供任何的同步與互斥關係,也就是說,在第一個進程結束對共享內存的寫操作之前,並無自動機制可以阻止第二個進程開始對它進行讀取,
		所以我們通常需要用其他的機制來同步對共享內存的訪問,例如信號量。
		
		共享內存的具體位置: 
			進程間需要共享的數據被放在一個叫做 IPC 共享內存區域的地方,
			所有需要訪問該共享區域的進程都要把該共享區域映射到本進程的地址空間中去。
		
		共享內存實現:
			1. 創建:int shmget( key_t key, int size, int shmflg);
			key:鍵值,多個進程通過key訪問同一個共享內存
			size:共享內存的大小,單位:字節
			shmflg:權限,可用同open()的權限位,也可以是八進制表示法,常用IPC_CREAT|0666表示
		
			2. 映射共享內存:將這段創建的貢獻內存映射到具體的進程空間中。 
				void shmat( int shmid, const void * shmaddr, int shmflg)
				shmid: 要映射的共享內存區域標識符
				shmaddr: 將共享內存映射到指定地址(爲0則表示系統自動分配地址並將該段共享內存映射到調用進程的地址空間)
				shmaddr: 要指定的映射地址, 默認爲NULL
				shmflg: 權限, SHM_RDONLY,共享內存只讀; 默認0,共享內存可讀寫
				返回值: 1.成功: 被映射的段地址    2.出錯:-1
				
			3. 撤銷映射
			4. 刪除共享內存:int shmdt( const void * shmaddr)
				shmaddr: 被映射的共享內存段地址
				返回值: 1.成功:0       2.出錯:-1

			總結:1.用shmget()申請一共享內存區間
				  2.將創建好的共享內存映射到用戶空間(系統調用)
				  3.撤銷映射
				  4.刪除共享內存
	
	5.消息隊列:
		消息隊列就是一些消息的列表,用戶可以在消息隊列中添加消息和讀取消息。這些消息存在於內核,由隊列ID標識。
		消息隊列的實現:
			1. 創建和打開消息隊列——————msgget();消息隊列數量會受到系統限制
				msgget( key_t key, int msgflg)
					key:鍵值,多個進程通過key訪問同一個個消息隊列, msgflg: 權限標誌位: IPC_CREAT | 0666
					
			2. 添加消息————————————————msgsnd();將消息添加到已打開的的消息隊列末位
				msgsnd( int msqid, const void * msgp, size_t msgsz, int msgflg)
				msqid:消息隊列的ID; msgp: 指向消息隊列的指針,此指針含有消息類型long mtype 和消息正文 char mtext[1];
				msgsz: 消息正文的字節數; msgflg: IPC_NOWAIT 若消息無法發出則函數立即返回;0:msgsnd調用被阻塞至發送成功爲止
				
			3. 讀取消息————————————————msgrcv();把消息從消息隊列中取走,可以取走指定的某一消息
				
			4..控制消息隊列————————————msgctl();可以完成多項功能。

7、Linux多線程編程

  線程是進程內獨立的一條運行路線,也是內核調度的最小單元,也被稱爲輕量級進程
1.	線程的基本編程

																																																			創建線程時,要制定線程的執行函數,創建之後就開始執行相應的線程函數,在該函數運行結束時,線程結束。
線程的創建:int pthread_creat( pthread_t *thread, pthread_attr_t * attr, void *( *start_routine)(void *), void *arg);
			thread: 線程標識符;
			attr: 線程屬性設置,NULL表示缺省屬性
			start_routine: 線程執行函數,參數和返回值都是void *;
			arg:傳遞給start_routine的參數。

		線程的退出:void pthread_exit( void * retval)
			retval: 線程結束時的返回值,可通過pthread_join函數來接收
			
		線程的回收:int pthread_jion( pthread_t thread, void ** thread_result)
			thread: 等待線程的標識符
			thread_result:用戶定義的指針,用來接收被等待線程結束時的返回值
			
		線程的終止:int pthread_cancel( pthread_t thread)
			thread: 要取消的線程的標識符
			
注意:線程的退出不能用exit(),這是進程的退出方式,一個進程可能含多個線程。pthread_cancel()用於一個終止另一個線程;
  1. 線程之間的同步與互斥

       同步與互斥機制:1. 互斥鎖     2.信號量   
       互斥鎖:互斥鎖通過簡單地加鎖方法來保證對共享資源的原子操作。																																																						                                   互斥鎖只存在兩種方式: 1.上鎖   2.解鎖    (某種意義上可以將互斥鎖看成全局變量)
       互斥鎖機制的基本函數:         
            1. 互斥鎖初始化:  int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t * mutexattr);  						  mutex:   互斥鎖     mutexattr: 互斥鎖屬性、NULL 爲缺省屬性;
     	
     		2. 互斥鎖上鎖:    int pthread_mutex_lock( pthread_mutex_t * mutex); 加鎖,若不成功則阻塞
     						   								   								  	
     		3. 互斥鎖判斷上鎖:int pthread_mutex_trylock( pthread_mutex_t * mutex); 加鎖,若不成功則返回
     		
     		4. 互斥鎖解鎖:    int pthread_mutex_unlock( pthread_mutex_t * mutex);解鎖
     		
     		5. 互斥鎖消除:    int pthread_mutex_destroy( pthread_mutex_t * mutex);刪除互斥鎖
     					
     	2.信號量
     		信號量也就是操作系統所用的 P V 操作,本質上是一個非負的整數計數器。
     		信號量(Semaphore)由一個值和一個指針組成,指針指向等待該信號量的進程。
     		信號量的值表示相應資源的使用情況。信號量S≥0時,S表示可用資源的數量
     	
     		PV原子操作主要用於浸出那個或線程間的同步和互斥這兩種典型情況。
     		1. 互斥:幾個進程或線程往往只設置一個信號量 sem;
     		2. 同步:幾何進程或線程往往會設置多個信號量並安排不同的初始值來實現他們之間的順序執行。
     		
     	3.無名信號量:
     		Linux實現POSIX的無名信號量,主要用於線程間的同步與互斥
     		
     		1. 初始化信號量: int sem_init( sem_t *sem, int pshared, unsigned int value)
     					sem: 信號量對象   pshared:決定信號量能否在進程間共享,= 0則表示信號量用在進程內部的線程間
     				  value: 信號量初始化值
     				  
     		2. P 操作: int sem_wait( sem_t * sem)或 int sem_trywait( sem_t * sem);在信號量 >0時,都能將信號量-1——————(申請資源)
     			 區別: 若信號量爲0,sem_wait()會阻塞線程, sem_trywait()會立即返回。
     		
     		3. V 操作:int sem_post( sem_t * sem);它將信號量的值+1,同時喚醒等待的線程  ————————(釋放資源),釋放信號
     		
     		4. 捕獲信號量的值: int sem_getvalue( sem_t * sem);
     		
     		5. 刪除信號量: int sem_destroy( sem_t * sem);
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章