Linux網絡編程--進程間通訊(一)

進程間通訊簡介(摘自《Linux網絡編程》p85)linux

  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]);socket

  管道是將兩個進程之間的標準輸入輸出相互對接的機制函數

  linux命令中使用的管道 |  : ls -l | grep *.c  //顯示文件(輸入端)-(|)-(輸出端)>找到.c結尾文件ui

實現:由於半雙工緣故,因此只能實現一段輸入,一段輸出,而不能雙向通訊。因此:實現爲,經過管道鏈接進程,一端開放讀文件描述,一端開放寫文件描述spa

//管道的性質就是,一個進程的輸出做爲另外一個進程的輸入
//那麼咱們能夠關閉一個進程讀端使之做爲輸入端,
//另外一個進程關閉寫端,讀取數據,接收數據做爲管道輸出端

//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;
}

②命名管道code

  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);

相關文章
相關標籤/搜索