
什么是進程
(相關資料圖)
1、進程和線程的區別
進程是指正在運行的程序,它擁有獨立的內存空間和系統資源,不同進程之間的數據不共享。進程是資源分配的基本單位。
線程是進程內的執行單元,它與同一進程內的其他線程共享進程的內存空間和系統資源。線程是調度的基本單位。
2、進程的創建和銷毀
在Linux中啟動一個進程有多種方法:
(1)通過system函數啟動進程。(使用簡單,效率較低)
#include/***@brief執行系統命令調用命令處理器來執行命令**Detailedfunctiondescription**@param[in]command:包含被請求變量名稱的C字符串**@return如果發生錯誤,則返回值為-1,否則返回命令的狀態。*/intsystem(constchar*command);
例子:通過system函數啟動一個進程,列出當前目錄下的文件及文件夾。
#include#includeintmain(void){system("ls");printf("lsend");return0;}
(2)通過fork函數啟動進程。(用于啟動子進程)
#include#include/***@brieffork系統調用用于創建一個子進程**Detailedfunctiondescription**@param[in]**@return如果發生錯誤,則返回值為-1,否則返回命令的狀態。*/pid_tfork(void);
例子:通過fork函數啟動子進程
#include#include#include#includeintmain(void){pid_tres=fork();///<子進程if(res==0){printf("res=%d,Iamchildprocess.pid=%d",res,getpid());exit(EXIT_SUCCESS);///<正常退出子進程}///<父進程elseif(res>0){printf("res=%d,Iamparentprocess.pid=%d",res,getpid());intchild_status=0;pid_tchild_pid=wait(&child_status);///<父進程阻塞等待信號到來或子進程結束printf("Childprocess(pid=%d)hasbeenterminated,child_status=%d",child_pid,child_status);}///<異常退出else{printf("Forkfailed.");exit(EXIT_FAILURE);}return0;}
編譯、運行:
我們使用了fork()系統調用來創建一個新進程。如果fork()返回值為0,則說明當前進程是子進程;如果返回值大于0,則說明當前進程是父進程。在父進程中,我們使用wait()系統調用來等待子進程結束。當子進程結束后,父進程會繼續執行。
(3)通過exec系列函數啟動進程。(用于啟動新進程,新進程會覆蓋舊進程)
#include/***@brief啟動新進程,新進程會覆蓋舊進程**Detailedfunctiondescription**@param[in]path:所執行文件的路徑*@param[in]file:所執行文件的名稱*@param[in]arg:傳入的參數列表,以NULL作為結束*@param[in]envp:傳入的環境變量**@return如果發生錯誤,則返回值為-1,否則返回命令的狀態。*/intexecl(constchar*path,constchar*arg,...);intexeclp(constchar*file,constchar*arg,...);intexecle(constchar*path,constchar*arg,...,char*constenvp[]);intexecv(constchar*path,char*constargv[]);intexecvp(constchar*file,char*constargv[]);intexecve(constchar*path,char*constargv[],char*constenvp[]);
例子:通過execl()函數的參數列表調用了ls命令程序
#include#includeintmain(void){execl("/bin/ls","ls","-la",NULL);printf("lsend");return0;}
execl()函數的參數列表調用了ls命令程序,與在終端上運行”ls -la”產生的結果是一樣的。
在Linux中終止一個進程有多種方法:
從main函數返回。(正常終止)
調用exit()函數終止。(正常終止)
調用_exit()函數終止。(正常終止)
調用abort()函數終止。(異常終止)
由系統信號終止。(異常終止)
進程間通信方式
進程間通信是指在不同進程之間傳播或交換信息的一種機制。每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,進程A把數據從用戶空間拷到內核緩沖區,進程B再從內核緩沖區把數據讀走,內核提供的這種機制稱為進程間通信。
進程間通信的目的:
傳輸數據。比如進程 A 負責生成數據,進程 B 負責處理數據,數據需要從 A 進程傳輸至 B 進程。
共享資源。比如進程 A 與進程 B 共享某一塊內存資源。
模塊化。將系統功能劃分為多個進程模塊進行開發,方便開發維護。
加速計算。多核處理器環境,一個特定進程劃分為幾個進程并行運行。
Linux IPC(Inter-process Comminication, 進程間通信)的方式:
1、消息隊列
內核中的一個優先級隊列,多個進程通過訪問同一個隊列,進行添加結點或者獲取結點實現通信。
POSIX消息隊列頭文件:
#include/*ForO_*constants*/#include/*Formodeconstants*/#include
編譯鏈接需要加上 -lrt 鏈接。
消息隊列API接口:
/***@brief創建消息隊列實例**Detailedfunctiondescription**@param[in]name:消息隊列名稱*@param[in]oflag:根據傳入標識來創建或者打開一個已創建的消息隊列-O_CREAT:創建一個消息隊列-O_EXCL:檢查消息隊列是否存在,一般與O_CREAT一起使用-O_CREAT|O_EXCL:消息隊列不存在則創建,已存在返回NULL-O_NONBLOCK:非阻塞模式打開,消息隊列不存在返回NULL-O_RDONLY:只讀模式打開-O_WRONLY:只寫模式打開-O_RDWR:讀寫模式打開*@param[in]mode:訪問權限*@param[in]attr:消息隊列屬性地址**@return成功返回消息隊列描述符,失敗返回-1,錯誤碼存于error中*/mqd_tmq_open(constchar*name,intoflag,mode_tmode,structmq_attr*attr);/***@brief無限阻塞方式接收消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:消息體緩沖區地址*@param[in]msg_len:消息體長度,長度必須大于等于消息屬性設定的最大值*@param[in]msg_prio:消息優先級**@return成功返回消息長度,失敗返回-1,錯誤碼存于error中*/mqd_tmq_receive(mqd_tmqdes,char*msg_ptr,size_tmsg_len,unsigned*msg_prio);/***@brief指定超時時間阻塞方式接收消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:消息體緩沖區地址*@param[in]msg_len:消息體長度,長度必須大于等于消息屬性設定的最大值*@param[in]msg_prio:消息優先級*@param[in]abs_timeout:超時時間**@return成功返回消息長度,失敗返回-1,錯誤碼存于error中*/mqd_tmq_timedreceive(mqd_tmqdes,char*msg_ptr,size_tmsg_len,unsigned*msg_prio,conststructtimespec*abs_timeout);/***@brief無限阻塞方式發送消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:待發送消息體緩沖區地址*@param[in]msg_len:消息體長度*@param[in]msg_prio:消息優先級**@return成功返回0,失敗返回-1*/mqd_tmq_send(mqd_tmqdes,constchar*msg_ptr,size_tmsg_len,unsignedmsg_prio);/***@brief指定超時時間阻塞方式發送消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:待發送消息體緩沖區地址*@param[in]msg_len:消息體長度*@param[in]msg_prio:消息優先級*@param[in]abs_timeout:超時時間**@return成功返回0,失敗返回-1*/mqd_tmq_timedsend(mqd_tmqdes,constchar*msg_ptr,size_tmsg_len,unsignedmsg_prio,conststructtimespec*abs_timeout);/***@brief關閉消息隊列**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符**@return成功返回0,失敗返回-1*/mqd_tmq_close(mqd_tmqdes);/***@brief分離消息隊列**Detailedfunctiondescription**@param[in]name:消息隊列名稱**@return成功返回0,失敗返回-1*/mqd_tmq_unlink(constchar*name);
消息隊列基本API接口使用例子:發送進程給接收進程發送測試數據。
send.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineMQ_MSG_MAX_SIZE512///<最大消息長度#defineMQ_MSG_MAX_ITEM5///<最大消息數目staticmqd_ts_mq;typedefstruct_msg_data{charbuf[128];intcnt;}msg_data_t;voidsend_data(void){staticintcnt=0;msg_data_tsend_data={0};cnt++;strcpy(send_data.buf,"hello");send_data.cnt=cnt;intret=mq_send(s_mq,(char*)&send_data,sizeof(send_data),0);if(ret<0){perror("mq_senderror");return;}printf("sendmsg=%s,cnt=%d",send_data.buf,send_data.cnt);}intmain(void){intret=0;structmq_attrattr;///<創建消息隊列memset(&attr,0,sizeof(attr));attr.mq_maxmsg=MQ_MSG_MAX_ITEM;attr.mq_msgsize=MQ_MSG_MAX_SIZE;attr.mq_flags=0;s_mq=mq_open("/mq",O_CREAT|O_RDWR,0777,&attr);if(-1==s_mq){perror("mq_openerror");return-1;}for(size_ti=0;i<10;i++){send_data();sleep(1);}mq_close(s_mq);return0;}
recv.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineMQ_MSG_MAX_SIZE512///<最大消息長度#defineMQ_MSG_MAX_ITEM5///<最大消息數目staticmqd_ts_mq;typedefstruct_msg_data{charbuf[128];intcnt;}msg_data_t;intmain(void){intret=0;structmq_attrattr;charrecv_msg[MQ_MSG_MAX_SIZE]={0};msg_data_trecv_data={0};intprio=0;ssize_tlen=0;s_mq=mq_open("/mq",O_RDONLY);if(-1==s_mq){perror("mq_openerror");return-1;}while(1){if((len=mq_receive(s_mq,(char*)&recv_data,MQ_MSG_MAX_SIZE,&prio))==-1){perror("mq_receiveerror");return-1;}printf("recv_msg=%s,cnt=%d",recv_data.buf,recv_data.cnt);sleep(1);}mq_close(s_mq);mq_unlink("/mq");return0;}
編譯、運行:
gccsend.c-osend_process-lrtgccrecv.c-orecv_process-lrt
2、共享內存
消息隊列的讀取和寫入的過程,會有發生用戶態與內核態之間的消息拷貝過程。而共享內存的方式則沒有這個拷貝過程,進程間通信速度較快。
在物理內存上開辟一塊內存空間,多個進程可以將同一塊物理內存空間映射到自己的虛擬地址空間,通過自己的虛擬地址直接訪問這塊空間,通過這種方式實現數據共享。
POSIX共享內存頭文件:
#include#include#include
共享內存API接口:
/***@brief創建共享內存實例**Detailedfunctiondescription**@param[in]name:要打開或創建的共享內存文件名*@param[in]oflag:打開的文件操作屬性-O_CREAT:創建一個共享內存文件-O_EXCL:檢查共享內存是否存在,一般與O_CREAT一起使用-O_CREAT|O_EXCL:共享內存不存在則創建,已存在返回NULL-O_NONBLOCK:非阻塞模式打開,共享內存不存在返回NULL-O_RDONLY:只讀模式打開-O_WRONLY:只寫模式打開-O_RDWR:讀寫模式打開*@param[in]mode:文件共享模式,例如0777**@return成功返回共享內存描述符,失敗返回-1,錯誤碼存于error中*/intshm_open(constchar*name,intoflag,mode_tmode);/***@brief刪除共享內存**Detailedfunctiondescription**@param[in]name:創建的共享內存文件名**@return成功返回0,失敗返回-1*/intshm_unlink(constchar*name);/***@brief將打開的文件映射到內存**Detailedfunctiondescription**@param[in]addr:要將文件映射到的內存地址,一般應該傳遞NULL來由Linux內核指定*@param[in]length:要映射的文件數據長度*@param[in]prot:映射的內存區域的操作權限(保護屬性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE*@param[in]flags:標志位參數,包括:MAP_SHARED、MAP_PRIVATE與MAP_ANONYMOUS。*@param[in]fd:用來建立映射區的文件描述符,用shm_open打開或者open打開的文件*@param[in]offset:映射文件相對于文件頭的偏移位置,應該按4096字節對齊**@return成功返回0,失敗返回-1*/void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);/***@brief取消內存映射**Detailedfunctiondescription**@param[in]addr:由mmap成功返回的地址*@param[in]length:要取消的內存長度**@return成功返回0,失敗返回-1*/intmunmap(void*addr,size_tlength);/***@brief將參數fd指定的文件大小改為參數length指定的大小**Detailedfunctiondescription**@param[in]fd:已打開的文件描述符,以寫入模式打開的文件*@param[in]length:要設置的長度**@return成功返回0,失敗返回-1*/intftruncate(intfd,off_tlength);/***@brief獲取文件相關的信息,將獲取到的信息放入到statbuf結構體中**Detailedfunctiondescription**@param[in]fd:已打開的文件描述符*@param[out]statbuf:文件的信息**@return成功返回0,失敗返回-1*/intfstat(intfd,structstat*statbuf);
共享內存基本API接口使用例子:發送進程給接收進程發送測試數據。
send.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineSHM_NAME"/shm"intmain(void){intret=0;///<創建和讀端相同的文件標識intshm_fd=shm_open(SHM_NAME,O_RDWR|O_CREAT,0666);if(shm_fd==-1){printf("shm_openerror");}///<設置共享內存文件為8KBftruncate(shm_fd,8*1024);///<獲取共享內存文件相關屬性信息structstatfilestat={0};fstat(shm_fd,&filestat);printf("st_size=%ld",filestat.st_size);///<內存映射char*shm_ptr=(char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);///<向共享內存中寫入數據charbuf[]="helloworld";memmove(shm_ptr,buf,sizeof(buf));printf("pid%d,%s",getpid(),shm_ptr);///<寫入完成后解除映射munmap(shm_ptr,filestat.st_size);return0;}
recv.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineSHM_NAME"/shm"intmain(void){///<創建共享內存文件標識符intshm_fd=shm_open(SHM_NAME,O_RDWR|O_CREAT,0666);if(shm_fd==-1){printf("shm_openfailed");exit(EXIT_FAILURE);}///<設置共享內存文件為8KBftruncate(shm_fd,8192);///<獲取共享內存文件相關屬性信息structstatfilestat;fstat(shm_fd,&filestat);printf("st_size=%ld",filestat.st_size);///<映射共享內存,并獲取共享內存的地址char*shm_ptr=(char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);///<獲取共享內存地址中的內容并打印,最后再解除映射,刪除共享內存printf("pid=%d,%s",getpid(),shm_ptr);munmap(shm_ptr,filestat.st_size);shm_unlink(SHM_NAME);return0;}
編譯、運行:
gccsend.c-osend_process-lrtgccrecv.c-orecv_process-lrt
對具有多個處理核系統消息傳遞的性能要優于共享內存。共享內存會有高速緩存一致性問題,這是由共享數據在多個高速緩存之間遷移而引起的。隨著系統的處理核的數量的日益增加,可能導致消息傳遞作為 IPC 的首選機制。
3、socket
UNIX域套接字與傳統基于TCP/IP協議棧的socket不同,unix domain socket以文件系統作為地址空間,不需經過TCP/IP的頭部封裝、報文ack確認、路由選擇、數據校驗與重傳過程,因此傳輸速率上也不會受網卡帶寬的限制。
unix domain socket在進程間通信同樣是基于“客戶端—服務器”(C-S)模式。
UNIX域套接字基本API接口使用例子:基于UNIX域套接字客戶端進程向服務端進程發送測試數據。
server.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#include#include#include#defineSERVER_PATH"/tmp/server"intmain(void){///<創建UNIX域字節流套接字intserver_fd=socket(AF_LOCAL,SOCK_STREAM,0);if(server_fd<0){printf("socketerror");exit(EXIT_FAILURE);}///<綁定服務端地址unlink(SERVER_PATH);structsockaddr_unserver_addr;memset((char*)&server_addr,0,sizeof(server_addr));server_addr.sun_family=AF_LOCAL;strncpy(server_addr.sun_path,SERVER_PATH,sizeof(server_addr.sun_path)-1);if(bind(server_fd,(structsockaddr*)&server_addr,sizeof(server_addr))<0){printf("binderror");close(server_fd);exit(EXIT_FAILURE);}///<監聽if(listen(server_fd,10)<0){printf("listenerror");close(server_fd);exit(EXIT_FAILURE);}///<等待客戶端連接intaddr_len=sizeof(structsockaddr);structsockaddr_unclient_addr;intclient_fd=accept(server_fd,(structsockaddr*)&client_addr,(socklen_t*)&addr_len);if(client_fd<0){printf("accepterror");close(server_fd);unlink(SERVER_PATH);exit(1);}else{printf("connectedclient:%s",client_addr.sun_path);}while(1){charbuf[128]={0};intrecv_len=read(client_fd,buf,sizeof(buf));if(recv_len<=0){printf("recverror!");close(client_fd);exit(EXIT_FAILURE);}printf("recv:%s",buf);}unlink(SERVER_PATH);close(server_fd);close(client_fd);return0;}
client.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#include#include#include#defineSERVER_PATH"/tmp/server"#defineCLIENT_PATH"/tmp/client"intmain(void){///<創建UNIX域字節流套接字intclient_fd=socket(AF_UNIX,SOCK_STREAM,0);if(client_fd<0){printf("socketerror");exit(EXIT_FAILURE);}///<顯式綁定客戶端地址structsockaddr_unclient_addr;memset((char*)&client_addr,0,sizeof(client_addr));client_addr.sun_family=AF_UNIX;strncpy(client_addr.sun_path,CLIENT_PATH,sizeof(client_addr.sun_path)-1);unlink(CLIENT_PATH);if(bind(client_fd,(structsockaddr*)&client_addr,sizeof(client_addr))<0){printf("binderror");close(client_fd);exit(1);}///<連接服務端structsockaddr_unserver_addr;server_addr.sun_family=AF_UNIX;strncpy(server_addr.sun_path,SERVER_PATH,sizeof(server_addr.sun_path)-1);intret=connect(client_fd,(structsockaddr*)&server_addr,sizeof(server_addr));if(ret<0){printf("connecterror");close(client_fd);unlink(CLIENT_PATH);exit(1);}printf("connecttoserver:%s",server_addr.sun_path);while(1){charbuf[128]={0};if(scanf("%s",buf)){intsend_len=write(client_fd,buf,strlen(buf));if(send_len<=0){printf("writeerror!");close(client_fd);exit(EXIT_FAILURE);}else{printf("sendsuccess!send:%s,send_len:%d",buf,send_len);}}}unlink(SERVER_PATH);close(client_fd);return0;}
編譯、運行:
gccserver.c-oserver_processgccclient.c-oclient_process
類socket的其它進程間通信方式:
實用 | nanomsg通信庫的簡單使用分享
mqtt應用于進程間通信
4、管道
在內核中開辟一塊緩沖區;若多個進程拿到同一個管道(緩沖區)的操作句柄,就可以訪問同一個緩沖區,就可以進行通信。涉及到兩次用戶態與內核態之間的數據拷貝。
(1)匿名管道
內核中的緩沖區是沒有具體的標識符的,匿名管道只能用于具有親緣關系的進程間通信。
調用pipe接口可以創建一個匿名管道,并返回了兩個描述符,一個是管道的讀取端描述符 fd[0],另一個是管道的寫入端描述符 fd[1]。
管道是一個半雙工通信(可以選擇方向的單向傳輸)
匿名管道基本API接口使用例子:父進程通過管道發送測試數據給子進程。
#include#include#include#includeintmain(){///<創建管道intpipefd[2]={-1};intret=pipe(pipefd);if(ret<0){printf("pipeerror");exit(EXIT_FAILURE);}intread_fd=pipefd[0];///0){///<父進程向管道寫入數據char*ptr="hello88888888";write(write_fd,ptr,strlen(ptr));}return0;}
編譯、運行:
如果需要雙向通信,則應該創建兩個管道。
(2)命名管道
命名管道也是內核中的一塊緩沖區,并且這個緩沖區具有標識符;這個標識符是一個可見于文件系統的管道文件,能夠被其他進程找到并打開管道文件,則可以獲取管道的操作句柄,所以該命名管道可用于同一主機上的任意進程間通信。
創建命名管道的接口:
intmkfifo(constchar*pathname,mode_tmode);
命名管道基本API接口使用例子:一個進程往管道中寫入測試數據,另一個進程從管道中讀取數據。
fifo_wr.c:
#include#include#include#include#include#include#include#defineFIFO_PATH"./fifo_file"typedefstruct_msg_data{charbuf[128];intcnt;}msg_data_t;voidsend_data(intfd){staticintcnt=0;msg_data_tsend_data={0};cnt++;strcpy(send_data.buf,"hello");send_data.cnt=cnt;write(fd,&send_data,sizeof(send_data));printf("sendmsg=%s,cnt=%d",send_data.buf,send_data.cnt);}intmain(void){///<創建管道文件intret=mkfifo(FIFO_PATH,0664);if(ret<0&&errno!=EEXIST){printf("mkfifoerror");exit(EXIT_FAILURE);}///<以只寫的方式打開管道文件intfd=open(FIFO_PATH,O_WRONLY);if(fd<0){printf("openfifoerror");exit(EXIT_FAILURE);}printf("openfifosuccess");///<寫10次for(size_ti=0;i<10;i++){send_data(fd);sleep(1);}close(fd);return0;}
fifo_rd.c:
#include#include#include#include#include#include#include#defineFIFO_PATH"./fifo_file"typedefstruct_msg_data{charbuf[128];intcnt;}msg_data_t;intmain(void){umask(0);///<創建管道文件intret=mkfifo(FIFO_PATH,0664);if(ret<0&&errno!=EEXIST){printf("mkfifoerror");exit(EXIT_FAILURE);}///<以只讀方式獲取管道文件的操作句柄intfd=open(FIFO_PATH,O_RDONLY);if(fd<0){printf("openerror");exit(EXIT_FAILURE);}printf("openfifosuccess");while(1){msg_data_tread_data={0};///<將從管道讀取的文件寫到buf中intret=read(fd,&read_data,sizeof(read_data));if(ret<0){printf("readerror");exit(EXIT_FAILURE);}elseif(ret==0){printf("allwriteclosedd");exit(EXIT_FAILURE);}printf("read_data=%s,cnt=%d",read_data.buf,read_data.cnt);sleep(1);}close(fd);return0;}
編譯、運行:
gccfifo_wr.c-ofifo_wrgccfifo_rd.c-ofifo_rd
5、信號量
信號量(Seamphore)是進程和線程間同步的一種機制。
信號量本質是一個非負的整型變量。增加一個可用資源執行加一,也稱為V操作;獲取一個資源資源后執行減一,也稱為P操作。
信號量根據信號值不同可分為兩類:
二值信號量,信號量值只有0和1,初始值為1,1表示資源可用,0表示資源不可用;二值信號量與互斥鎖類似。
計數信號量, 信號量的值在0到一個大于1的限制值之間,信號值表示可用的資源的數目。
信號量根據作用對象不同可分為兩類:
有名信號量,信號值保存在文件中,用于進程間同步
無名信號量,又稱為基于內存信號量,信號值保存在內存中,用于線程間同步
POSIX信號量頭文件:
#include
編譯鏈接需要加-lpthread參數。
信號量API接口:
/***@brief創建信號量**Detailedfunctiondescription**@param[in]name:信號量名稱*@param[in]mode:訪問權限*@param[in]value:信號量初始值**@return成功時返回指向信號量的指針,出錯時為SEM_FAILED*/sem_t*sem_open(constchar*name,intoflag,mode_tmode,unsignedintvalue);/***@brief初始化信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址*@param[in]pshared:信號量作用域,分為進程內作用域PTHREAD_PROCESS_PRIVATE和跨進程作用域PTHREAD_PROCESS_SHARED*@param[in]value:信號量初始值**@return成功返回0,失敗返回-1*/intsem_init(sem_t*sem,intpshared,unsignedintvalue);/***@brief獲取信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址*@param[out]sval:保存返回信號值地址**@return成功返回0,失敗返回-1*/intsem_getvalue(sem_t*sem,int*sval);/***@brief阻塞方式等待信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址**@return成功返回0,失敗返回-1*/intsem_wait(sem_t*sem);/***@brief指定超時時間阻塞方式等待信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址*@param[in]sem:超時時間,單位為時鐘節拍**@return成功返回0,失敗返回-1*/intsem_timedwait(sem_t*sem,conststructtimespec*abs_timeout);/***@brief非阻塞方式等待信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址**@return成功返回0,失敗返回-1*/intsem_trywait(sem_t*sem);/***@brief產生信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址**@return成功返回0,失敗返回-1*/intsem_post(sem_t*sem);/***@brief銷毀信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址**@return成功返回0,失敗返回-1*/intsem_destroy(sem_t*sem);/***@brief關閉信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址**@return成功返回0,失敗返回-1*/intsem_close(sem_t*sem);/***@brief分離信號量**Detailedfunctiondescription**@param[in]name:信號量名稱**@return成功返回0,失敗返回-1*/intsem_unlink(constchar*name);
信號量基本API接口使用例子:父子進程間通信
#include#include#include#include#include#defineSEM_NAME"sem"intmain(void){intsem_val=0;///<創建信號量sem_t*sem=sem_open(SEM_NAME,O_CREAT,0666,1);if(NULL==sem){printf("sem_openerror");exit(EXIT_FAILURE);}///<創建子進程pid_tpid=fork();if(pid==-1){printf("forkerror");sem_close(sem);sem_unlink(SEM_NAME);exit(EXIT_FAILURE);}elseif(pid==0){///<子進程進行5次P操作for(size_ti=0;i<5;i++){sem_wait(sem);if(sem_getvalue(sem,&sem_val)!=-1){printf("childprocessPoperation,sem_val=%d",sem_val);sleep(1);}}_exit(1);}elseif(pid>0){///<父進程執行5次V操作for(size_ti=0;i<5;i++){sem_post(sem);if(sem_getvalue(sem,&sem_val)!=-1){printf("prarentprocessVoperation,sem_val=%d",sem_val);sleep(2);}}}///<刪除sem信號量sem_close(sem);if(sem_unlink(SEM_NAME)!=-1){printf("sem_unlinksuccess");}return0;}
編譯、運行:
IPC總結
操作系統根據不同的場景提供了不同的方式,消息隊列、共享內存、UNIX域套接字、管道、信號量。
消息隊列:內核中的一個優先級隊列,多個進程通過訪問同一個隊列,在隊列當中添加或者獲取節點來實現進程間通信。
共享內存:本質是一塊物理內存,多個進程將同一塊物理內存映射到自己的虛擬地址空間中,再通過頁表映射到物理地址達到進程間通信,它是最快的進程間通信方式,相較其他通信方式少了兩步數據拷貝操作。
UNIX域套接字:與TCP/IP套接字使用方式相同,但UNIX域套接字以文件系統作為地址空間,不需經過TCP/IP的頭部封裝、報文ack確認、路由選擇、數據校驗與重傳過程,因此傳輸速率上也不會受網卡帶寬的限制。
管道:內核中的一塊緩沖區,分為匿名管道和命名管道。匿名管道只能用于具有親緣關系的進程間;而命名管道可用于同一主機上任意進程間通信。
信號量:本質是內核中的一個計數器,主要實現進程間的同步與互斥,對資源進行計數,有兩種操作,分別是在訪問資源之前進行的p操作,還有產生資源之后的v操作。
審核編輯:湯梓紅
標簽: