【linux草鞋应用编程系列】_3_ 进程间通信
生活随笔
收集整理的這篇文章主要介紹了
【linux草鞋应用编程系列】_3_ 进程间通信
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、進(jìn)程間通信
? ? ? ?linux下面提供了多種進(jìn)程間通信的方法, 管道、信號、信號量、消息隊列、共享內(nèi)存、套接字等。下面我們分別 介紹管道、信號量、消息隊列、共享內(nèi)存。 ? ? ? ?信號和套接字在后續(xù)介紹。 ? 1、管道 ? ? 管道又分為無名管道、命名管道。 ? 無名管道用于父子進(jìn)程間通信, 而命名管道則可以用于同一計算機上運行的 兩個進(jìn)程間的通信。管道可以用類似“水管”的原理來理解。 ? 1)無名管道 ? ? 要在父子進(jìn)程進(jìn)程間使用管道進(jìn)行通信,那么先需要創(chuàng)建管道, 在linux中使用 pipe()創(chuàng)建管道。其原型如下: PIPE(2) Linux Programmer’s Manual PIPE(2) NAMEpipe - create pipe SYNOPSIS#include <unistd.h>int pipe(int filedes[2]); //參數(shù)為一個長度為2 的整型數(shù)組的數(shù)組首地址, 為輸出參數(shù), ? ? 返回值: ? ? ? ? ? ? ? ?成功創(chuàng)建管道返回0; 失敗則返回-1;? ? ?? ? ? 管道分為兩端,一端用來寫,另一端則用來讀(可以想象為水管,位置高的一端進(jìn)水,位置低的一端將水流出去) 。 pipe()函數(shù)的輸出參數(shù) filedes[0] 用于讀取數(shù)據(jù), filedes[1] 用于寫入數(shù)據(jù)。 ? ??管道的操作和普通文件的操作一樣, 但是要注意,讀的時候要將寫端關(guān)閉,寫的時候要將讀端關(guān)閉。?? ? Exp: ?pipe.c ? 首先測試從父進(jìn)程給子進(jìn)程寫數(shù)據(jù)。 #include <stdio.h> #include <unistd.h> #include <stdlib.h>int main(int argc,char* argv[]) {int fd_pipe[2];pid_t pid;char buf[32];//創(chuàng)建管道if( pipe(fd_pipe) ){perror("create pipe");exit(1);}pid=fork();if( 0==pid ){/*close(fd_pipe[1]); //關(guān)閉寫端*/read(fd_pipe[0], buf,sizeof(buf));printf("in child process read data from pipe.\n");printf("the data read from pipe is:%s\n",buf);exit(0);}/*close(fd_pipe[0]); //關(guān)閉讀端*/write(fd_pipe[1], "pipe test",sizeof("pipe test"));sleep(1);return 0; } ? ? 程序執(zhí)行情況如下: [root@localhost ipc]# gcc main.c [root@localhost ipc]# ./a.out in child process read data from pipe. the data read from pipe is:pipe test ? ? 利用系統(tǒng)調(diào)用 read()、write()進(jìn)行操作的時候管道默認(rèn)是阻塞的,如果管道沒有數(shù)據(jù)可讀,那么read( )函數(shù)就 阻塞,直到有數(shù)據(jù)讀才返回。 ? ?Exp: ?pipe.c ?子進(jìn)程寫入數(shù)據(jù)到管道,父進(jìn)程從管道讀取數(shù)據(jù) #include <stdio.h> #include <unistd.h> #include <stdlib.h>#define CHAR "pipe test from child to parent\n"int main(int argc,char* argv[]) {int fd_pipe[2];pid_t pid;char buf[32];//創(chuàng)建管道if( pipe(fd_pipe) ){perror("create pipe");exit(1);}pid=fork();if( 0==pid ){close(fd_pipe[0]);write(fd_pipe[1],CHAR,sizeof(CHAR));exit(0);} close(fd_pipe[1]);read(fd_pipe[0],buf,sizeof(buf));printf("data from child is: %s",buf);return 0; } ? ? 執(zhí)行結(jié)果如下: [root@localhost ipc]# gcc main.c [root@localhost ipc]# ./a.out data from child is: pipe test from child to parent 2)命名管道 ? ? 命名管道用于系統(tǒng)中兩個進(jìn)程之間通信;命名管道可用于系統(tǒng)中兩個沒有親緣關(guān)系的進(jìn)程進(jìn)行通信(也可以用于父 子進(jìn)程間的通信) 。 ? ? 要使用命名管道,則需要創(chuàng)建命名管道,用函數(shù) mkfifo () 創(chuàng)建命名管道。其原型如下: MKFIFO(3) Linux Programmer’s Manual MKFIFO(3) NAMEmkfifo - make a FIFO special file (a named pipe) SYNOPSIS#include <sys/types.h>#include <sys/stat.h>int mkfifo( const char *pathname, //生成的管道特殊文件的位置和文件名mode_t mode); //管道特殊文件的訪問權(quán)限 返回值:???? ? ? ? ?成功創(chuàng)建管道文件返回0, 失敗返回 -1. ? ?? ? ??創(chuàng)建號管道特殊文件后,就可以和訪問普通文件一樣訪問管道特殊文件。 ? Exp: ?測試命名管道 ? ?pipe-w.c ?創(chuàng)建命令管道并向管道寫入數(shù)據(jù) #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h>#define CHAR "pipe named\n"int main(int argc,char* argv[]) {int fd;int ret;//創(chuàng)建管道ret=mkfifo("./fifo-pipe",0666);if(ret){perror("mkfifo: fifo-pipe");exit(0);}fd=open("./fifo-pipe",O_WRONLY);write(fd,CHAR,sizeof(CHAR));close(fd);return 0; } ? ? pipe-r.c 打開pipe-w.c 文件,并且從命名管道讀取數(shù)據(jù): #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h>int main(int argc,char* argv[]) {int fd;int ret;char buf[32];//打開命名管道文件fd=open("./fifo-pipe",O_RDONLY);if(-1 == fd){perror("open fifo-pipe");exit(1);}ret=read(fd,buf,sizeof(buf));if(ret<0){perror("read fifo-pipe");exit(1);}printf("the data read from fifo pipe:%s\n",buf);close(fd);return 0; } 程序的執(zhí)行結(jié)果如下: ? ??pipe-w.c 生成wp , ?pipe-r.c 生成rp [root@localhost pipe]# ll //查看沒有 fifo-pipe 的命名管道文件 總計 28 -rw-r--r-- 1 root root 560 12-11 16:57 pipe_fork.c -rw-r--r-- 1 root root 500 12-11 17:15 pipe-r.c -rw-r--r-- 1 root root 405 12-11 17:17 pipe-w.c -rwxr-xr-x 1 root root 5359 12-11 17:15 rp -rwxr-xr-x 1 root root 5296 12-11 17:17 wp [root@localhost pipe]# ./wp & //wp運行,并且進(jìn)入后臺 [1] 29409 [root@localhost pipe]# jobs [1]+ Running ./wp & //wp在后臺運行, 等待命名管道的數(shù)據(jù)被讀取, 即wp 阻塞 [root@localhost pipe]# ./rp //rp 讀取管道數(shù)據(jù), the data read from fifo pipe:pipe named //數(shù)據(jù)讀取成功 [1]+ Done ./wp //管道中的數(shù)據(jù)被讀取完后,wp不再阻塞,返回 [root@localhost pipe]# jobs [root@localhost pipe]# ll 總計 28 prw-r--r-- 1 root root 0 12-11 17:23 fifo-pipe //生成一個命名管道文件 -rw-r--r-- 1 root root 560 12-11 16:57 pipe_fork.c -rw-r--r-- 1 root root 500 12-11 17:15 pipe-r.c -rw-r--r-- 1 root root 405 12-11 17:17 pipe-w.c -rwxr-xr-x 1 root root 5359 12-11 17:15 rp -rwxr-xr-x 1 root root 5296 12-11 17:17 wp [root@localhost pipe]# ? ? 要點: ? ? ? ? ? ? 在進(jìn)程操作管道的時候, write 和 read 都是阻塞的; 如果寫的數(shù)據(jù)沒有被讀取走,那么就會寫的進(jìn)程就會 在 write 函數(shù)阻塞; ?如果讀數(shù)據(jù)的時候,管道沒有數(shù)據(jù),那么就會等待管道里面別寫入數(shù)據(jù),進(jìn)程在read 函數(shù)阻塞。 ? ? 2、消息隊列 ? ? 消息隊列也是linux下進(jìn)程間通信的一種方式, 如果要使用消息隊列在進(jìn)程間進(jìn)行通信,必須創(chuàng)建一個消息隊列 或者打開一個已經(jīng)存在的消息隊列。 ? ? 要打開一個已經(jīng)存在的消息隊列,或者創(chuàng)建一個新的消息隊列,則必須先獲取一個關(guān)于消息隊列的IPC鍵值;通過函 數(shù) ftok( )獲取消息隊列的IPC鍵值。 ? ? ftok 的原型如下: FTOK(3) Linux Programmer’s Manual FTOK(3) NAMEftok - convert a pathname and a project identifier to a System V IPC key//由一個特定的工程號和文件生成一個特定的IPC鍵值, SYNOPSIS# include <sys/types.h># include <sys/ipc.h>key_t ftok(const char *pathname, //文件名int proj_id); //工程號 返回值: ? ? ? ? ? ? 成功返回 IPC鍵值, 失敗返回-1. ? ? 要點: ? ? ? ? ? ? 如果文件名和工程號一致,內(nèi)核保證在任何進(jìn)程中都將得到同樣的 IPC 鍵值。 ? ? ? 有了消息隊列的IPC鍵值后,就是創(chuàng)建或者打開消息隊列, 通過 msgget( ) 創(chuàng)建或打開一個消息隊列, 其原型如下: MSGGET(2) Linux Programmer’s Manual MSGGET(2) NAMEmsgget - get a message queue identifierSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget( key_t key, //IPC 鍵值int msgflg); //打開或者創(chuàng)建標(biāo)志, 可以取值 IPC_CREAT ? ? 返回值: ? ? ? ? ? ? 如果成功,返回消息隊列的ID號, 失敗返回 -1。 ? ? ? 創(chuàng)建了消息隊列或者打開已經(jīng)消息隊列后,就需要完消息隊列中添加消息,即發(fā)送消息; 發(fā)送消息通過函數(shù) msgsnd( ) 實現(xiàn)。 ? ? 當(dāng)發(fā)送完消息后,就可以從消息隊列中獲取消息,從消息隊列中讀取消息用函數(shù) msgrcv( )實現(xiàn)。 ????原型如下: MSGOP(2) Linux Programmer’s Manual MSGOP(2) NAMEmsgop - message operations SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgsnd(int msqid, //消息隊列IDconst void *msgp, //要發(fā)送的消息的消息結(jié)構(gòu)體size_t msgsz, //消息字符串的大小,或者消息結(jié)構(gòu)的大小int msgflg); // 消息標(biāo)志 ssize_t msgrcv(int msqid, //消息隊列IDvoid *msgp, //接受消息的消息結(jié)構(gòu)體指針size_t msgsz, //消息結(jié)構(gòu)體的大小long msgtyp, //指定要接收到消息的類型int msgflg); //消息標(biāo)志 要發(fā)送或接收消息,還需要定義一個如下格式的結(jié)構(gòu)體:struct msgbuf {long mtype; /* message type, must be > 0 */ //消息類型, 這個值必須大于 0char mtext[1]; /* message data */ //要發(fā)送的消息數(shù)據(jù), 字符數(shù)組長度可以根據(jù)實際需要定義 }; ? ? 消息隊列使用完后,需要刪除消息隊列,通過 msgctl 函數(shù)實現(xiàn),這是一個與ioctl 函數(shù)類似的函數(shù),其原型如下: MSGCTL(2) Linux Programmer’s Manual MSGCTL(2) NAMEmsgctl - message control operations SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgctl(int msqid, //消息隊列IDint cmd, //操作命令, 操作命令有很多,刪除消息隊列用 IPC_RMID 命令struct msqid_ds *buf); //輸出參數(shù),通過這個結(jié)構(gòu)體可獲取消息隊列的狀態(tài)信息,如果不需要獲取//消息隊列的信息,那么就設(shè)置為NULL ? ? 返回值:? ? ? ? ? ? ? ? ? 成功刪除(cmd=IPC_RMID)返回0 ,失敗返回-1. ? Exp: 發(fā)送消息的源文件: ?msgsnd.c ? #include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h>typedef struct {long type;char data[128]; }msgbuf;int main(void) {int ret;key_t key;int msgid;msgbuf msg={type: 1,data: "this is a message queue test.\n",};//獲取鍵值key=ftok("./msgsnd.c",1);if(-1 == key){perror("ftok");exit(1);}//打開或創(chuàng)建一個消息隊列msgid=msgget(key,IPC_CREAT);if(-1 == msgid ){perror("msgget");exit(2);}//發(fā)送消息ret=msgsnd(msgid,&msg,sizeof(msgbuf),0);if(-1 == ret){perror("msgsnd");}return 0; } ? ? 接收消息的源代碼文件: ?msgrcv.c #include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h>typedef struct {long type;char data[128]; }msgbuf;int main(int argc,char* argv[]) {int ret;key_t key;int msgid;msgbuf msg;//獲取鍵值key=ftok("./msgsnd.c",1);if(-1 == key ){perror("ftok");exit(1);}//打開消息隊列msgid=msgget(key,0);if(-1 == msgid ){perror("msgget");exit(2);}//接收消息ret=msgrcv(msgid, &msg, sizeof(msgbuf),1,0);if(-1 == ret){perror("msgrcv");exit(3);}printf("the recive message is: %s",msg.data);//刪除消息隊列 msgctl(msgid,IPC_RMID,NULL);return 0; }? 測試結(jié)果如下: [root@localhost msg]# gcc msgsnd.c -o snd [root@localhost msg]# gcc msgrcv.c -o rcv [root@localhost msg]# ./snd [root@localhost msg]# ./rcv the recive message is: this is a message queue test. [root@localhost msg]#?
3、信號量 ? ? 信號量主要用于兩個進(jìn)程間同步的,一般用于多進(jìn)程間的同步操作。 ? ? 例如當(dāng)兩個進(jìn)程同時要訪問聲卡的時候,那么就需要控制聲卡先由那個進(jìn)程操作,等第一個進(jìn)程操作完后,其他進(jìn)程 才能進(jìn)行操作; 但是為什么我們可以同時用mplayer 還能同時使用kmplyer播放音樂呢? 從用戶的角度來看,確實是這 樣的,但是從硬件的角度來看,在某一時刻聲卡就只能為一個應(yīng)用層程序服務(wù),當(dāng)兩個應(yīng)用程序同時對聲卡進(jìn)行操作時就 會出現(xiàn)異常,為了防止這種異常,需要進(jìn)行對兩個進(jìn)程進(jìn)行控制,當(dāng)有一個進(jìn)程獲取聲卡的控制權(quán)后,另外的一個進(jìn)程就 不能在同一時刻訪問聲卡,這就是互斥操作。(可以這樣測試, 在windows的操作系統(tǒng)上安裝VM虛擬機,打開windows media player, 然后啟動虛擬機(設(shè)置虛擬機在啟動的時候自動掛載聲卡設(shè)備),這時候聲卡就會工作不正常,會出現(xiàn) 一小段時間的異常, 聲音不正常,這就是出現(xiàn)兩個應(yīng)用程序同時使用聲卡出現(xiàn)的異常)。 ? ??信號量就是為解決類似的問題而設(shè)計的,信號量用來控制應(yīng)用程序在同一時刻對某一系統(tǒng)資源的訪問(這個系統(tǒng)資源 也稱作臨界資源,訪問臨界資源的代碼,也稱作臨界區(qū)代碼)。 ? ? 信號量用結(jié)構(gòu)體 struct sembuf 描述,其定義如下: /* semop system calls takes an array of these. */ struct sembuf {unsigned short sem_num; /* semaphore index in array */ //信號量集合中的信號量索引值,即表示信號量集合中第幾個信號量short sem_op; /* semaphore operation */ //要對信號量進(jìn)行的操作,=-1 表示信號量不可獲取, =1 表示可以獲取信號量short sem_flg; /* operation flags */ //信號量標(biāo)志 }; ? ? 這里有一點需要說明: ?通常對信號量進(jìn)行操作分為 P操作、V操作,當(dāng)設(shè)置 sem_op = -1 時表示進(jìn)行P操作, sem_op = 1 時表示要進(jìn)行V操作。 ? ? ? 和消息隊列一樣,要使用信號量,首先需要獲取一個用于信號量到IPC鍵值, 用 ftok( ) 函數(shù)獲取。 獲取到用于 信號量到IPC鍵值后,還需要創(chuàng)建或者打開一個已經(jīng)存在的信號量,通過打開或創(chuàng)建信號量獲取一個關(guān)于信號量的 信號量ID;然后通過對信號量ID進(jìn)行操作,就可以使用信號量。 ? ? 通過semget()函數(shù)創(chuàng)建或打開一個信號量,并獲取關(guān)于信號量的ID; 原型如下: SEMGET(2) Linux Programmer’s Manual SEMGET(2) NAMEsemget - get a semaphore set identifier //獲取一個信號集合的ID SYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget( key_t key, //IPC鍵值int nsems, //信號量集合中信號量的個數(shù), 要創(chuàng)建的信號量到個數(shù)int semflg); //信號量的標(biāo)志, 同OPEN的打開標(biāo)志類似 ? ??? 在獲取信號量集合ID后,需要對信號量進(jìn)行一些設(shè)定(或者說信號量初始化),然后才能操作,通過函數(shù) semctl( )對 信號量進(jìn)行初始化操作; setctl( )的原型如下: SEMCTL(2) Linux Programmer’s Manual SEMCTL(2) NAMEsemctl - semaphore control operationsSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, //信號量集合ID int semnum, //信號量集合中的信號量索引值int cmd, //要對信號量進(jìn)行的操作,可以使用的命名: IPC_SET、IPC_STAT、IPC_INFO、GETVAL、SETVAL.........); //最后一個參數(shù)根據(jù) 操作的不同,可以傳遞,也可以不傳遞 ? ? 返回值: ? ? ? ? ? ? 如果成功返回0 ,失敗返回-1.(操作為 IPC_GETVAL, 成功返回信號量到值 )。 ? ? SETVAL: 對信號量進(jìn)行設(shè)置。 這時候,要傳遞4個參數(shù), 最后一個可變參數(shù),要定義一個類型如下: union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux specific) */}; ? ? 這是一個聯(lián)合體類型,根據(jù)不同的操作命令,傳遞的值表示不同的意義。 ? ????可以通過 semop( )函數(shù)來操作信號量,其原型如下: SEMOP(2) Linux Programmer’s Manual SEMOP(2)NAMEsemop, semtimedop - semaphore operationsSYNOPSIS#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid, //信號量集合IDstruct sembuf *sops, //信號量結(jié)構(gòu)體指針unsigned nsops); //表示要操作的信號量個數(shù)int semtimedop(int semid, //信號量集合IDstruct sembuf *sops, //信號量結(jié)構(gòu)體指針unsigned nsops, //表示要操作的信號量個數(shù)struct timespec *timeout); //表示超時等待時間,如果在超時時間內(nèi)沒有獲取到可操作的信號量,就返回 Exp: ?測試 信號量到代碼, #include <stdio.h> #include <sys/ipc.h> #include <sys/sem.h> #include <fcntl.h> #include <stdlib.h>int main(void) {int i;int j;int ret;int fd; pid_t pid;key_t key;int semid;char buf[64];int size;struct sembuf sembuf;/*sembuf=(struct sembuf*)malloc(sizeof (struct sembuf));*///打開文件,用來進(jìn)行操作fd=open("./test",O_RDWR | O_CREAT | O_TRUNC,0666);if(-1 == fd){perror("open");exit(1);}//IPC鍵值key=ftok("./main.c",1);if(-1 == key){perror("ftok");exit(2);}//獲取信號量集合的IDsemid=semget(key,1,IPC_CREAT);if(-1 == semid ){perror("semget");exit(3);}//初始化信號量集合中的第一個信號量,設(shè)定信號量的值為0 , sem.sem_op = 0;ret=semctl(semid, 0, SETVAL, 1);pid=fork();if( pid==0 ) //----------------子進(jìn)程------------ {//信號量的P 操作, 即加鎖信號量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;semop(semid,&sembuf,1);size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信號量的V操作,即解鎖信號量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;semop(semid,&sembuf,1);exit(0);}//---------------子進(jìn)程結(jié)束---------------------//-----------------------父進(jìn)程---------------//信號量的P 操作, 即加鎖信號量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;semop(semid,&sembuf,1);size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信號量的V操作,即解鎖信號量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;semop(semid,&sembuf,1);semctl(semid,0,IPC_RMID);close(fd);return 0; } 代碼執(zhí)行后生成的test文件內(nèi)容如下: pid=1114, ppid=714 pid=1114, ppid=714 pid=1114, ppid=714 pid=1114, ppid=714 pid=1114, ppid=714 pid=1115, ppid=1 pid=1115, ppid=1 pid=1115, ppid=1 pid=1115, ppid=1 pid=1115, ppid=1 如果將信號量的加鎖和解鎖取消, 代碼如下: #include <stdio.h> #include <sys/ipc.h> #include <sys/sem.h> #include <fcntl.h> #include <stdlib.h>int main(void) {int i;int j;int ret;int fd; pid_t pid;key_t key;int semid;char buf[64];int size;struct sembuf sembuf;/*sembuf=(struct sembuf*)malloc(sizeof (struct sembuf));*///打開文件,用來進(jìn)行操作fd=open("./test",O_RDWR | O_CREAT | O_TRUNC,0666);if(-1 == fd){perror("open");exit(1);}//IPC鍵值key=ftok("./main.c",1);if(-1 == key){perror("ftok");exit(2);}//獲取信號量集合的IDsemid=semget(key,1,IPC_CREAT);if(-1 == semid ){perror("semget");exit(3);}//初始化信號量集合中的第一個信號量,設(shè)定信號量的值為0 ret=semctl(semid, 0, SETVAL, 1);pid=fork();if( pid==0 ) //----------------子進(jìn)程------------ {//信號量的P 操作, 即加鎖信號量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/ //取消信號量到作用 size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信號量的V操作,即解鎖信號量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/ //取消信號量到作用 exit(0);}//---------------子進(jìn)程結(jié)束---------------------//-----------------------父進(jìn)程---------------//信號量的P 操作, 即加鎖信號量sembuf.sem_num=0;sembuf.sem_op=-1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/ //取消信號量到作用 size=sprintf(buf,"pid=%d, ppid=%d\n",getpid(),getppid());for(i=0;i<5;i++){j=0;while(j<size){ret=write(fd,&buf[j++], 1);if(-1 == ret){perror("write");exit(4);}usleep(1);}}//信號量的V操作,即解鎖信號量sembuf.sem_num=0;sembuf.sem_op=1;sembuf.sem_flg=0;/*semop(semid,&sembuf,1);*/ //取消信號量到作用 semctl(semid,0,IPC_RMID);close(fd);return 0; } ? ? 生成的test文件內(nèi)容如下: ppiidd==22333387,, ppppiidd==2731347ppiidd==22333378,, ppppiidd==721343 7p ipdi=d2=323373,8 ,p ppipdi=d7=1243 3p7i dp=i2d3=3273,3 8p,p ipdp=i7d1=42 3p3i7d =p2i3d3=72,3 3p8p,i dp=p7i1d4= 2337 ? ? 可以發(fā)現(xiàn)兩個進(jìn)程同時訪問一個文件,而沒有互斥機制的話,就會出現(xiàn)亂碼。因此在訪問臨界資源的時候,就需要 采用互斥機制。 ? ? 4、共享內(nèi)存 ? ? 進(jìn)程都具有自己的虛擬地址空間(即進(jìn)程空間),進(jìn)程A不能隨意的訪問進(jìn)程B的進(jìn)程空間; 內(nèi)核提供了一種機制, 可以在物理內(nèi)存中開辟一塊存儲空間,這塊存儲空間可供進(jìn)程A或者進(jìn)程B訪問, 這樣一塊存儲空間就是共享內(nèi)存。 ? ? 要使用共享內(nèi)存也需要獲取一個IPC鍵值,通過 ?ftok()獲取IPC鍵值。 ? ? 獲取到IPC鍵值后,就需要向系統(tǒng)申請共享的存儲空間,通過函數(shù) shmget( ) 申請共享空間,并獲取關(guān)于共享內(nèi)存的 ID標(biāo)識符。 shmget()的原型如下所示: SHMGET(2) Linux Programmer’s Manual SHMGET(2) NAMEshmget - allocates a shared memory segment SYNOPSIS#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, //IPC 鍵值size_t size, //要申請的內(nèi)存空間的大小int shmflg); //共享內(nèi)存的空間打開標(biāo)志 ,與 open 的打開標(biāo)志類似, 返回值:申請成功返回共享內(nèi)存標(biāo)識ID, 失敗返回-1。 ? ? 申請成功后,還不能訪問共享內(nèi)存,因為訪問內(nèi)存需要知道內(nèi)存的地址或者指針,所以就需要向系統(tǒng)申請返回 共享內(nèi)存的地址或者指針。通過 shmmat( )向系統(tǒng)申請返回共享內(nèi)存的首地址或者指針。其原型如下: SHMOP(2) Linux Programmer’s Manual SHMOP(2) NAMEshmop - shared memory operations SYNOPSIS#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, //共享內(nèi)存標(biāo)志IDconst void *shmaddr, //傳遞NULL,表示要系統(tǒng)分配存儲緩沖區(qū),傳遞地址表示指定地址int shmflg); //打開標(biāo)志,int shmdt(const void *shmaddr); //刪除共享內(nèi)存 返回值: ? ? ? ? 成功返回共享內(nèi)存的首地址(虛擬地址),失敗返回NULL。 ? ? ? ? ? 在成功返回共享內(nèi)存首地址后,就可以向訪問用malloc 分配的內(nèi)存一樣進(jìn)行操作。 ? Exp: ?申請共享內(nèi)存,并往共享內(nèi)存寫的文件 ?shm-w.c? #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <string.h>#define SHM_SIZE 128int main(int argc,char* argv[]) {key_t key;int shm_id;char* shm_p;//獲取IPC 鍵值key=ftok("./shm-w.c",1);if(-1 == key){perror("ftok");exit(1);}//申請共享內(nèi)存空間,大小為 SHM_SIZEshm_id=shmget(key,SHM_SIZE,IPC_CREAT);if(-1 == shm_id ){perror("shmget");exit(2);}//將申請的共享內(nèi)存映射到用戶空間shm_p=shmat(shm_id,NULL,0); // if(NULL == shm_p ){perror("shmat");exit(3);}//將數(shù)據(jù)寫入到共享內(nèi)存 寫入到數(shù)據(jù)可以在其他進(jìn)程中讀取memset(shm_p,0,SHM_SIZE);strcpy(shm_p, "this is a sheard memmory.\n"); //這個函數(shù)不安全,需要注意return 0; }從共享內(nèi)存中讀取數(shù)據(jù)的文件 shm-r.c #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <string.h>#define SHM_SIZE 128int main(int argc,char* argv[]) {key_t key;int shm_id;char* shm_p;char buf[SHM_SIZE];//獲取IPC 鍵值key=ftok("./shm-w.c",1);if(-1 == key){perror("ftok");exit(1);}//申請共享內(nèi)存空間,大小為 SHM_SIZEshm_id=shmget(key,SHM_SIZE,IPC_CREAT);if(-1 == shm_id ){perror("shmget");exit(2);}//將申請的共享內(nèi)存映射到用戶空間shm_p=shmat(shm_id,NULL,0); // if(NULL == shm_p ){perror("shmat");exit(3);}//從共享內(nèi)存讀取數(shù)據(jù)memset(buf,0,SHM_SIZE);strcpy(buf, shm_p); //這個函數(shù)不安全,需要注意printf("the data read from sheard memory is: %s",buf);shmdt(shm_p); //申請撤銷共享內(nèi)存return 0; } 程序執(zhí)行的效果如下: [root@localhost shm]# gcc shm-w.c -o shmw [root@localhost shm]# gcc shm-r.c -o shmr [root@localhost shm]# ./shmw [root@localhost shm]# ./shmr the data read from sheard memory is: this is a sheard memmory. [root@localhost shm]#?
【Linux草鞋應(yīng)用編程系列】_3_進(jìn)程間通信
本系列文章未完,待續(xù)。
如果查看的過程中發(fā)現(xiàn)錯誤,請不吝指教,包括錯別字、標(biāo)點符號等。
? ?前篇:【linux草鞋應(yīng)用編程系列】_2_ 環(huán)境變量和進(jìn)程控制?
?轉(zhuǎn)載于:https://www.cnblogs.com/volcanol/p/3473642.html
總結(jié)
以上是生活随笔為你收集整理的【linux草鞋应用编程系列】_3_ 进程间通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sharepoint权限操作(记录以备忘
- 下一篇: Linux 学习笔记_10_Shell编