進程間通信
進程間通信目的
數據傳輸:一個進程需要將他的數據發送給另一個進程
資源共享:多個進程間共享同樣的資源
通知時間:一個進程需要向另一個進程發送消息,通知他們發生了某種事件(如進程終止時要通知父進程)
進程控制:有些進程希望完全控制另一個程序的執行(如debug),此事控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道他的狀態改變
進程間通信發展
管道:
System V進程間通信
POSIX進程間通信
進程間通信分類:
1.匿名管道
2.命名管道
3.System V IPC:
System V 消息隊列
System V 共享內存
System V 信號量
4.POSIX IPC:
消息隊列
共享內存
信號量
互斥量
條件變量
讀寫鎖
管道
管道定義:
管道是最古老的進程間通信的方式
吧從一個進程連接到另一個進程的一個數據流稱爲一個“管道”
匿名管道
#include <unistd.h>
int pipe(int fd[2]);
功能:創建一個匿名管道,參數:fd,文件描述符數組,其中fd【0】表示讀端,fd【1】表示寫端
返回值:成功返回0,失敗返回錯誤代碼
實例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
int fd[2];
int ret = pipe(fd);
if(ret < 0){
perror("pipe");
return 1;
}
pid_t id = fork();
if(id == 0){//child//子進程寫,父進程讀
close(fd[0]);
int count = 0;
const char *msg = "hello f,I am c!\n";
while(1){
usleep(1000);
write(fd[1],msg,strlen(msg));
printf("%d\n",count++);
}
}else{//father
close(fd[1]);
char buf[64];
while(1){
ssize_t s = read(fd[0],buf,sizeof(buf)-1);
if(s > 0){
buf[s] = '\0';
printf("f say: %s\n",buf);
}
}
// printf("%d,%d\n",fd[0],fd[1]);
return 0;
}
用fork共享管道的原理
深度理解管道
1.父進程創建管道
2.父進程fork出子進程
3.父進程關閉fd[0],子進程關閉fd[1]
所以實質上,看待管道,就如同看待文件一樣!管道使用方法和文件是一樣的,符合Linux下一切皆文件的思想。
那麼當我們的子進程寫的快,父進程讀的很慢或者父進程讀的很快,子進程寫的很慢的時候,會怎麼樣呢》接下來要提到管道讀寫規則:
當沒有數據可讀時:
O_NONBLOCK disable:read調用阻塞,即進程暫停執行,一直等到有數據來爲止。
O_NONBLOCK enable:read調用返回-1,errno值爲EAGAIN。
當管道滿的時候:
O_NONBLOCK disable:write調用阻塞,直到有進程讀走數據
O_NONBLOCK enable:write調用返回-1,errno值爲EAGAIN。
如果所有的管道寫端對應的文件描述符被關閉,則read返回0
如果所有的管道讀端對應的文件描述符被關閉,則write操作會產生信號SIGPIPE,進而可能導致write進程退出
當要寫入的數據量不大於PIPE_BUF時,linux將保證寫入的原子性
當要寫入的數據量不大於PIPE_BUF時,linux將不再保證寫入的原子性
管道特點:
只能用於具有共同祖先的進程(具有親緣關係的進程)之間進行通信;通常,一個管道由一個進程創建,然後該進程調用fork,此後父子進程之間就可應用該管道。
管道提供流式服務
一般而言,進程退出,管道釋放,所以管道的生命週期隨進程
一般而言,內核會對管道操作進行同步與互斥
管道是半雙工的,數據智能向一個方向流動;需要雙方通信時,需要建立兩個管道。
命名管道
管道應用的一個限制就是只能在具有相同祖先(具有親緣關係)的進程間通信。
如果我們想在不相關的進程之間交換數據,可以使用fifo文件來做這項工作,他經常被稱爲命名管道。
命名管道是一種特殊類型的文件
命名管道創建
命令行方式:mkfifo filename
程序創建:int mkfifo(const char* filename,mode_t mode)
匿名管道與命名管道的區別
匿名管道由pipe函數創建並打開
命名管道由mkfifo函數創建,打開用open
FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在於他們創建和打開的方式不同,一旦這些工作完成之後,他們具有相同的語義。
命名管道的打開規則
如果當前打開操作是爲讀而打開FIFO時
O_NONBLOCK disable:阻塞到直到有相應進程爲寫而打開該FIFO
O_NONBLOCK enable:立刻返回成功
如果當前打開操作是爲寫而打開FIFO時
O_NONBLOCK disable:阻塞到直到有相應進程爲讀而打開該FIFO
O_NONBLOCK endble:立刻返回失敗,錯誤碼爲ENXIO
例:client/server
server:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
umask(0);
if(mkfifo("mypipe",0644)<0){
perror("fifo error\n");
return 1;
}
int rfd = open("mypipe",O_RDONLY);
if(rfd < 0){
perror("rfd error\n");
return -1;
}
char buf[1024];
while(1){
buf[0] = 0;
printf("please waite....\n");
ssize_t s = read(rfd,buf,sizeof(buf)-1);
if(s>0){
buf[s] = '\0';
printf("Client say# %s\n",buf);
}else if(s == 0){
printf("Client quit,exit now!\n");
exit(EXIT_SUCCESS);
}else
exit(EXIT_FAILURE);
}
close(rfd);
return 0;
}
client:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(){
int wfd = open("mypipe",O_WRONLY);
if(wfd < 0){
perror("wfd error\n");
return 1;
}
char buf[1024];
while(1){
buf[0] = 0;
printf("Please Enter# ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s > 0){
buf[s] = 0;
write(wfd,buf,strlen(buf));
}else{
perror("error");
return -1;
}
}
close(wfd);
return 0;
}