什么是進程
(相關(guān)資料圖)
1、進程和線程的區(qū)別
進程是指正在運行的程序,它擁有獨立的內(nèi)存空間和系統(tǒng)資源,不同進程之間的數(shù)據(jù)不共享。進程是資源分配的基本單位。
線程是進程內(nèi)的執(zhí)行單元,它與同一進程內(nèi)的其他線程共享進程的內(nèi)存空間和系統(tǒng)資源。線程是調(diào)度的基本單位。
2、進程的創(chuàng)建和銷毀
在Linux中啟動一個進程有多種方法:
(1)通過system函數(shù)啟動進程。(使用簡單,效率較低)
#include/***@brief執(zhí)行系統(tǒng)命令調(diào)用命令處理器來執(zhí)行命令**Detailedfunctiondescription**@param[in]command:包含被請求變量名稱的C字符串**@return如果發(fā)生錯誤,則返回值為-1,否則返回命令的狀態(tài)。*/intsystem(constchar*command);
例子:通過system函數(shù)啟動一個進程,列出當(dāng)前目錄下的文件及文件夾。
#include#includeintmain(void){system("ls");printf("lsend");return0;}
(2)通過fork函數(shù)啟動進程。(用于啟動子進程)
#include#include/***@brieffork系統(tǒng)調(diào)用用于創(chuàng)建一個子進程**Detailedfunctiondescription**@param[in]**@return如果發(fā)生錯誤,則返回值為-1,否則返回命令的狀態(tài)。*/pid_tfork(void);
例子:通過fork函數(shù)啟動子進程
#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);///<父進程阻塞等待信號到來或子進程結(jié)束printf("Childprocess(pid=%d)hasbeenterminated,child_status=%d",child_pid,child_status);}///<異常退出else{printf("Forkfailed.");exit(EXIT_FAILURE);}return0;}
編譯、運行:
我們使用了fork()系統(tǒng)調(diào)用來創(chuàng)建一個新進程。如果fork()返回值為0,則說明當(dāng)前進程是子進程;如果返回值大于0,則說明當(dāng)前進程是父進程。在父進程中,我們使用wait()系統(tǒng)調(diào)用來等待子進程結(jié)束。當(dāng)子進程結(jié)束后,父進程會繼續(xù)執(zhí)行。
(3)通過exec系列函數(shù)啟動進程。(用于啟動新進程,新進程會覆蓋舊進程)
#include/***@brief啟動新進程,新進程會覆蓋舊進程**Detailedfunctiondescription**@param[in]path:所執(zhí)行文件的路徑*@param[in]file:所執(zhí)行文件的名稱*@param[in]arg:傳入的參數(shù)列表,以NULL作為結(jié)束*@param[in]envp:傳入的環(huán)境變量**@return如果發(fā)生錯誤,則返回值為-1,否則返回命令的狀態(tài)。*/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()函數(shù)的參數(shù)列表調(diào)用了ls命令程序
#include#includeintmain(void){execl("/bin/ls","ls","-la",NULL);printf("lsend");return0;}
execl()函數(shù)的參數(shù)列表調(diào)用了ls命令程序,與在終端上運行”ls -la”產(chǎn)生的結(jié)果是一樣的。
在Linux中終止一個進程有多種方法:
從main函數(shù)返回。(正常終止)
調(diào)用exit()函數(shù)終止。(正常終止)
調(diào)用_exit()函數(shù)終止。(正常終止)
調(diào)用abort()函數(shù)終止。(異常終止)
由系統(tǒng)信號終止。(異常終止)
進程間通信方式
進程間通信是指在不同進程之間傳播或交換信息的一種機制。每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進程A把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進程B再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機制稱為進程間通信。
進程間通信的目的:
傳輸數(shù)據(jù)。比如進程 A 負(fù)責(zé)生成數(shù)據(jù),進程 B 負(fù)責(zé)處理數(shù)據(jù),數(shù)據(jù)需要從 A 進程傳輸至 B 進程。
共享資源。比如進程 A 與進程 B 共享某一塊內(nèi)存資源。
模塊化。將系統(tǒng)功能劃分為多個進程模塊進行開發(fā),方便開發(fā)維護。
加速計算。多核處理器環(huán)境,一個特定進程劃分為幾個進程并行運行。
Linux IPC(Inter-process Comminication, 進程間通信)的方式:
1、消息隊列
內(nèi)核中的一個優(yōu)先級隊列,多個進程通過訪問同一個隊列,進行添加結(jié)點或者獲取結(jié)點實現(xiàn)通信。
POSIX消息隊列頭文件:
#include/*ForO_*constants*/#include/*Formodeconstants*/#include
編譯鏈接需要加上 -lrt 鏈接。
消息隊列API接口:
/***@brief創(chuàng)建消息隊列實例**Detailedfunctiondescription**@param[in]name:消息隊列名稱*@param[in]oflag:根據(jù)傳入標(biāo)識來創(chuàng)建或者打開一個已創(chuàng)建的消息隊列-O_CREAT:創(chuàng)建一個消息隊列-O_EXCL:檢查消息隊列是否存在,一般與O_CREAT一起使用-O_CREAT|O_EXCL:消息隊列不存在則創(chuàng)建,已存在返回NULL-O_NONBLOCK:非阻塞模式打開,消息隊列不存在返回NULL-O_RDONLY:只讀模式打開-O_WRONLY:只寫模式打開-O_RDWR:讀寫模式打開*@param[in]mode:訪問權(quán)限*@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:消息體緩沖區(qū)地址*@param[in]msg_len:消息體長度,長度必須大于等于消息屬性設(shè)定的最大值*@param[in]msg_prio:消息優(yōu)先級**@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:消息體緩沖區(qū)地址*@param[in]msg_len:消息體長度,長度必須大于等于消息屬性設(shè)定的最大值*@param[in]msg_prio:消息優(yōu)先級*@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無限阻塞方式發(fā)送消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:待發(fā)送消息體緩沖區(qū)地址*@param[in]msg_len:消息體長度*@param[in]msg_prio:消息優(yōu)先級**@return成功返回0,失敗返回-1*/mqd_tmq_send(mqd_tmqdes,constchar*msg_ptr,size_tmsg_len,unsignedmsg_prio);/***@brief指定超時時間阻塞方式發(fā)送消息**Detailedfunctiondescription**@param[in]mqdes:消息隊列描述符*@param[in]msg_ptr:待發(fā)送消息體緩沖區(qū)地址*@param[in]msg_len:消息體長度*@param[in]msg_prio:消息優(yōu)先級*@param[in]abs_timeout:超時時間**@return成功返回0,失敗返回-1*/mqd_tmq_timedsend(mqd_tmqdes,constchar*msg_ptr,size_tmsg_len,unsignedmsg_prio,conststructtimespec*abs_timeout);/***@brief關(guān)閉消息隊列**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接口使用例子:發(fā)送進程給接收進程發(fā)送測試數(shù)據(jù)。
send.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineMQ_MSG_MAX_SIZE512///<最大消息長度#defineMQ_MSG_MAX_ITEM5///<最大消息數(shù)目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;///<創(chuàng)建消息隊列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///<最大消息數(shù)目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、共享內(nèi)存
消息隊列的讀取和寫入的過程,會有發(fā)生用戶態(tài)與內(nèi)核態(tài)之間的消息拷貝過程。而共享內(nèi)存的方式則沒有這個拷貝過程,進程間通信速度較快。
在物理內(nèi)存上開辟一塊內(nèi)存空間,多個進程可以將同一塊物理內(nèi)存空間映射到自己的虛擬地址空間,通過自己的虛擬地址直接訪問這塊空間,通過這種方式實現(xiàn)數(shù)據(jù)共享。
POSIX共享內(nèi)存頭文件:
#include#include#include
共享內(nèi)存API接口:
/***@brief創(chuàng)建共享內(nèi)存實例**Detailedfunctiondescription**@param[in]name:要打開或創(chuàng)建的共享內(nèi)存文件名*@param[in]oflag:打開的文件操作屬性-O_CREAT:創(chuàng)建一個共享內(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,錯誤碼存于error中*/intshm_open(constchar*name,intoflag,mode_tmode);/***@brief刪除共享內(nèi)存**Detailedfunctiondescription**@param[in]name:創(chuàng)建的共享內(nèi)存文件名**@return成功返回0,失敗返回-1*/intshm_unlink(constchar*name);/***@brief將打開的文件映射到內(nèi)存**Detailedfunctiondescription**@param[in]addr:要將文件映射到的內(nèi)存地址,一般應(yīng)該傳遞NULL來由Linux內(nèi)核指定*@param[in]length:要映射的文件數(shù)據(jù)長度*@param[in]prot:映射的內(nèi)存區(qū)域的操作權(quán)限(保護屬性),包括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_tlength,intprot,intflags,intfd,off_toffset);/***@brief取消內(nèi)存映射**Detailedfunctiondescription**@param[in]addr:由mmap成功返回的地址*@param[in]length:要取消的內(nèi)存長度**@return成功返回0,失敗返回-1*/intmunmap(void*addr,size_tlength);/***@brief將參數(shù)fd指定的文件大小改為參數(shù)length指定的大小**Detailedfunctiondescription**@param[in]fd:已打開的文件描述符,以寫入模式打開的文件*@param[in]length:要設(shè)置的長度**@return成功返回0,失敗返回-1*/intftruncate(intfd,off_tlength);/***@brief獲取文件相關(guān)的信息,將獲取到的信息放入到statbuf結(jié)構(gòu)體中**Detailedfunctiondescription**@param[in]fd:已打開的文件描述符*@param[out]statbuf:文件的信息**@return成功返回0,失敗返回-1*/intfstat(intfd,structstat*statbuf);
共享內(nèi)存基本API接口使用例子:發(fā)送進程給接收進程發(fā)送測試數(shù)據(jù)。
send.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#defineSHM_NAME"/shm"intmain(void){intret=0;///<創(chuàng)建和讀端相同的文件標(biāo)識intshm_fd=shm_open(SHM_NAME,O_RDWR|O_CREAT,0666);if(shm_fd==-1){printf("shm_openerror");}///<設(shè)置共享內(nèi)存文件為8KBftruncate(shm_fd,8*1024);///<獲取共享內(nèi)存文件相關(guān)屬性信息structstatfilestat={0};fstat(shm_fd,&filestat);printf("st_size=%ld",filestat.st_size);///<內(nèi)存映射char*shm_ptr=(char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);///<向共享內(nèi)存中寫入數(shù)據(jù)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){///<創(chuàng)建共享內(nèi)存文件標(biāo)識符intshm_fd=shm_open(SHM_NAME,O_RDWR|O_CREAT,0666);if(shm_fd==-1){printf("shm_openfailed");exit(EXIT_FAILURE);}///<設(shè)置共享內(nèi)存文件為8KBftruncate(shm_fd,8192);///<獲取共享內(nèi)存文件相關(guān)屬性信息structstatfilestat;fstat(shm_fd,&filestat);printf("st_size=%ld",filestat.st_size);///<映射共享內(nèi)存,并獲取共享內(nèi)存的地址char*shm_ptr=(char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);///<獲取共享內(nèi)存地址中的內(nèi)容并打印,最后再解除映射,刪除共享內(nèi)存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
對具有多個處理核系統(tǒng)消息傳遞的性能要優(yōu)于共享內(nèi)存。共享內(nèi)存會有高速緩存一致性問題,這是由共享數(shù)據(jù)在多個高速緩存之間遷移而引起的。隨著系統(tǒng)的處理核的數(shù)量的日益增加,可能導(dǎo)致消息傳遞作為 IPC 的首選機制。
3、socket
UNIX域套接字與傳統(tǒng)基于TCP/IP協(xié)議棧的socket不同,unix domain socket以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗與重傳過程,因此傳輸速率上也不會受網(wǎng)卡帶寬的限制。
unix domain socket在進程間通信同樣是基于“客戶端—服務(wù)器”(C-S)模式。
UNIX域套接字基本API接口使用例子:基于UNIX域套接字客戶端進程向服務(wù)端進程發(fā)送測試數(shù)據(jù)。
server.c:
#include#include#include#include#include/*ForO_*constants*/#include/*Formodeconstants*/#include#include#include#include#defineSERVER_PATH"/tmp/server"intmain(void){///<創(chuàng)建UNIX域字節(jié)流套接字intserver_fd=socket(AF_LOCAL,SOCK_STREAM,0);if(server_fd<0){printf("socketerror");exit(EXIT_FAILURE);}///<綁定服務(wù)端地址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);}///<監(jiān)聽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){///<創(chuàng)建UNIX域字節(jié)流套接字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);}///<連接服務(wù)端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應(yīng)用于進程間通信
4、管道
在內(nèi)核中開辟一塊緩沖區(qū);若多個進程拿到同一個管道(緩沖區(qū))的操作句柄,就可以訪問同一個緩沖區(qū),就可以進行通信。涉及到兩次用戶態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝。
(1)匿名管道
內(nèi)核中的緩沖區(qū)是沒有具體的標(biāo)識符的,匿名管道只能用于具有親緣關(guān)系的進程間通信。
調(diào)用pipe接口可以創(chuàng)建一個匿名管道,并返回了兩個描述符,一個是管道的讀取端描述符 fd[0],另一個是管道的寫入端描述符 fd[1]。
管道是一個半雙工通信(可以選擇方向的單向傳輸)
匿名管道基本API接口使用例子:父進程通過管道發(fā)送測試數(shù)據(jù)給子進程。
#include#include#include#includeintmain(){///<創(chuàng)建管道intpipefd[2]={-1};intret=pipe(pipefd);if(ret<0){printf("pipeerror");exit(EXIT_FAILURE);}intread_fd=pipefd[0];///0){///<父進程向管道寫入數(shù)據(jù)char*ptr="hello88888888";write(write_fd,ptr,strlen(ptr));}return0;}
編譯、運行:
如果需要雙向通信,則應(yīng)該創(chuàng)建兩個管道。
(2)命名管道
命名管道也是內(nèi)核中的一塊緩沖區(qū),并且這個緩沖區(qū)具有標(biāo)識符;這個標(biāo)識符是一個可見于文件系統(tǒng)的管道文件,能夠被其他進程找到并打開管道文件,則可以獲取管道的操作句柄,所以該命名管道可用于同一主機上的任意進程間通信。
創(chuàng)建命名管道的接口:
intmkfifo(constchar*pathname,mode_tmode);
命名管道基本API接口使用例子:一個進程往管道中寫入測試數(shù)據(jù),另一個進程從管道中讀取數(shù)據(jù)。
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){///<創(chuàng)建管道文件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);///<創(chuàng)建管道文件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)是進程和線程間同步的一種機制。
信號量本質(zhì)是一個非負(fù)的整型變量。增加一個可用資源執(zhí)行加一,也稱為V操作;獲取一個資源資源后執(zhí)行減一,也稱為P操作。
信號量根據(jù)信號值不同可分為兩類:
二值信號量,信號量值只有0和1,初始值為1,1表示資源可用,0表示資源不可用;二值信號量與互斥鎖類似。
計數(shù)信號量, 信號量的值在0到一個大于1的限制值之間,信號值表示可用的資源的數(shù)目。
信號量根據(jù)作用對象不同可分為兩類:
有名信號量,信號值保存在文件中,用于進程間同步
無名信號量,又稱為基于內(nèi)存信號量,信號值保存在內(nèi)存中,用于線程間同步
POSIX信號量頭文件:
#include
編譯鏈接需要加-lpthread參數(shù)。
信號量API接口:
/***@brief創(chuàng)建信號量**Detailedfunctiondescription**@param[in]name:信號量名稱*@param[in]mode:訪問權(quán)限*@param[in]value:信號量初始值**@return成功時返回指向信號量的指針,出錯時為SEM_FAILED*/sem_t*sem_open(constchar*name,intoflag,mode_tmode,unsignedintvalue);/***@brief初始化信號量**Detailedfunctiondescription**@param[in]sem:信號量實例地址*@param[in]pshared:信號量作用域,分為進程內(nèi)作用域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:超時時間,單位為時鐘節(jié)拍**@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產(chǎn)生信號量**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關(guān)閉信號量**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;///<創(chuàng)建信號量sem_t*sem=sem_open(SEM_NAME,O_CREAT,0666,1);if(NULL==sem){printf("sem_openerror");exit(EXIT_FAILURE);}///<創(chuàng)建子進程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){///<父進程執(zhí)行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總結(jié)
操作系統(tǒng)根據(jù)不同的場景提供了不同的方式,消息隊列、共享內(nèi)存、UNIX域套接字、管道、信號量。
消息隊列:內(nèi)核中的一個優(yōu)先級隊列,多個進程通過訪問同一個隊列,在隊列當(dāng)中添加或者獲取節(jié)點來實現(xiàn)進程間通信。
共享內(nèi)存:本質(zhì)是一塊物理內(nèi)存,多個進程將同一塊物理內(nèi)存映射到自己的虛擬地址空間中,再通過頁表映射到物理地址達到進程間通信,它是最快的進程間通信方式,相較其他通信方式少了兩步數(shù)據(jù)拷貝操作。
UNIX域套接字:與TCP/IP套接字使用方式相同,但UNIX域套接字以文件系統(tǒng)作為地址空間,不需經(jīng)過TCP/IP的頭部封裝、報文ack確認(rèn)、路由選擇、數(shù)據(jù)校驗與重傳過程,因此傳輸速率上也不會受網(wǎng)卡帶寬的限制。
管道:內(nèi)核中的一塊緩沖區(qū),分為匿名管道和命名管道。匿名管道只能用于具有親緣關(guān)系的進程間;而命名管道可用于同一主機上任意進程間通信。
信號量:本質(zhì)是內(nèi)核中的一個計數(shù)器,主要實現(xiàn)進程間的同步與互斥,對資源進行計數(shù),有兩種操作,分別是在訪問資源之前進行的p操作,還有產(chǎn)生資源之后的v操作。
審核編輯:湯梓紅
標(biāo)簽: