天天看點

多程序程式設計之程序間通信多程序程式設計之程序間通信

多程序程式設計之程序間通信

轉:https://www.cnblogs.com/funblogs/p/7675515.html

  程序間通信(Interprocess Communication, IPC),經典的IPC:管道、FIFO、消息隊列、信号量以及共享存儲和套接字。

  一、管道

  管道是UNIX系統IPC的最古老的形式,所有的UNIX系統都提供此種通信機制。

  1·、兩個局限性:

    (1)半雙工,資料隻能在一個方向流動,現在有些系統可以支援全雙工管道,但是為了最佳的可移植性,應認為系統不支援全雙工管道;

    (2)管道隻能在具有公共祖先之間的兩個程序之間使用;

  2、管道的建立:

   它可以看成是一種特殊的檔案,對于它的讀寫也可以使用普通的read、write 等函數。但是它不是普通的檔案,并不屬于其他任何檔案系統,并且隻存在于記憶體中。管道是通過調用pipe函數建立的。

1 #include <unistd.h>
2 
3 int pipe(int fd[2]);
4 
5                     //傳回值:若成功,傳回0,若出錯,傳回-1.      

  經由參數fd傳回的兩個檔案描述符:fd[0]為讀而打開,fd[1]為寫而打開,fd[1]的輸出是fd[0]的輸入。通常,程序會先調用pipe,接着調用fork,進而建立了父程序與子程序的IPC通道。fork之後做什麼取決于我們想要的資料流的方向,對于從父程序到子程序,父程序關閉管道的讀端fd[0],子程序關閉寫端fd[1]。

  3、關閉管道的一端

    (1)當讀一個寫端被關閉的管道時,在所有資料都被讀取後,read傳回0,表示檔案結束;

    (2)當寫一個讀端被關閉的管道時,則産生信号SIGPIPE,如果忽略該信号或者捕捉該信号并從其處理程式傳回,則wirte傳回-1.

  關閉寫端:

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <signal.h>
 5 void sig_pipe(int signal)
 6 {
 7     printf("catch the SIGPIPE signal\n");
 8 }
 9 int main()
10 {
11     int n;
12     int fd[2];
13     int count = 0;
14     char buf[100] = {0};
15 16     if(pipe(fd) < 0)
17     {
18         perror("Fail to create pipe");
19         exit(EXIT_FAILURE);
20     }
21     //close(fd[0]);
22      
23     if ((n = write(fd[1], "hello world", 20)) < 0) {
24         perror("Write error");
25         exit(EXIT_FAILURE);
26     }
27    
28    close(fd[1]);
29 #if 1   
30     if((n = read(fd[0],buf,sizeof(buf))) < 0)
31     {
32         perror("Fail to read pipe");
33         exit(EXIT_FAILURE);
34     }
35 
36     printf("Rread %d bytes : %s.\n",n,buf);
37 #endif
38     return 0;
39 }
執行結果:
Rread 20 bytes : hello world.      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  關閉讀端: 

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <signal.h>
 5 void sig_pipe(int signal)
 6 {
 7     printf("catch the SIGPIPE signal\n");
 8 }
 9 int main()
10 {
11     int n;
12     int fd[2];
13     int count = 0;
14     char buf[100] = {0};
15     signal(SIGPIPE,sig_pipe);
16     if(pipe(fd) < 0)
17     {
18         perror("Fail to create pipe");
19         exit(EXIT_FAILURE);
20     }
21     close(fd[0]);
22      
23     if ((n = write(fd[1], "hello world", 20)) < 0) {
24         perror("Write error");
25         exit(EXIT_FAILURE);
26     }
27    
28    //close(fd[1]);
29 #if 0   
30     if((n = read(fd[0],buf,sizeof(buf))) < 0)
31     {
32         perror("Fail to read pipe");
33         exit(EXIT_FAILURE);
34     }
35 
36     printf("Rread %d bytes : %s.\n",n,buf);
37 #endif
38     return 0;
39 }
執行結果:      

catch the SIGPIPE signal

Write error: Broken pipe

多程式程式設計之程式間通信多程式程式設計之程式間通信

  在寫管道時,常量PIPE_BUF規定了核心管道的緩沖區大小,如果對管道調用write,而且要求寫的位元組數小于等于PIPE_BUF,則此操作不會與其他程序對同一管道的write操作交叉進行,如果多個程序對同一管道寫的位元組數超過PIPE_BUF,所寫的資料可能會與其他程序所寫的資料互相交叉。用pathconf或fpathconf函數可以獲得PIPE_BUF的值。

 二、有名管道FIFO

  未命名的管道隻能在兩個相關的程序之間通信,通過有名管道FIFO,不相關的程序也能交換資料。FIFO不同于管道之處在于它提供一個路徑與之關聯,以FIFO的檔案形式存在于系統中。

  1、有名管道的建立:

1 #include <sys/stat.h>
2 
3 int mkfifo(const char *path, mode_t mode);
4 
5                                     //傳回值:若成功,傳回0;若出錯,傳回-1。參數pathname為路徑名,建立管道的名字,mode為建立fifo的權限。      

  2、有名管道的讀寫規則 

  A.從FIFO中讀取資料

  約定:如果一個程序為了從FIFO中讀取資料而以阻塞的方式打開FIFO, 則稱核心為該程序的讀操作設定了阻塞标志

  <1>如果有程序為寫而打開FIFO,且目前FIFO内沒有資料,則對于設定了阻塞标志的讀操作來說,将一直阻塞。對于沒有設定阻塞标志讀操作來說傳回-1,目前errno值為EAGAIN,提醒以後再試。

  <2>對于設定阻塞标志的讀操作說,造成阻塞的原因有兩種:目前FIFO内有資料,但有其他程序正在讀這些資料;另外就是FIFO内沒有資料。解阻塞的原因則是FIFO中有新的資料寫入,不論寫入資料量的大小,也不論讀操作請求多少資料量。

  <3>如果沒有程序寫打開FIFO,則設定了阻塞标志的讀操作會阻塞

  <4>如果寫端關閉,管道中有資料讀取管道中的資料,如果管道中沒有資料讀端将不會繼續阻塞,此時傳回0。

  注意:如果FIFO中有資料,則設定了阻塞标志的讀操作不會因為FIFO中的位元組數小于請求讀的位元組數而阻塞,此時,讀操作會傳回FIFO中現有的資料量。

  B.向FIFO中寫入資料

  約定:如果一個程序為了向FIFO中寫入資料而阻塞打開FIFO,那麼稱該程序内的寫操作設定了阻塞标志。

  對于設定了阻塞标志的寫操作:

  <1>當要寫入的資料量不大于PIPE_BUF時,linux将保證寫入的原子性。如果此時管道空閑緩沖區不足以容納要寫入的位元組數,則進入睡眠,直到當緩沖區中能夠容納寫入的位元組數時,才開始進行一次性寫操作。

  <2>當要寫入的資料量大于PIPE_BUF時,Linux将不再保證寫入的原子性。FIFO緩沖區一有空閑區域,寫程序就會試圖向管道寫入資料,寫操作在寫完所有請求寫的資料後傳回。

  對于沒有設定阻塞标志的寫操作:

  <1>當要寫入的資料量大于PIPE_BUF時,linux将不再保證寫入的原子性。在寫滿所有FIFO空閑緩沖區後,寫操作傳回。

  <2>當要寫入的資料量不大于PIPE_BUF時,linux将保證寫入的原子性。如果目前FIFO空閑緩沖區能夠容納請求寫入的位元組數,寫完後成功傳回;如果目前FIFO空閑緩沖區不能夠容納請求寫入的位元組數,則傳回EAGAIN錯誤,提醒以後再寫。

  讀有名管道:

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <stdio.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <string.h>
 5 #include <sys/types.h>
 6 int main(int argc, char **argv)
 7 {
 8     int fd;
 9     char buf[100] = {0};
10     int n = 0;
11     //if (mkfifo("/tmp/myfifo", 0777) < 0) {
12     //    printf("create fifo failed\n");
13     //    return 0;
14     //}
15     printf("before open\n");
16     fd = open("/tmp/myfifo", O_RDONLY|O_NONBLOCK);//非阻塞方式打開有名管道
17     if (fd < 0) {
18         printf("open error\n");
19         return 0;
20     }
21     printf("after open\n");
22     while (1) {
23         memset(buf, 0, sizeof(buf));
24         if ((n = read(fd, buf, 100)) == 0) {
25             printf("1: n = %d\n", n);
26             printf("no data in fifo\n");
27             sleep(1);
28         } else {
29             printf("2: n = %d\n", n);
30             printf("get data: %s\n",buf);
31             sleep(1);
32         }
33     }
34     close(fd);
35     return 0;
36 }      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  寫有名管道: 

多程式程式設計之程式間通信多程式程式設計之程式間通信
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
    int fd;
    char buf[100] = {0};
    int n = 0;
    if(access("/tmp/myfifo", F_OK) != 0){
        if (mkfifo("/tmp/myfifo", 0777) < 0) {
            printf("create fifo failed\n");
            return 0;
        }
    }
    printf("before open\n");
    fd = open("/tmp/myfifo", O_WRONLY);//以阻塞方式打開有名管道
    if (fd < 0) {
        printf("open error\n");
        return 0;
    }
    printf("after open\n");
#if 0
    while (1) {
        memset(buf, 0, sizeof(buf));
        if ((n = read(fd, buf, 100)) == 0) {
            printf("n = %d\n", n);
            printf("no data in fifo\n");
            sleep(1);
        } else {
            printf("         n = %d\n", n);
            printf("get data: %s\n",buf);
            sleep(1);
        }
    }
#endif
    n = write(fd, "abc123", 6);
    printf("n = %d\n", n);
    close(fd);
    return 0;
}      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  阻塞與非阻塞的總結:

  (1):寫程序阻塞打開有名管道,讀程序阻塞打開有名管道

    先運作寫程序,阻塞,在運作讀程序,寫程序不再阻塞,讀程序不阻塞;

    先運作讀程序,阻塞,在運作寫程序,讀程序不再阻塞,寫程序不阻塞。

  (2):讀寫程序都非阻塞打開有名管道

    先運作寫程序,寫程序打開管道失敗;

    先運作讀程序,讀程序讀取不到内容,退出,如果循環讀取,當寫程序運作後,可讀到寫程序寫入的内容;

  (3):寫程序阻塞打開有名管道,讀程序非阻塞打開有名管道

    先運作寫程序,寫程序阻塞,在運作讀程序,運作正常;

    先運作讀程序,如果讀程序不是循環讀,則退出,再運作寫程序,寫程序阻塞;

  (4):寫程序非阻塞打開有名管道,讀程序阻塞打開有名管道

    先運作寫程序,寫程序打開管道失敗;

    先運作讀程序,讀程序阻塞,再運作寫程序,運作正常;

  三、消息隊列

    1、消息隊列的特點

      1)消息隊列是消息的連結清單,具有特定的格式,存放在記憶體中并由消息隊列辨別符辨別.

        2)消息隊列允許一個或多個程序向它寫入與讀取消息.

        3)管道和命名管道都是通信資料都是先進先出的原則,消息隊列可以實作消息的随機查詢,消息不一定要以先進先出的次序讀取,也可以按消息的類型讀取.比FIFO更有優勢

    2、相關函數 

     1)獲得key值,因為消息隊列獨立于程序而存在,為了差別不同的消息隊列,需要以key值标記消息隊列,這樣兩個不相關的程序可以通過事先約定的key值通過消息隊列進行消息收發。

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/ips.h>
 3 
 4 key_t futon(char *pathname, int projid)
 5 參數:
 6     pathname:檔案名(含路徑),通常設定為目前目錄“.” 
 7     projid:子序号,雖然為int類型,但隻使用了低8位,通常用一個字母表示
 8 傳回值:
 9     成功傳回key值,失敗傳回-1;
10 
11 man手冊:
12      The ftok() function attempts to create a unique key suitable for use with the semget(2), and shmget(2) functions, given the path
13      of an existing file and a user-selectable id.
14 
15      The specified path must specify an existing file that is accessible to the calling process or the call will fail.  Also, note
16      that links to files will return the same key, given the same id.      
多程式程式設計之程式間通信多程式程式設計之程式間通信

    2)建立或打開消息隊列

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/msg.h>
 4 
 5 int msgget(key_t key, int msgflag)
 6 /*
 7 功能:
 8     用于建立一個新的或打開一個已經存在的消息隊列,此消息隊列與key相對應。
 9 參數:
10     key:函數ftok的傳回值或IPC_PRIVATE,為IPC_PRIVATE時表示建立自己的消息隊列,這樣就隻能用于和自己有親緣關系的程序間通信。
11     msgflag:
12         IPC_CREAT:建立新的消息隊列。
13         IPC_EXCL:與IPC_CREAT一同使用,表示如果要建立的消息隊列已經存在,則傳回錯誤。
14         IPC_NOWAIT:讀寫消息隊列要求無法滿足時,不阻塞。
15 傳回值:
16     調用成功傳回隊列辨別符,否則傳回-1.
17 */      
多程式程式設計之程式間通信多程式程式設計之程式間通信

    3)将消息添加到消息隊列

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/msg.h>
 4 
 5 int msgsnd(int msqid,  struct msgbuf *msgp,  size_t msgsz,  int msgflag)
 6 /*
 7 功能:
 8     将新消息添加到隊列尾端,即向消息隊列中發送一條消息。
 9 參數:
10     msqid:消息隊列的辨別符。
11     msgp:指向消息緩沖區的指針,此位置用來暫時存儲發送和接收的消息,是一個使用者可定義的通用結構,形态如下
12  
13 struct msgbuf {
14     long mtype;     /* 消息類型,必須 > 0 */
15     char mtext[1];  /* 消息文本 */
16 };
17     msgsz:消息的大小,消息的總大小不能超過8192位元組,在<linux/msg.h>有個宏控制,#define MSGMAX 8192。
18     msgflag:
19          IPC_NOWAIT: 指明在消息隊列沒有足夠空間容納要發送的消息時,msgsnd立即傳回。
20          0:msgsnd調用阻塞直到條件滿足為止.(一般選這個)
21 */      
多程式程式設計之程式間通信多程式程式設計之程式間通信

    4)從消息隊列讀取消息

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/msg.h>
 4 
 5 ssize_t msgrcv(int msqid,  struct msgbuf *msgp,  size_t msgsz,  long msgtype,  int msgflag)
 6 
 7 /*
 8 功能:
 9     從隊列中接收消息
10 參數:
11     msqid:已打開的消息隊列id
12     msgp:存放消息的結構體指針。msgp->mtype與第四個參數是相同的。
13     msgsz:消息的位元組數,指定mtext的大小。
14     msgtype:消息類型,消息類型 mtype的值。如果為0,則接受該隊列中的第一條資訊,如果小于0,則接受小于該值的絕對值的消息類型,如果大于0,接受指定類型的消息,即該值消息。
15     msgflag:函數的控制屬性。
16     msgflag:
17         MSG_NOERROR:若傳回的消息比msgsz位元組多,則消息就會截短到msgsz位元組,且不通知消息發送程序.
18         IPC_NOWAIT:調用程序會立即傳回.若沒有收到消息則傳回-1.
19         0:msgrcv調用阻塞直到條件滿足為止.
20 在成功地讀取了一條消息以後,隊列中的這條消息将被删除。
21 傳回值:成功執行時,msgrcv()傳回0, 失敗傳回-1
22 */      
多程式程式設計之程式間通信多程式程式設計之程式間通信

    5)消息隊列屬性控制

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/ipc.h>
 3 #include <sys/msg.h>
 4 
 5 int msgctl(int msqid,  int cmd,  struct msqid_ds *buf)
 6 
 7 /*
 8    功能:
 9  對消息隊列進行各種控制操作,操作的動作由cmd控制。
10 參數:
11     msqid:消息隊列ID,消息隊列辨別符,該值為msgget建立消息隊列的傳回值。
12     cmd:
13         IPC_STAT:取出系統儲存的消息隊列的msqid_ds 資料,并将其存入參數buf 指向的msqid_ds 結構
14         IPC_SET:設定消息隊列的msqid_ds 資料中的msg_perm 成員。設定的值由buf 指向的msqid_ds結構給出
15         IPC_RMID:删除由msqid訓示的消息隊列
16 */      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  3、執行個體

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 /* msgsnd.c */
 2 
 3 
 4 #include <sys/types.h>
 5 #include <sys/ipc.h>
 6 #include <sys/msg.h>
 7 #include <stdio.h>
 8 #include <stdlib.h>
 9 #include <unistd.h>
10 #include <string.h>
11 #define  BUFFER_SIZE 512
12 
13 
14 struct message
15 {
16     long msg_type;
17     char msg_text[BUFFER_SIZE];
18 };
19 
20 
21 int main()
22 {
23     int   qid;
24     key_t key;
25     struct message msg;
26 
27     /*根據不同的路徑和關鍵表示産生标準的key*/
28     if ((key = ftok(".", 'a')) == -1)
29     {
30         perror("ftok");
31         exit(1);
32     }
33 
34     /*建立消息隊列*/
35     if ((qid = msgget(key, IPC_CREAT|0666)) == -1)
36     {
37         perror("msgget");
38         exit(1);
39     }
40     printf("Open queue %d\n",qid);
41 
42     while(1)
43     {
44        printf("Enter some message to the queue(enter 'quit' to exit):");
45         if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)
46         {
47             puts("no message");
48             exit(1);
49         }
50 
51         msg.msg_type = getpid();
52 
53         /*添加消息到消息隊列*/
54         if ((msgsnd(qid, &msg, strlen(msg.msg_text), 0)) < 0)
55         {
56             perror("message posted");
57             exit(1);
58         }
59 
60         if (strncmp(msg.msg_text, "quit", 4) == 0)
61         {
62             break;
63         }
64     }
65 
66     exit(0);
67 }                
多程式程式設計之程式間通信多程式程式設計之程式間通信
多程式程式設計之程式間通信多程式程式設計之程式間通信
1 /* msgrcv.c */
 2 
 3 #include <sys/types.h>
 4 #include <sys/ipc.h>
 5 #include <sys/msg.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <unistd.h>
 9 #include <string.h>
10 #define  BUFFER_SIZE        512
11 
12 struct message
13 {
14     long msg_type;
15     char msg_text[BUFFER_SIZE];
16 };
17 
18 int main()
19 {
20     int qid;
21     key_t key;
22     struct message msg;
23     
24     /*根據不同的路徑和關鍵表示産生标準的key*/
25     if ((key = ftok(".", 'a')) == -1)
26     {
27         perror("ftok");
28         exit(1);
29     }
30     
31     /*建立消息隊列*/
32     if ((qid = msgget(key, IPC_CREAT|0666)) == -1)
33     {
34         perror("msgget");
35         exit(1);
36     }
37     printf("Open queue %d\n", qid);
38     
39     do
40     {
41         /*讀取消息隊列*/
42         memset(msg.msg_text, 0, BUFFER_SIZE);
43         if (msgrcv(qid, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)   //讀取消息不管是誰發的
44         {
45             perror("msgrcv");
46             exit(1);
47         }
48         printf("The message from process %d : %s", msg.msg_type, msg.msg_text);        
49         
50     } while(strncmp(msg.msg_text, "quit", 4));
51     
52     
53     /*從系統核心中移走消息隊列 */
54     if ((msgctl(qid, IPC_RMID, NULL)) < 0)
55     {
56         perror("msgctl");
57         exit(1);
58     }
59     
60     exit(0);
61 }      
多程式程式設計之程式間通信多程式程式設計之程式間通信

   四、共享存儲

    共享存儲允許一個或多個程序共享一個給定的存儲區,因為資料不需要在用戶端程序和伺服器程序之間進行複制,是以這是最快的一種IPC,對于像管道和消息隊列等通信方式,則需要在核心和使用者空間進行四次的資料拷貝,而共享記憶體則隻拷貝兩次資料:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。

    不同程序之間共享的記憶體通常安排為同一段實體記憶體。程序可以将同一段共享記憶體連接配接到它們自己的位址空間中,所有程序都可以通路共享記憶體中的位址,同一塊實體記憶體被映射到程序A、B各自的程序位址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。由于多個程序共享同一塊記憶體區域,必然需要某種同步機制,互斥鎖和信号量都可以。

    1、共享存儲的特點:

      1)速度快,效率高;

      2)需要有同步機制;

    2、共享存儲操作流程

      1)建立/打開共享記憶體

      2)映射共享記憶體,即把即把指定的共享記憶體映射到程序的位址空間用于通路

      3)撤銷共享記憶體映射

      4)删除共享記憶體

    3、相關函數

      1)建立/打開共享記憶體

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/ipc.h>
 2 #include <sys/shm.h>
 3 
 4 int shmget(key_t key, size_t size, int shmflg);
 5 
 6 /*
 7    功能:配置設定一塊共享記憶體
 8 
 9    參數:
10         key: 函數ftok的傳回值或IPC_PRIVATE,為IPC_PRIVATE時建立的共享記憶體隻能用于有親緣關系的程序通信;
11         size:建立共享記憶體的大小(機關位元組),通常取系統頁長的整數倍,若size值不是系統頁的整數倍,則最後一頁剩餘部分是不可用的;
12         shmflg:
13             0:取共享記憶體辨別符,若不存在則函數會報錯
14             IPC_CREAT:如果核心中不存在鍵值與key相等的共享記憶體,則建立一個共享記憶體;如果存在這樣的共享記憶體,傳回此共享記憶體的辨別符
15             IPC_CREAT|IPC_EXCL:如果核心中不存在鍵值與key相等的共享記憶體,則建立一個消息隊列;如果存在這樣的共享記憶體則報錯  
16    傳回值:成功,傳回共享記憶體辨別符,失敗傳回-1;      
17 */                                              
多程式程式設計之程式間通信多程式程式設計之程式間通信

      2)映射共享記憶體

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/shm.h>
 3 
 4 void *shmat(int shmid, const void *shmaddr, int shmflg)
 5 
 6 /*
 7     功能:連接配接共享記憶體辨別符為shmid的共享記憶體,連接配接成功後把共享記憶體區對象映射到調用程序的位址空間,随後可像本地空間一樣通路
 8     
 9     參數:
10           msqid:共享記憶體辨別符  
11           shmaddr:指定共享記憶體出現在程序記憶體位址的什麼位置,直接指定為NULL讓核心自己決定一個合适的位址位置  
12           shmflg:SHM_RDONLY:為隻讀模式,否則為讀寫模式
13 
14     傳回值:
15             成功,傳回共享記憶體位址,出錯傳回-1
16 */          
多程式程式設計之程式間通信多程式程式設計之程式間通信

      3)撤銷共享記憶體映射

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/shm.h>
 3 
 4 int shmdt(const void *shmaddr)
 5 
 6 /*
 7     功能:取消共享記憶體與使用者程序之間的映射
 8 
 9     參數:shmaddr,連接配接的共享記憶體的起始位址
10 
11     傳回值:成功傳回0,出錯傳回-1。
12 */      
多程式程式設計之程式間通信多程式程式設計之程式間通信

      4)控制共享記憶體

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/shm.h>
 3 
 4 int shmctl(int shmid, int cmd, struct shmid_ds *buf)
 5 
 6 /*
 7     功能:完成對共享記憶體的控制
 8 
 9     參數:
10         shmid:共享記憶體辨別id;
11         cmd:
12             IPC_STAT得到共享記憶體的狀态
13             IPC_SET改變共享記憶體的狀态
14             IPC_RMID删除共享記憶體
15         but:是一個結構體指針,IPC_STAT時,取得共享記憶體狀态放在這個結構體中,IPC_SET改變共享記憶體狀态時,用這個結構指定
16 
17     傳回值:成功傳回0,失敗傳回-1;
18 */          
多程式程式設計之程式間通信多程式程式設計之程式間通信

    4、執行個體:

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <string.h>
 4 #include <sys/ipc.h>
 5 #include <sys/shm.h>
 6 //#include <error.h>
 7 #define SIZE 1024
 8 int main()
 9 {
10     int shmid ;
11     char *shmaddr ;
12     struct shmid_ds buf ;
13     int flag = 0 ;
14     int pid ;
15  
16     shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;
17     if ( shmid < 0 )
18     {
19             perror("get shm  ipc_id error") ;
20             return -1 ;
21     }
22     pid = fork() ;
23     if ( pid == 0 )
24     {
25         shmaddr = (char *)shmat( shmid, NULL, 0 ) ;
26         if ( (int)shmaddr == -1 )
27         {
28             perror("shmat addr error") ;
29             return -1 ;
30  
31         }
32         strcpy( shmaddr, "Hi, I am child process!\n") ;
33         shmdt( shmaddr ) ;
34         return  0;
35     } else if ( pid > 0) {
36         sleep(3 ) ;
37         flag = shmctl( shmid, IPC_STAT, &buf) ;
38         if ( flag == -1 )
39         {
40             perror("shmctl shm error") ;
41             return -1 ;
42         }
43  
44         printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
45         printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
46         printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;
47         shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
48         if ( (int)shmaddr == -1 )
49         {
50             perror("shmat addr error") ;
51             return -1 ;
52  
53         }
54         printf("%s", shmaddr) ;
55         shmdt( shmaddr ) ;
56         shmctl(shmid, IPC_RMID, NULL) ;
57     }else{
58         perror("fork error") ;
59         shmctl(shmid, IPC_RMID, NULL) ;
60     }
61  
62     return 0 ;
63 }      
多程式程式設計之程式間通信多程式程式設計之程式間通信

   五、UNIX域套接字

    UNIX域套接字用于在同一台機器上運作的程序之間的通信,雖然網際網路域套接字可用于同一目的,但UNIX域套接字的效率更高。UNIX域套接字僅僅複制資料;它們并不執行協定處理,不需要添加或删除網絡報頭,無需計算檢驗和,不要産生順序号,無需發送确認封包。UNIX域套接字有兩種類型的套接字:位元組流套接字和資料包套接字,位元組流套接字類似于TCP,資料包套接字類似于UDP。

  1、非命名的UNIX域套接字

  1)建立

    UNIX域套接字是套接字和管道之間的混合物。為了建立一對非命名的,互相連接配接的UNXI域套接字,可以使用socketpair函數。

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <sys/types.h>
 2 #include <sys/socket.h>
 3 
 4 int socketpair(int domain, int type, int protocol, int sv[2]);
 5 
 6 功能:建立一對匿名的已連接配接的全雙工的socket。建立出來的兩個描述符是等價的。
 7 
 8 參數:
 9     domain:表示協定族,隻能為AF_LOCAL或者AF_UNIX;
10     type:表示協定類型,可以是SOCK_STREAM或者SOCK_DGRAM;
11     protocol:表示協定,隻能為0;
12     sv[2]:接收代表兩個套接口的整數數組
13 
14 傳回值:成功傳回0,失敗傳回-1;      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  2)、執行個體:父子程序個關閉一個檔案描述符,則在父程序寫可從子程序讀取,反之若子程序寫,父程序同樣可以讀取。

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/socket.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 int main(void){
 7         int fd[2];
 8         int pid;
 9        char wbuf1[16] = "1234567890";
10     char wbuf2[16] = "abcdefg";
11        char rbuf[16];
12         if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
13                 perror("socketpair");
14                 return -1;
15         }
16 
17         if((pid = fork())<0){
18                 perror("fork");
19                 return -1;
20         }else if(pid == 0){
21                 //child
22                 close(fd[0]);
23                 if(write(fd[1],wbuf1,strlen(wbuf1)) < 0){
24                         perror("write");
25                         exit(-1);
26                 }
27         if(read(fd[1],rbuf,16) < 0) {
28             perror("child read");
29             exit(-1);
30         }
31         printf("child:%s\n",rbuf);
32         }else{
33                 //parent
34                 close(fd[1]);
35         if(write(fd[0], wbuf2, 16) < 0) {
36             perror("parent write");
37             exit(-1);
38         }
39                 if(read(fd[0],rbuf,16) < 0){
40                         perror("read");
41                         exit(-1);
42                 }
43                 printf("parent:%s\n",rbuf);
44         }
45         return 0;
46 }      
多程式程式設計之程式間通信多程式程式設計之程式間通信

  2、命名UNIX域套接字

    socketpair函數建立互相連接配接的一對套接字,但是每一個套接字都沒有名字。這意味着無關程序不能使用它們。命名UNIX域套接字和網際網路域套接字類似,不過UNIX域套接字隻能用于同一台裝置之間的程序間通信。

    1)UNIX域套接字的特點

      1)UNIX域套接字與TCP套接字比較,在同一台主機的傳輸速度前者是後者的兩倍;

      2)UNIX域套接字可以在同一台主機上各程序之間傳遞描述符;

      3)UNIX域套接字與傳統套接字的差別是用路徑名來表示協定族的描述。

    2)UNIX域位址結構

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 <linux/un.h>
 2 
 3 #define UNIX_PATH_MAX    108
 4 
 5 struct sockaddr_un {
 6     sa_family sun_family;
 7     char sun_path[UNIX_PATH_MAX];
 8 }
 9 
10 
11 //UNIX域位址結構成員變量sun_family的值是AF_UNIX或AF_LOCAL;
12 //sun_path是一個路徑名,此路徑名的屬性為0777,可以進行讀寫等操作;      

注意:

sun_path成員包含一路經名,當我們将一個位址綁定至UNIX域套接字時,系統用該路經名建立一類型為S_IFSOCK檔案。該檔案僅用于向客戶程序告知套接字名字。該檔案不能打開,也不能由應用程式用于通信。如果當我們試圖綁定位址時,該檔案已經存在,那麼bind請求失敗。當關閉套接字時,并不自動删除該檔案,是以我們必須確定在應用程式終止前,對該檔案執行解除連結操作。

多程式程式設計之程式間通信多程式程式設計之程式間通信

    3)套接字函數

    UNIX域套接字函數和以太網套接字函數類似,但是當用于UNIX域套接字時,套接字函數有一些差别和限制,主要有如下幾條:

    (1)使用函數bind()進行套接字和位址綁定的時候,位址結構中的路徑名和路徑名所表示的檔案的預設權限為0777;

    (2)結構sun_path中的路徑名必須是一個絕對路徑,不能是相對路徑;

    (3)函數connect()的路徑名必須是一個綁定在某個已打開的unix域套接字上的路徑名,并且套接字類型也必須一緻;

    (4)如果UNIX域位元組流套接字的connect()函數發現監聽隊列已滿,則立刻傳回ECONNREFUSED錯誤,這和tcp不同,tcp套接字如果發現監聽隊列已滿,伺服器方會忽略到來的syn,tcp連接配接發起方會接着發送syn進行重傳;

    4)執行個體

多程式程式設計之程式間通信多程式程式設計之程式間通信
1 /*用戶端程式*/
  2 
  3 #define     IPPROBE_STR                              "/tmp/ipprobe.str"
  4 
  5  
  6 
  7 int local_write_read(char *path, char *write_buf, int write_len, char *read_buf, int read_len)
  8 
  9 {
 10 
 11          int sockfd;
 12 
 13          struct sockaddr_un servaddr;
 14 
 15          int ret = 0;
 16 
 17  
 18 
 19          assert(path);
 20 
 21          assert(write_buf);
 22 
 23          assert(read_buf);
 24 
 25          if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
 26 
 27          {
 28 
 29                    return -1;
 30 
 31          }
 32 
 33  
 34 
 35          memset(&servaddr, 0 ,sizeof(servaddr));
 36 
 37          servaddr.sun_family = AF_LOCAL;
 38 
 39          strcpy(servaddr.sun_path, path);
 40 
 41  
 42 
 43          if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
 44 
 45          {
 46 
 47                    return -1;
 48 
 49          }
 50 
 51  
 52 
 53          ret = write(sockfd, write_buf, write_len);
 54 
 55          if (ret < 0)
 56 
 57          {
 58 
 59                    return -1;
 60 
 61          }
 62 
 63         
 64 
 65          ret = read(sockfd, read_buf, read_len);
 66 
 67          printf("local_write_read ret = %d\n", ret);
 68 
 69          if (ret < 0)
 70 
 71          {
 72 
 73                    read_buf = NULL;
 74 
 75                    return -1;
 76 
 77          }
 78 
 79  
 80 
 81          close(sockfd);
 82 
 83          return 0;
 84 
 85 }
 86 
 87 
 88 /*伺服器程式*/
 89 
 90 int response_server()
 91 
 92 {
 93 
 94          struct sockaddr_un serv_addr, cli_addr;
 95 
 96          int listen_fd, conn_fd;
 97 
 98          socklen_t cli_len;
 99 
100          int ret = 0;
101 
102          char buf[256];
103 
104          unsigned short flag;
105 
106          struct ipprobe_t *ipprobe;
107 
108          struct ipprobe_reg_t *reg;
109 
110          struct ipprobe_entry_t *entry;
111 
112          struct ipprobe_query_t query;
113 
114          unsigned short id;
115 
116          int i = 0;
117 
118  
119 
120  
121 
122         
123 
124          debug_msg("response_server start\n");       
125 
126  
127 
128          if ((listen_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
129 
130          {
131 
132                    debug_msg("socket");
133 
134                    return -1;
135 
136          }
137 
138         
139 
140          memset(&serv_addr, 0, sizeof(serv_addr));
141 
142          serv_addr.sun_family = AF_LOCAL;
143 
144          strcpy(serv_addr.sun_path, IPPROBE_STR);
145 
146          unlink(serv_addr.sun_path);
147 
148         
149 
150          if (bind(listen_fd, (struct sockaddr *)&serv_addr, SUN_LEN(&serv_addr)) < 0)
151 
152          {
153 
154                    debug_msg("bind");
155 
156                    return -1;
157 
158          }
159 
160         
161 
162          if (listen(listen_fd, 10) < 0)
163 
164          {
165 
166                    debug_msg("listen");
167 
168                    return -1;
169 
170          }
171 
172  
173 
174          cli_len = SUN_LEN(&cli_addr);
175 
176          debug_msg("listening\n");
177 
178         
179 
180          for (;;)
181 
182          {
183 
184                    if ((conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len)) < 0 )
185 
186                    {
187 
188                             return -1;
189 
190                    }
191 
192                   
193 
194                    memset(buf, 0, sizeof(buf));
195 
196                    if (read(conn_fd, buf, sizeof(buf)) < 0)
197 
198                    {
199 
200                             debug_msg("read");
201 
202                             return -1;
203 
204                    }
205 
206  
207 
208                    flag =  *(unsigned short *)buf;
209 
210                    debug_msg("flag = %d\n", flag);
211 
212                    switch (flag)
213                         ……
214         }
215 }
216