什么是進(jìn)程
1、進(jìn)程和線程的區(qū)別
進(jìn)程是指正在運(yùn)行的程序,它擁有獨(dú)立的內(nèi)存空間和系統(tǒng)資源,不同進(jìn)程之間的數(shù)據(jù)不共享。進(jìn)程是資源分配的基本單位。
線程是進(jìn)程內(nèi)的執(zhí)行單元,它與同一進(jìn)程內(nèi)的其他線程共享進(jìn)程的內(nèi)存空間和系統(tǒng)資源。線程是調(diào)度的基本單位。
2、進(jìn)程的創(chuàng)建和銷毀
在Linux中啟動(dòng)一個(gè)進(jìn)程有多種方法:
(1)通過system函數(shù)啟動(dòng)進(jìn)程。(使用簡單,效率較低)
?
#include? /** ?*?@brief?執(zhí)行系統(tǒng)命令調(diào)用命令處理器來執(zhí)行命令 ?* ?*?Detailed?function?description ?* ?*?@param[in]?command:?包含被請求變量名稱的?C?字符串 ?* ?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。 ?*/ int?system(const?char?*command);
?
例子:通過system函數(shù)啟動(dòng)一個(gè)進(jìn)程,列出當(dāng)前目錄下的文件及文件夾。
?
#include? #include? int?main(void) { ????system("ls"); ????printf("ls?end "); ????return?0; }
?
(2)通過fork函數(shù)啟動(dòng)進(jìn)程。(用于啟動(dòng)子進(jìn)程)
?
#include? #include? /** ?*?@brief?fork系統(tǒng)調(diào)用用于創(chuàng)建一個(gè)子進(jìn)程 ?* ?*?Detailed?function?description ?* ?*?@param[in] ?* ?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。 ?*/ pid_t?fork(void);
?
例子:通過fork函數(shù)啟動(dòng)子進(jìn)程
?
#include? #include? #include? #include? int?main(void)? { ????pid_t?res?=?fork(); ????///?0)? ????{ ????????printf("res?=?%d,?I?am?parent?process.?pid?=?%d ",?res,?getpid()); ????????int?child_status?=?0; ????????pid_t?child_pid?=?wait(&child_status);???///?
編譯、運(yùn)行:
我們使用了fork()系統(tǒng)調(diào)用來創(chuàng)建一個(gè)新進(jìn)程。如果fork()返回值為0,則說明當(dāng)前進(jìn)程是子進(jìn)程;如果返回值大于0,則說明當(dāng)前進(jìn)程是父進(jìn)程。在父進(jìn)程中,我們使用wait()系統(tǒng)調(diào)用來等待子進(jìn)程結(jié)束。當(dāng)子進(jìn)程結(jié)束后,父進(jìn)程會(huì)繼續(xù)執(zhí)行。
(3)通過exec系列函數(shù)啟動(dòng)進(jìn)程。(用于啟動(dòng)新進(jìn)程,新進(jìn)程會(huì)覆蓋舊進(jìn)程)
?
#include? /** ?*?@brief?啟動(dòng)新進(jìn)程,新進(jìn)程會(huì)覆蓋舊進(jìn)程 ?* ?*?Detailed?function?description ?* ?*?@param[in]?path:?所執(zhí)行文件的路徑 ?*?@param[in]?file:?所執(zhí)行文件的名稱 ?*?@param[in]?arg:?傳入的參數(shù)列表,以NULL作為結(jié)束 ?*?@param[in]?envp:?傳入的環(huán)境變量 ?* ?*?@return?如果發(fā)生錯(cuò)誤,則返回值為?-1,否則返回命令的狀態(tài)。 ?*/ int?execl(const?char?*path,?const?char?*arg,?...); int?execlp(const?char?*file,?const?char?*arg,?...); int?execle(const?char?*path,?const?char?*arg,?...,?char?*const?envp[]); int?execv(const?char?*path,?char?*const?argv[]); int?execvp(const?char?*file,?char?*const?argv[]); int?execve(const?char?*path,?char?*const?argv[],?char?*const?envp[]);?
例子:通過execl()函數(shù)的參數(shù)列表調(diào)用了ls命令程序
?
#include? #include? int?main(void) { ????execl("/bin/ls",?"ls",?"-la",?NULL); ????printf("ls?end "); ????return?0; }?
execl()函數(shù)的參數(shù)列表調(diào)用了ls命令程序,與在終端上運(yùn)行”ls -la”產(chǎn)生的結(jié)果是一樣的。
在Linux中終止一個(gè)進(jìn)程有多種方法:
從main函數(shù)返回。(正常終止)
調(diào)用exit()函數(shù)終止。(正常終止)
調(diào)用_exit()函數(shù)終止。(正常終止)
調(diào)用abort()函數(shù)終止。(異常終止)
由系統(tǒng)信號終止。(異常終止)
進(jìn)程間通信方式
進(jìn)程間通信是指在不同進(jìn)程之間傳播或交換信息的一種機(jī)制。每個(gè)進(jìn)程各自有不同的用戶地址空間,任何一個(gè)進(jìn)程的全局變量在另一個(gè)進(jìn)程中都看不到,所以進(jìn)程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進(jìn)程A把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進(jìn)程B再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信。
進(jìn)程間通信的目的:
傳輸數(shù)據(jù)。比如進(jìn)程 A 負(fù)責(zé)生成數(shù)據(jù),進(jìn)程 B 負(fù)責(zé)處理數(shù)據(jù),數(shù)據(jù)需要從 A 進(jìn)程傳輸至 B 進(jìn)程。
共享資源。比如進(jìn)程 A 與進(jìn)程 B 共享某一塊內(nèi)存資源。
模塊化。將系統(tǒng)功能劃分為多個(gè)進(jìn)程模塊進(jìn)行開發(fā),方便開發(fā)維護(hù)。
加速計(jì)算。多核處理器環(huán)境,一個(gè)特定進(jìn)程劃分為幾個(gè)進(jìn)程并行運(yùn)行。
Linux IPC(Inter-process Comminication, 進(jìn)程間通信)的方式:
1、消息隊(duì)列
內(nèi)核中的一個(gè)優(yōu)先級隊(duì)列,多個(gè)進(jìn)程通過訪問同一個(gè)隊(duì)列,進(jìn)行添加結(jié)點(diǎn)或者獲取結(jié)點(diǎn)實(shí)現(xiàn)通信。
POSIX消息隊(duì)列頭文件:
?
#include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include??
編譯鏈接需要加上 -lrt 鏈接。
消息隊(duì)列API接口:
?
/** ?*?@brief?創(chuàng)建消息隊(duì)列實(shí)例 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?消息隊(duì)列名稱 ?*?@param[in]?oflag:根據(jù)傳入標(biāo)識來創(chuàng)建或者打開一個(gè)已創(chuàng)建的消息隊(duì)列 ????????????????????-?O_CREAT:?創(chuàng)建一個(gè)消息隊(duì)列 ????????????????????-?O_EXCL:?檢查消息隊(duì)列是否存在,一般與O_CREAT一起使用 ????????????????????-?O_CREAT|O_EXCL:?消息隊(duì)列不存在則創(chuàng)建,已存在返回NULL ????????????????????-?O_NONBLOCK:?非阻塞模式打開,消息隊(duì)列不存在返回NULL ????????????????????-?O_RDONLY:?只讀模式打開 ????????????????????-?O_WRONLY:?只寫模式打開 ????????????????????-?O_RDWR:?讀寫模式打開 ?*?@param[in]?mode:訪問權(quán)限 ?*?@param[in]?attr:消息隊(duì)列屬性地址 ?* ?*?@return?成功返回消息隊(duì)列描述符,失敗返回-1,錯(cuò)誤碼存于error中 ?*/ mqd_t?mq_open(const?char?*name,?int?oflag,??mode_t?mode,?struct?mq_attr?*attr); /** ?*?@brief?無限阻塞方式接收消息 ?* ?*?Detailed?function?description ?* ?*?@param[in]?mqdes:?消息隊(duì)列描述符 ?*?@param[in]?msg_ptr:消息體緩沖區(qū)地址 ?*?@param[in]?msg_len:消息體長度,長度必須大于等于消息屬性設(shè)定的最大值 ?*?@param[in]?msg_prio:消息優(yōu)先級 ?* ?*?@return?成功返回消息長度,失敗返回-1,錯(cuò)誤碼存于error中 ?*/ mqd_t?mq_receive(mqd_t?mqdes,?char?*msg_ptr,?size_t?msg_len,?unsigned?*msg_prio); /** ?*?@brief?指定超時(shí)時(shí)間阻塞方式接收消息 ?* ?*?Detailed?function?description ?* ?*?@param[in]?mqdes:?消息隊(duì)列描述符 ?*?@param[in]?msg_ptr:消息體緩沖區(qū)地址 ?*?@param[in]?msg_len:消息體長度,長度必須大于等于消息屬性設(shè)定的最大值 ?*?@param[in]?msg_prio:消息優(yōu)先級 ?*?@param[in]?abs_timeout:超時(shí)時(shí)間 ?* ?*?@return?成功返回消息長度,失敗返回-1,錯(cuò)誤碼存于error中 ?*/ mqd_t?mq_timedreceive(mqd_t?mqdes,?char?*msg_ptr,?size_t?msg_len,?unsigned?*msg_prio,?const?struct?timespec?*abs_timeout); /** ?*?@brief?無限阻塞方式發(fā)送消息 ?* ?*?Detailed?function?description ?* ?*?@param[in]?mqdes:?消息隊(duì)列描述符 ?*?@param[in]?msg_ptr:待發(fā)送消息體緩沖區(qū)地址 ?*?@param[in]?msg_len:消息體長度 ?*?@param[in]?msg_prio:消息優(yōu)先級 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ mqd_t?mq_send(mqd_t?mqdes,?const?char?*msg_ptr,?size_t?msg_len,?unsigned?msg_prio); /** ?*?@brief?指定超時(shí)時(shí)間阻塞方式發(fā)送消息 ?* ?*?Detailed?function?description ?* ?*?@param[in]?mqdes:?消息隊(duì)列描述符 ?*?@param[in]?msg_ptr:待發(fā)送消息體緩沖區(qū)地址 ?*?@param[in]?msg_len:消息體長度 ?*?@param[in]?msg_prio:消息優(yōu)先級 ?*?@param[in]?abs_timeout:超時(shí)時(shí)間 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ mqd_t?mq_timedsend(mqd_t?mqdes,?const?char?*msg_ptr,?size_t?msg_len,?unsigned?msg_prio,?const?struct?timespec?*abs_timeout); /** ?*?@brief?關(guān)閉消息隊(duì)列 ?* ?*?Detailed?function?description ?* ?*?@param[in]?mqdes:?消息隊(duì)列描述符 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ mqd_t?mq_close(mqd_t?mqdes); /** ?*?@brief?分離消息隊(duì)列 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?消息隊(duì)列名稱 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ mqd_t?mq_unlink(const?char?*name);?
消息隊(duì)列基本API接口使用例子:發(fā)送進(jìn)程給接收進(jìn)程發(fā)送測試數(shù)據(jù)。
send.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #define?MQ_MSG_MAX_SIZE????512??///?
recv.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #define?MQ_MSG_MAX_SIZE????512??///?
編譯、運(yùn)行:
?
gcc?send.c?-o?send_process?-lrt gcc?recv.c?-o?recv_process?-lrt?
2、共享內(nèi)存
消息隊(duì)列的讀取和寫入的過程,會(huì)有發(fā)生用戶態(tài)與內(nèi)核態(tài)之間的消息拷貝過程。而共享內(nèi)存的方式則沒有這個(gè)拷貝過程,進(jìn)程間通信速度較快。
在物理內(nèi)存上開辟一塊內(nèi)存空間,多個(gè)進(jìn)程可以將同一塊物理內(nèi)存空間映射到自己的虛擬地址空間,通過自己的虛擬地址直接訪問這塊空間,通過這種方式實(shí)現(xiàn)數(shù)據(jù)共享。
POSIX共享內(nèi)存頭文件:
?
#include? #include? #include??
共享內(nèi)存API接口:
?
/** ?*?@brief?創(chuàng)建共享內(nèi)存實(shí)例 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?要打開或創(chuàng)建的共享內(nèi)存文件名 ?*?@param[in]?oflag:打開的文件操作屬性 ????????????????????-?O_CREAT:?創(chuàng)建一個(gè)共享內(nèi)存文件 ????????????????????-?O_EXCL:?檢查共享內(nèi)存是否存在,一般與O_CREAT一起使用 ????????????????????-?O_CREAT|O_EXCL:?共享內(nèi)存不存在則創(chuàng)建,已存在返回NULL ????????????????????-?O_NONBLOCK:?非阻塞模式打開,共享內(nèi)存不存在返回NULL ????????????????????-?O_RDONLY:?只讀模式打開 ????????????????????-?O_WRONLY:?只寫模式打開 ????????????????????-?O_RDWR:?讀寫模式打開 ?*?@param[in]?mode:文件共享模式,例如?0777 ?* ?*?@return?成功返回共享內(nèi)存描述符,失敗返回-1,錯(cuò)誤碼存于error中 ?*/ int?shm_open(const?char?*name,?int?oflag,?mode_t?mode); /** ?*?@brief?刪除共享內(nèi)存 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?創(chuàng)建的共享內(nèi)存文件名 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?shm_unlink(const?char?*name); /** ?*?@brief?將打開的文件映射到內(nèi)存 ?* ?*?Detailed?function?description ?* ?*?@param[in]?addr:?要將文件映射到的內(nèi)存地址,一般應(yīng)該傳遞NULL來由Linux內(nèi)核指定 ?*?@param[in]?length:?要映射的文件數(shù)據(jù)長度 ?*?@param[in]?prot:?映射的內(nèi)存區(qū)域的操作權(quán)限(保護(hù)屬性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE ?*?@param[in]?flags:?標(biāo)志位參數(shù),包括:MAP_SHARED、MAP_PRIVATE與MAP_ANONYMOUS。 ?*?@param[in]?fd:??用來建立映射區(qū)的文件描述符,用?shm_open打開或者open打開的文件 ?*?@param[in]?offset:?映射文件相對于文件頭的偏移位置,應(yīng)該按4096字節(jié)對齊 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ void?*mmap(void?*addr,?size_t?length,?int?prot,?int?flags,?int?fd,?off_t?offset); ? /** ?*?@brief?取消內(nèi)存映射 ?* ?*?Detailed?function?description ?* ?*?@param[in]?addr:?由mmap成功返回的地址 ?*?@param[in]?length:?要取消的內(nèi)存長度 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?munmap(void?*addr,?size_t?length); /** ?*?@brief?將參數(shù)fd指定的文件大小改為參數(shù)length指定的大小 ?* ?*?Detailed?function?description ?* ?*?@param[in]?fd:?已打開的文件描述符,以寫入模式打開的文件 ?*?@param[in]?length:?要設(shè)置的長度 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?ftruncate(int?fd,off_t?length); /** ?*?@brief?獲取文件相關(guān)的信息,將獲取到的信息放入到statbuf結(jié)構(gòu)體中 ?* ?*?Detailed?function?description ?* ?*?@param[in]?fd:?已打開的文件描述符 ?*?@param[out]?statbuf:?文件的信息 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?fstat(int?fd,?struct?stat?*statbuf);?
共享內(nèi)存基本API接口使用例子:發(fā)送進(jìn)程給接收進(jìn)程發(fā)送測試數(shù)據(jù)。
send.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #define?SHM_NAME?"/shm" int?main(void) { ????int?ret?=?0; ????///?
recv.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #define?SHM_NAME?"/shm" int?main(void) { ????///?
編譯、運(yùn)行:
?
gcc?send.c?-o?send_process?-lrt gcc?recv.c?-o?recv_process?-lrt?
對具有多個(gè)處理核系統(tǒng)消息傳遞的性能要優(yōu)于共享內(nèi)存。共享內(nèi)存會(huì)有高速緩存一致性問題,這是由共享數(shù)據(jù)在多個(gè)高速緩存之間遷移而引起的。隨著系統(tǒng)的處理核的數(shù)量的日益增加,可能導(dǎo)致消息傳遞作為 IPC 的首選機(jī)制。
3、socket
UNIX域套接字與傳統(tǒng)基于TCP/IP協(xié)議棧的socket不同,unix domain socket以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報(bào)文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗(yàn)與重傳過程,因此傳輸速率上也不會(huì)受網(wǎng)卡帶寬的限制。
unix domain socket在進(jìn)程間通信同樣是基于“客戶端—服務(wù)器”(C-S)模式。
UNIX域套接字基本API接口使用例子:基于UNIX域套接字客戶端進(jìn)程向服務(wù)端進(jìn)程發(fā)送測試數(shù)據(jù)。
server.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #include? #include? #include? #define?SERVER_PATH?"/tmp/server"? int?main(void) { ?///?
client.c:
?
#include? #include? #include? #include? #include????????????/*?For?O_*?constants?*/ #include?????????/*?For?mode?constants?*/ #include? #include? #include? #include? #define?SERVER_PATH?"/tmp/server" #define?CLIENT_PATH?"/tmp/client" int?main(void) { ?///?
編譯、運(yùn)行:
?
gcc?server.c?-o?server_process gcc?client.c?-o?client_process?
類socket的其它進(jìn)程間通信方式:
實(shí)用 | nanomsg通信庫的簡單使用分享
mqtt應(yīng)用于進(jìn)程間通信
4、管道
在內(nèi)核中開辟一塊緩沖區(qū);若多個(gè)進(jìn)程拿到同一個(gè)管道(緩沖區(qū))的操作句柄,就可以訪問同一個(gè)緩沖區(qū),就可以進(jìn)行通信。涉及到兩次用戶態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝。
(1)匿名管道
內(nèi)核中的緩沖區(qū)是沒有具體的標(biāo)識符的,匿名管道只能用于具有親緣關(guān)系的進(jìn)程間通信。
調(diào)用pipe接口可以創(chuàng)建一個(gè)匿名管道,并返回了兩個(gè)描述符,一個(gè)是管道的讀取端描述符 fd[0],另一個(gè)是管道的寫入端描述符 fd[1]。
管道是一個(gè)半雙工通信(可以選擇方向的單向傳輸)
匿名管道基本API接口使用例子:父進(jìn)程通過管道發(fā)送測試數(shù)據(jù)給子進(jìn)程。
?
#include? #include? #include? #include? int?main() { ????///?0) ???{ ??///?
編譯、運(yùn)行:
如果需要雙向通信,則應(yīng)該創(chuàng)建兩個(gè)管道。
(2)命名管道
命名管道也是內(nèi)核中的一塊緩沖區(qū),并且這個(gè)緩沖區(qū)具有標(biāo)識符;這個(gè)標(biāo)識符是一個(gè)可見于文件系統(tǒng)的管道文件,能夠被其他進(jìn)程找到并打開管道文件,則可以獲取管道的操作句柄,所以該命名管道可用于同一主機(jī)上的任意進(jìn)程間通信。
創(chuàng)建命名管道的接口:
?
int?mkfifo(const?char?*pathname,?mode_t?mode);?
命名管道基本API接口使用例子:一個(gè)進(jìn)程往管道中寫入測試數(shù)據(jù),另一個(gè)進(jìn)程從管道中讀取數(shù)據(jù)。
fifo_wr.c:
?
#include? #include? #include? #include? #include? #include? #include? #define?FIFO_PATH??"./fifo_file" typedef?struct?_msg_data { ????char?buf[128]; ????int?cnt; }msg_data_t; void?send_data(int?fd) { ????static?int?cnt?=?0; ????msg_data_t?send_data?=?{0}; ????cnt++; ????strcpy(send_data.buf,?"hello"); ????send_data.cnt?=?cnt; ????write(fd,?&send_data,?sizeof(send_data)); ????printf("send?msg?=?%s,?cnt?=?%d ",?send_data.buf,?send_data.cnt); } int?main(void) { ????///?
fifo_rd.c:
?
#include? #include? #include? #include? #include? #include? #include? #define?FIFO_PATH??"./fifo_file" typedef?struct?_msg_data { ????char?buf[128]; ????int?cnt; }msg_data_t; int?main(void) { ????umask(0); ????///?
編譯、運(yùn)行:
?
gcc?fifo_wr.c?-o?fifo_wr gcc?fifo_rd.c?-o?fifo_rd?
5、信號量
信號量(Seamphore)是進(jìn)程和線程間同步的一種機(jī)制。
信號量本質(zhì)是一個(gè)非負(fù)的整型變量。增加一個(gè)可用資源執(zhí)行加一,也稱為V操作;獲取一個(gè)資源資源后執(zhí)行減一,也稱為P操作。
信號量根據(jù)信號值不同可分為兩類:
二值信號量,信號量值只有0和1,初始值為1,1表示資源可用,0表示資源不可用;二值信號量與互斥鎖類似。
計(jì)數(shù)信號量, 信號量的值在0到一個(gè)大于1的限制值之間,信號值表示可用的資源的數(shù)目。
信號量根據(jù)作用對象不同可分為兩類:
有名信號量,信號值保存在文件中,用于進(jìn)程間同步
無名信號量,又稱為基于內(nèi)存信號量,信號值保存在內(nèi)存中,用于線程間同步
POSIX信號量頭文件:
?
#include??
編譯鏈接需要加-lpthread參數(shù)。
信號量API接口:
?
/** ?*?@brief?創(chuàng)建信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?信號量名稱 ?*?@param[in]?mode:?訪問權(quán)限 ?*?@param[in]?value:?信號量初始值 ?* ?*?@return?成功時(shí)返回指向信號量的指針,出錯(cuò)時(shí)為SEM_FAILED ?*/ sem_t?*sem_open(const?char?*name,int?oflag,?mode_t?mode,?unsigned?int?value); /** ?*?@brief?初始化信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?*?@param[in]?pshared:?信號量作用域,分為進(jìn)程內(nèi)作用域PTHREAD_PROCESS_PRIVATE和跨進(jìn)程作用域PTHREAD_PROCESS_SHARED ?*?@param[in]?value:?信號量初始值 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_init(sem_t?*sem,?int?pshared,?unsigned?int?value); /** ?*?@brief?獲取信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?*?@param[out]?sval:?保存返回信號值地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_getvalue(sem_t?*sem,?int?*sval); /** ?*?@brief?阻塞方式等待信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_wait(sem_t?*sem); /** ?*?@brief?指定超時(shí)時(shí)間阻塞方式等待信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?*?@param[in]?sem:?超時(shí)時(shí)間,單位為時(shí)鐘節(jié)拍 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_timedwait(sem_t?*sem,?const?struct?timespec?*abs_timeout); /** ?*?@brief?非阻塞方式等待信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_trywait(sem_t?*sem); /** ?*?@brief?產(chǎn)生信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_post(sem_t?*sem); /** ?*?@brief?銷毀信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_destroy(sem_t?*sem); /** ?*?@brief?關(guān)閉信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?sem:?信號量實(shí)例地址 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_close(sem_t?*sem); /** ?*?@brief?分離信號量 ?* ?*?Detailed?function?description ?* ?*?@param[in]?name:?信號量名稱 ?* ?*?@return?成功返回0,失敗返回-1 ?*/ int?sem_unlink(const?char?*name);?
信號量基本API接口使用例子:父子進(jìn)程間通信
?
#include? #include? #include? #include? #include? #define?SEM_NAME?"sem" int?main?(void) { ????int?sem_val?=?0; ????///?0) ????{ ????????///?
編譯、運(yùn)行:
IPC總結(jié)
操作系統(tǒng)根據(jù)不同的場景提供了不同的方式,消息隊(duì)列、共享內(nèi)存、UNIX域套接字、管道、信號量。
消息隊(duì)列: 內(nèi)核中的一個(gè)優(yōu)先級隊(duì)列,多個(gè)進(jìn)程通過訪問同一個(gè)隊(duì)列,在隊(duì)列當(dāng)中添加或者獲取節(jié)點(diǎn)來實(shí)現(xiàn)進(jìn)程間通信。
共享內(nèi)存: 本質(zhì)是一塊物理內(nèi)存,多個(gè)進(jìn)程將同一塊物理內(nèi)存映射到自己的虛擬地址空間中,再通過頁表映射到物理地址達(dá)到進(jìn)程間通信,它是最快的進(jìn)程間通信方式,相較其他通信方式少了兩步數(shù)據(jù)拷貝操作。
UNIX域套接字: 與TCP/IP套接字使用方式相同,但UNIX域套接字以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報(bào)文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗(yàn)與重傳過程,因此傳輸速率上也不會(huì)受網(wǎng)卡帶寬的限制。
管道: 內(nèi)核中的一塊緩沖區(qū),分為匿名管道和命名管道。匿名管道只能用于具有親緣關(guān)系的進(jìn)程間;而命名管道可用于同一主機(jī)上任意進(jìn)程間通信。
信號量: 本質(zhì)是內(nèi)核中的一個(gè)計(jì)數(shù)器,主要實(shí)現(xiàn)進(jìn)程間的同步與互斥,對資源進(jìn)行計(jì)數(shù),有兩種操作,分別是在訪問資源之前進(jìn)行的p操作,還有產(chǎn)生資源之后的v操作。
審核編輯:湯梓紅
評論
查看更多