APUE 學習筆記(十) 高級I/O

1. Unix IPC(InterProcess Communication)

同一主機的各個進程間的IPC:管道、FIFO、消息隊列、信號量、共享存儲器
不一樣主機上的各個進程間IPC:socket套接字
 

2. 管道

管道進行IPC有兩個侷限:
(1) 半雙工,即 數據只能在一個方向上流動
(2) 只能在 具備公共祖先的進程之間使用。一般,一個管道由一個進程建立,而後該進程調用fork,此後 父子進程之間可使用該管道
 
fstat函數對管道的每一端都返回一個FIFO類型的文件描述符,能夠用S_ISFIFO宏來測試管道
 
一般是一個進程調用pipe函數建立管道,緊接着調用fork,這樣就建立了從父進程到子進程的IPC通道
 
對於從父進程到子進程的管道,父進程關閉 管道的讀端(fd[0])子進程關閉管道的寫端(fd[1])
 
popen、pclose兩個函數實現的操做是:建立一個管道,調用fork產生一個子進程,關閉管道的不使用端,執行一個shell以運行命令,最後等待命令終止
 
#include <stdio.h>

FILE* popen(const char* cmdstring, const char* type);
int   pclose(FILE* fp);

函數popen先執行fork,而後調用exec以執行cmdstring,而且返回一個標準I/O文件指針shell

 

  

type爲「r」表示可讀,爲「w」表示可寫
 
簡單實現一個popen:
 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <errno.h>
 4 #include <sys/wait.h>
 5 #include <fcntl.h>
 6 
 7 /* pointer to array allocated at run-time */
 8 static pid_t* childpid = NULL;
 9 
10 /* from our open_max() */
11 static int maxfd;
12 
13 FILE* my_popen(const char* cmdstring, const char* type)
14 {
15     int pfd[2];
16     pid_t pid;
17 
18     /* only allow type = "r" or "w" */
19     if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {
20         errno = EINVAL;
21         return NULL;
22     }
23        childpid = (pid_t*)calloc(maxfd, sizeof(pid_t));
24         if (childpid == NULL) {
25             return NULL;
26         }
27     }
28 
29     if (pipe(pfd) < 0) {
30         return NULL;
31     }
32 
33     if ((pid = fork()) < 0) {
34         return NULL;
35     } else if (pid == 0) {      /* child */
36         if (*type == 'r') {
37             close(pfd[0]);
38             if (pfd[1] != STDOUT_FILENO) {
39                dup2(pfd[1], STDOUT_FILENO);
40                 close(pfd[1]);
41             }
42         } else {
43             close(pfd[1]);
44             if (pfd[0] != STDIN_FILENO) {
45                 dup2(pfd[0], STDIN_FILENO);
46                 close(pfd[0]);
47             }
48         }
49 
50         /* close all fds in childpid[] */
51         for (int i = 0; i < maxfd; ++i) {
52             if (childpid[i] > 0) {
53                 close(i);
54             }
55         }
56 
57     }
58 
59     /* parent continue */
60     FILE* fp;
61     if (*type == 'r') {
62         close(pfd[1]);
63         if ((fp = fdopen(pfd[0], type)) == NULL) {
64             return NULL;
65         }
66     } else {
67         close(pfd[0]);
68         if ((fp = fdopen(pfd[1], type)) == NULL) {
69            return NULL;
70         close(pfd[0]);
71         if ((fp = fdopen(pfd[1], type)) == NULL) {
72             return NULL;
73         }
74     }
75 
76     childpid[fileno(fp)] = pid;
77     return fp;
78 }
 

3. 消息隊列

消息隊列是消息的連接表,存放在內核中並由消息隊列ID標識
msgget用於建立一個新隊列或打開一個現存的隊列
msgsnd將新消息添加到隊列尾端
msgrcv用戶從隊列中取消息
 

4. 信號量

信號量是一個計數器,用於多進程對共享數據對象的訪問
 

5. 共享存儲器

共享存儲器容許多個進程共享一個給定的存儲區,由於 數據不須要在客戶進程和服務器進程之間複製,因此這是最快的IPC
使用共享存儲區時必須確保 多個進程之間對給定的存儲區的同步訪問,一般, 信號量被用來實現對共享存儲區的同步訪問
#include <sys/shm.h>
/* 得到共享存儲標識符 */
int shmget(key_t key, size_t size, int flag);

/* 對共享存儲區執行多種操做 */
int shmctl(int shmid, int cmd, struct shmid_ds* buf);

/* 進程將共享存儲區鏈接到它的地址空間中 */
void* shmat(int shmid, const void* addr, int flag);

若是addr爲0,則此段鏈接到內核選擇的第一個可用地址上。通常將addr指定爲0,以便由內核選擇地址服務器

打印各類不一樣類型的數據所存放的位置:socket

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <sys/shm.h>
 5 
 6 #define ARRAY_SIZE 40000
 7 #define MALLOC_SIZE 100000
 8 #define SHM_SIZE 100000
 9 #define SHM_MODE 0600  /* user read/write */
10 
11 char array[ARRAY_SIZE];   /* uninitialized data = bss */
12 
13 int main(int argc, char* argv[])
14 {
15     int shmid;
16     char* ptr = NULL;
17     char* shmptr = NULL;
18 
19     fprintf(stdout, "array[] from %p to %p\n", array, array + ARRAY_SIZE);
20     fprintf(stdout, "stack around %p\n", &shmid);
21     ptr = (char*)malloc(MALLOC_SIZE);
22     if (ptr == NULL) {
23         fprintf(stderr, "malloc error\n");
24     }
25 
26     fprintf(stdout, "malloc from %p to %p\n", ptr, ptr + MALLOC_SIZE);
27 
28     shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE);
29     if (shmid < 0) {
30         fprintf(stderr, "shmget error\n");
31     }
32 
33     shmptr = shmat(shmid, 0, 0);
34     if (shmptr == (void*)-1) {
35         fprintf(stderr, "shmat error\n");
36     }
37 
38     fprintf(stdout, "shared memory from %p to %p\n", shmptr, shmptr + SHM_SIZE);
39     if (shmctl(shmid, IPC_RMID, 0) < 0) {
40         fprintf(stderr, "shmctl error\n");
41     }
42     free(ptr);
43     return 0;
44 }

在基於Intel的Linux系統上運行此程序,其輸出以下:函數

 

 

若是mmap函數指定了 MAP_SHARED標誌,則此存儲映射區可被多個進程共享
匿名存儲映射:調用mmap時指定MAP_ANON標誌,並將 文件描述符指定爲-1,結果獲得的映射區域是匿名的,由於並不與一個文件描述符相關聯
相關文章
相關標籤/搜索