Linux網路編程--處理序間通訊(一),linux網路編程
處理序間通訊簡介(摘自《Linux網路編程》p85)
AT&T 在 UNIX System V 中引入了幾種新的進程通訊方式,即訊息佇列( MessageQueues),訊號量( semaphores)和共用記憶體( shared memory),統稱為 System V IPC。在Linux 系統編程中,它們有著廣泛的應用。
System V IPC 的一個顯著的特點,是它的具體執行個體在核心中是以對象的形式出現的,我們稱之為 IPC 對象。每個 IPC 對象在系統核心中都有一個唯一的標識符。通過標識符核心可以正確的引用指定的 IPC 對象.。需要注意的是,標識符的唯一性只在每一類的 IPC 對象內成立。比如說,一個訊息佇列和一個訊號量的標識符可能是相同的,但絕對不會出現兩個有相同標識符的訊息佇列。
標識符只在核心中使用, IPC 對象在程式中是通過關鍵字( key)來訪問的。和 IPC 物件識別碼一樣,關鍵字也必須是唯一的。而且,要訪問同一個 IPC 對象, Server 和 Client必須使用同一個關鍵字。因此,如何構造新的關鍵字使之不和已有的關鍵字衝突,並保證Server 和 Client 使用的關鍵字是相同的,是建立 IPC 對象時首先要解決的一個問題。(具體在後邊的msg通訊中詳解)
通訊方法還有:半雙工管道pipe,具名管道fifo,訊息佇列,訊號量,共用內,socket通訊端等,下面一一介紹:
①半雙工管道:
int pipe(int filedes[2]);
管道是將兩個進程之間的標準輸入輸出相互對接的機制
linux命令中使用的管道 | : ls -l | grep *.c //顯示檔案(輸入端)-(|)-(輸出端)>找到.c結尾檔案
實現:因為半雙工緣故,所以只能實現一段輸入,一段輸出,而不能雙向通訊。所以:實現為,通過管道串連進程,一端開放讀檔案描述,一端開放寫檔案描述
//管道的性質就是,一個進程的輸出作為另一個進程的輸入//那麼我們可以關閉一個進程讀端使之作為輸入端,//另一個進程關閉寫端,讀取資料,接收資料作為管道輸出端//FIFO具名管道//檔案系統中,具名管道是特殊檔案的方式存在的//不同進程可以通過具名管道共用資料//具名管道一直是阻塞方式的,且必須是顯示的通過open建立串連到管道的通道#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/types.h>int main(){ int result = 1; int fd[2]; pid_t pid; int *write_fd = &fd[1]; //寫檔案描述 int *read_fd = &fd[0]; //讀檔案描述 int nbytes; char str[] = "管道,你好\n"; char readBuffer[80]; memset(readBuffer,0,sizeof(readBuffer)); result = pipe(fd); //建立管道 if(-1==result) { printf("管道建立失敗!\n"); return -1; } pid = fork(); //進程建立分叉程式 if(-1 == pid) { printf("fork失敗"); return -1; } if(0==pid) //子進程關閉讀端,寫入字元 { close(*read_fd); result = write(*write_fd,str,strlen(str)); printf("寫入%d個資料\n",result); } else //父進程關閉寫端,讀取資料 { close(*write_fd); nbytes = read(*read_fd,readBuffer,sizeof(readBuffer)); printf("接收到%d個資料,內容為%s",nbytes,readBuffer); } return 0;}
②具名管道
int mkfifo(const char* pathname,mode_t mode);
類似於普通管道,只是
a.在檔案系統中以裝置特殊檔案的形式存在
b.不同進程之間可以通過具名管道共用資料
操作區別於普通管道:FIFO中必須顯式通過open建立串連到管道的通道,且總是處於阻塞狀態的
③訊息佇列
訊息佇列是核心地址空間的內部鏈表,通過核心在各個進程之間傳遞內容。每個訊息佇列通過唯一IPC標識符標識,不同隊列相對獨立。
//file: msg.h
/* message buffer for msgsnd and msgrcv calls */struct msgbuf { __kernel_long_t mtype; /* type of message */ char mtext[1]; /* message text */};/* Obsolete, used only for backwards compatibility and libc5 compiles */struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; /* first message on queue,unused */ struct msg *msg_last; /* last message in queue,unused */ __kernel_time_t msg_stime; /* last msgsnd time */ __kernel_time_t msg_rtime; /* last msgrcv time */ __kernel_time_t msg_ctime; /* last change time */ unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */ unsigned long msg_lqbytes; /* ditto */ unsigned short msg_cbytes; /* current number of bytes on queue */ unsigned short msg_qnum; /* number of messages in queue */ unsigned short msg_qbytes; /* max number of bytes on queue */ __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */};
//filename
/* Obsolete, used only for backwards compatibility and libc5 compiles */struct ipc_perm{ __kernel_key_t key; //函數msgget()使用的索引值 __kernel_uid_t uid; //使用者UID __kernel_gid_t gid; //使用者GID __kernel_uid_t cuid; //建立者UID __kernel_gid_t cgid; //建立者GID __kernel_mode_t mode; //許可權 unsigned short seq; //序號};
核心中的訊息佇列
註:結構list_head 形成一個鏈表,結構msg_msg之中的m_list使得訊息形成鏈表,尋找,插入時,對m_list域進行位移找到位置
相關函數:
索引值構建 key_t ftok(const char* pathname,int proj_id);
擷取訊息 int msgget(key_t key,int msgflg);
發送訊息 int msgsnd(int msqid, const void * msgp,size_t msgsz,int msgflg);
接收訊息 ssize_t msgrcv(int msqid, void * msgp, size_t msgsz, long msgtype, int msgflg);
訊息控制 int msgctl(int msqid, int cmd, struct msqid_ds *buf); //向核心發送cmd命令判斷進行何種操作
一個簡單例子
④訊號量
訊號量是一種計數器,用來控制對多個進程共用的資源所進行的訪問。常用作鎖機制(生產者消費者模型是個典型使用)
訊號量結構
//filename sys/sem.h
/* arg for semctl system calls. */union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short *array; /* 數組結構 */ struct seminfo *__buf; /* 訊號量內部結構 */ void *__pad;};
相關函數
建立訊號量 int semget(key_t key, int nsems, int semflg);
//key 來自於ftok()
訊號量操作函數 int semop(int semid,struct sembuf* sops, unsigned nsops);
//訊號量的P,V操作通過向已經建立好的訊號量發送命令完成
控制訊號量參數
int semctl(int semid, int semnum ,int cmd,.....);
//用於在訊號量集合上執行控制操作
#include<stdio.h>#include<unistd.h>#include<sys/ipc.h>#include<sys/sem.h>#include<sys/types.h>typedef int sem_t;union semun{ int val; struct semid_ds * buf; unsigned short *array;}arg;sem_t CreateSem(key_t key, int value){ union semun sem; sem_t semid; sem.val = value; semid = semget(key,0,IPC_CREAT); if(-1 == semid) { printf("create semaphore error\n"); return -1; } semctl(semid,0,SETVAL,sem); return semid;}int Sem_P(sem_t semid){struct sembuf sops = {0,+1,IPC_NOWAIT};return (semop(semid,&sops,1));}int Sem_V(sem_t semid){ struct sembuf sops = {0,-1,IPC_NOWAIT}; return (semop(semid,&sops,1));}void SetvalueSem(sem_t semid , int value){ union semun sem; sem.val = value; semctl(semid,0,SETVAL,sem);}int GetvalueSem(sem_t semid){ union semun sem; return semctl(semid,0,GETVAL,sem);}void DestroySem(sem_t semid){ union semun sem; sem.val = 0; semctl(semid,0,IPC_RMID,sem);}int main(){ key_t key; int semid; char i; int value = 0; key = ftok("/ipc/sem",'a'); semid = CreateSem(key,100); for( i = 0;i <= 3;++i) { Sem_P(semid); Sem_V(semid); } value = GetvalueSem(semid); DestroySem(semid); return 0;}
⑤共用記憶體(最快捷的方法)沒有中間過程,管道等
在多個進程之間共用記憶體地區的一種處理序間通訊方式,在多個進程之間對記憶體段進行映射的方式實現記憶體共用。
相關函數
建立共用記憶體函數 int shmget(key_y key, size_t size, int shmflg);
獲得共用記憶體位址void * shmat(int shmid,const void* shmaddr, int shmflg);
刪除共用記憶體函數 int shmdt(const void* shmadddr);
共用記憶體控制函數 int shmctl(int shmid ,int cmd, struct shmid_ds * buf);
⑥訊號
用於在一個或多個進程之間傳遞非同步訊號。
相關函數
訊號截取 sighandler signal(int signum ,sighandler handler);
發送訊號 int kill(pid_t pid, int sig);
int raise(int sig);