进程间通信:
1: 资源共享 ---
2: 数据传输 --- 发送接受信息
3: 通知事件 --- 信号
4: 进程控制 --- 控制进程状态转换
比较古老:
无名管道(PIPE)
有名管道(FIFO ,named pipe)
信号(signal)
SYSTEM V IPC
信号量(Sem)
共享内存(shm)
消息队列(Msg)
BSD
套接字(Socket)---- 网络编程
无名管道(PIPE)
int fd[2];
读0写1 -- 只能往read fd[0] , write [1];
open(无) ---> pipe 已有创建并打开功能
read / write / close
无名管道只能用在有亲缘关系的进程中,进程结束了也随之消失( 局限)
一定是先pipe申请管道,才去fork子进程
1:无名管道有大小限制(4096B), 只要有空位,就可以往里面写数据,--- -- 不能保证数据的原子性
写满后只能等到有数据被读出才能继续写入
2: 读数据是一定要读到数据才能返回的,否则就会等待
3:只有确定不再需要使用该管道的读或者写功能才能关闭close, 否则关了后就没法再open使用
4:全部进程中必须有一个fd[0]是没有关闭的,才能write进数据;
如果fd[0]全关了,write的时候内核会发一个信号 SIGPIPE, 进程对于SIGPIPE的默认操作是终止进程
实例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sig_isr(int sig)
{
printf("catch a SIGPIPE %d \n", sig);
//exit(0);
}
int main(void)
{
pid_t pid;
int fd[2];
char buf[100];
if(pipe(fd) == -1)
{
perror("pipe error");
exit(1);
}
printf("fd[0] = %d , fd[1]= %d\n", fd[0], fd[1]); //输出3,4。按顺序1,2被用了
if((pid = fork())< 0)
{
perror("fork error");
exit(1);
}
else if(pid == 0)// read
{
// 读0写1 -- read fd[0] , write [1];
close(fd[1]);// 写端关闭
printf("ready to read...\n");
read(fd[0], buf, 100); // 等待
printf("%s \n", buf);
/*如果上面没有read等待主进程的write,就关闭了f[0]
那write的时候内核会发一个信号 SIGPIPE, 进程对于SIGPIPE的默认操作是终止进程*/
close(fd[0]);
_exit(0);
}
else // write
{
signal(SIGPIPE , sig_isr);
sleep(1);// for check read's hangup
close(fd[0]);//进程间的通信,关闭不需要的,留下的在同一个管道里通信。
printf("start write\n");
write(fd[1], "hello world \n", 100);
printf("end write\n");
close(fd[1]);
wait(NULL);
printf("exit\n");
exit(0);
}
}
有名管道(FIFO ,named pipe)
FIFO 是有一个具体的文件的
mkfifo FIFO -m 0666 --- shell指令去创建
mkfifo("path", 0666) --使用函数去创建,mode&~umask
操作:open / close /read / write
是可以在任意两个进程中通信
open(“path” , )
O_RDONLY
O_WRONLY
O_RDWR (FIFO不要用RDWR)
| O_APPEND (和wr一起用,追加)
| O_TRUNC (和wr一起用,覆盖,直接清空后再写入,对于FIFO或者设备文件无效)
|O_CREAT|O_EXCL (如果已经存在,会返回失败; FIFO不能用open里面的CREAT创建)
|O_NONBLOCK
读写方式|写的方式 |创建并检查 |不阻塞
open(fifo , O_WRONLY|O_NONBLOCK) 看下能否以只写方式打开,不能就马上返回一个失败,比如在没有进程用只读方式打开时候,就会失败
1:open(fifo, O_RDONLY);
open(fifo, O_WRONLY);
不管先运行哪个,都会等另一个进程把对应连接打开时候才open结束
reader 的 open会等到writer的open 运行起来才open结束,
反之亦然
2:open(fifo, O_RDONLY|O_NONBLOCK ); 就不等待writer打开文件了
3:open(fifo , O_WRONLY|O_NONBLOCK):
如果没有reader打开该管道文件的话,就直接报错,退出进程
用perror去抓信息,得到的会是No such device or address
注意:
1:管道有大小限制(4096B), 在写入数据之前,会先判断管道大小是否足够,若不够就不会写入 -- 保证了数据的原子性
写满后只能等到有数据被读出才能继续写入
2: read读数据是一定要读到数据才能返回的,否则就会等待
3:全部进程中必须有一个reader是没有关闭的,才能write进数据;
如果reader全关了,write的时候内核会发一个信号 SIGPIPE, 进程对于SIGPIPE的默认操作是终止进程
4:如果writer设置成非阻塞, 就必须先运行reader,否则无法通信,见open(fifo , O_WRONLY|O_NONBLOCK):
unlink()删除已经不再需要使用的文件,避免造成垃圾文件,对应的是mkfifo创建
实例:
fifoReader.c
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#define FIFO "/root/c/process/ififo"
int main(void)
{
int fd;
char buf[128];
if(mkfifo(FIFO, 0666))//创建管道文件
{
perror("Mkfifo error");
}
printf("open for reading... \n");
// fd=open(FIFO,O_RDONLY);//阻塞
fd=open(FIFO,O_RDONLY|O_NONBLOCK);//非阻塞
/*
1:open(fifo, O_RDONLY);
open(fifo, O_WRONLY);
不管先运行哪个,都会等另一个进程把对应连接打开时候才open结束
reader 的 open会等到writer的open 运行起来才open结束,
反之亦然
2:open(fifo, O_RDONLY|O_NONBLOCK ); 就不等待writer打开文件了
3:open(fifo , O_WRONLY|O_NONBLOCK):
如果没有reader打开该管道文件的话,就直接报错,退出进程
用perror去抓信息,得到的会是No such device or address
得先运行reader
*/
printf("opened ... \n");
if(fd<0)
{
perror("Failed to open fifo:");
return -1;
}
while(1)
{
int count;
count=read(fd,buf,127);
//要用底层io。read()会返回实际读到的字节数
if(count>0)
{
buf[count]=0;//结束符,也可以='\0';
printf("fifoReader receive a string:%s\n",buf);
}
if(strncmp(buf,"exit",4)==0)
{
break;
}
}
close(fd);
return 0;
}
fifoWriter.c
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#define FIFO "/root/c/process/ififo"
int main(void)
{
int fd;
char buf[128];
if(mkfifo(FIFO, 0666))
{
perror("Mkfifo error");
}
printf("open for writing ... \n");
// fd=open(FIFO,O_WRONLY);// 阻塞
fd=open(FIFO,O_WRONLY|O_NONBLOCK);// 如果写端设置成非阻塞,不能先于读端运行,否则 open失败
printf("opened... \n");
if(fd<0)
{
perror("Failed to open fifo:");
return -1;
}
while(1)
{
fgets(buf,128,stdin);//标准输入内容
write(fd,buf,strlen(buf));//把缓存写入
if(strncmp(buf,"exit",4)==0)
{
break;
}
}
close(fd);
unlink(FIFO);
return 0;
}
区别:
无名管道
1: 亲缘关系进程中
2:没有一个具体的文件
3:调用write的时候必须保证有个fd[0]是没有closed
4:pipe 在fork前 ( 申请管道并打开)
5:不保证写入数据的原子性
有名管道
1: 任意两个
2:具体文件
3: 调用write的时候必须保证有个读端(RDONLY的方式)是没有closed
4:mkfifo (申请)
open(path, O_RDONLY) 阻塞, read 是阻塞的,会等有数据可读才返回
open(path,O_RDONLY | O_NONBLOCK) 不阻塞, read也不阻塞,没有数据返回是0
5:保证写入数据的原子性
6:有名管道文件不能在window中存在