IPC溝通的有許多方式,例如:Shared Memory, Message Queue, PIPE, FIFO, Unix Socket 等等。
下面將會整理 Linux 下常見的 IPC 的運作方式舉例。
一、Shared Memory
主要有四個 API:
- int shmget(key_t key, size_t size, int shmflg);
- int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- void *shmat(int shmid, const void *shmaddr, int shmflg);
- int shmdt(const void *shmaddr);
用法說明:
- 使用函數 key_t ftok(char *name, int id) 取得一個在此系統內唯一的值,假設為 vKey。
- 使用函數 vShmId = shmget(vKey, 1024, 0600)取得一個大小為1024bytes, 權限為 0600(read+write) 的Shared Memory 。
- 使用函數 pShmPtr = shmat(vShmId,0,0) 取得 Shared Memory 對應的指標,以供後續操作。
- 當不需要使用時,使用 shmctl(vShmId, IPC_RMID, 0) 移除此 Shared Memory 。
舉例:
請參考 http://www.cs.cf.ac.uk/Dave/C/node27.html
二、Message Queue 的基本用法
參考 http://tldp.org/LDP/lpg/node27.html,主要有四個API
- int msgget ( key_t key, int msgflg );
- int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg );
- int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );
- int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
用法說明:
- 使用函數 key_t ftok(char *name, int id) 取得一個在此系統內唯一的值,假設為 vKey。當然這個值也可以自行指定。
- 使用函數 vQueueId = msgget( vKey, IPC_CREAT | 0660 ) 取得此 message queue 對應的 ID。
- 定義將傳送的 message 資料結構,假設是 tMsgBuf vxMsgBuf。此處需注意的是第一個欄位需定義為 long mtype, 舉例如下:
struct msgbuf
{
long mtype;
char mtext[1];
};
- 使用函數 msgsnd(vQueueId, &vxMsgBuf, sizeof(tMsgBuf), 0) 將資料送往 message queue。
- 使用函數 msgrcv(vQueueId, &vxMsgBuf, sizeof(tMsgBuf), mtype, 0)) 取得 message queue 內, 定義為 mtype 的資料。
舉例:
#define MSG_KEY 9999
#define MSG_SIZE 1024
struct tMsgBuf
{
long mtype;
char mtext[MSG_SIZE];
};
typedef struct tMsgBuf tMessageBuffer;
三、PIPEmain()
{
tMessageBuffer sndmsg={0} ;
tMessageBuffer recvmsg={0} ;
msqid = msgget(MSG_KEY , PERMS | IPC_CREAT));
msgsnd(msqid, &sndmsg, MSG_SIZE, 0);
msgrcv(msqid, &recvmsg, MSG_SIZE, 0, 0)
}
只需要一個 API,但是其只適用於 parent process 與 child process
int pipe(int fd[2]);用法說明:
- 使用 pipe() 建立兩個 file descripter,第一個 FD 用來從PIPE讀取資料,第二個FD 用來作為寫入資料至PIPE,如下例:
#include<unistd.h>
int fds[2];
int pipe(fds);- 接著呼叫 fork ,因為child process 會擁有同樣的 FD,因此便可以透過這兩個 FD 進行溝通。
舉例:
請參考
http://www.vr.ncue.edu.tw/esa/EmbeddedSystemProgramming2010/ch05.htm
四、FIFO (Named Pipe)
使用以下幾個 API
int mkfifo(const char *pathname, mode_t mode);用法說明:
int open(const char *pathname, int flags);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int fd);
int unlink(const char *pathname);
1. 定義 FIFO 對應的檔案
int client_to_server;2. 使用 mknod 或 mkfifo 建立 FIFO 對應的檔案
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
mkfifo(myfifo, 0666);3. 開啟檔案以供讀取或寫入
mkfifo(myfifo2, 0666);
client_to_server = open(myfifo, O_RDONLY);4. 寫入資料,或讀取資料
server_to_client = open(myfifo2, O_WRONLY);
read(client_to_server, buf, BUFSIZ);5. 關閉檔案,並刪除 FIFO 檔案
write(server_to_client,buf,BUFSIZ);
close(client_to_server);
close(server_to_client);
unlink(myfifo);
unlink(myfifo2);
舉例:
請參考
http://stackoverflow.com/questions/8611035/proper-fifo-client-server-connection
五、Unix Socket
參考資料:
此處socket應用與一般撰寫網路應用相同,只是此時改成使用AF_UNIX address family,設定的資訊由 sockaddr_in 的 port, address 改為 sockaddr_un 的 path
可能會用到的 API 列舉如下:
sockfd = socket(int socket_family, int socket_type, int protocol);用法說明:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int send(int s, const char *msg, int len, int flags),
int recv(int s, char *buf, int len, int flags)
int close(int fd);
請直接參考 http://learn.akae.cn/media/ch37s04.html舉例:
請參考
http://learn.akae.cn/media/ch37s04.htmlhttp://www.cs.cf.ac.uk/Dave/C/node28.html
參考資料: