通信的本质是传递数据,是相互的。进程之间不能“直接”相互传递数据,因为进程具有独立性,所有的数据操作都会发生写时拷贝,所以一定要通过媒介的方式来传播。 什么是管道?管道是Unix最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。
一.匿名管道
供具有血缘关系的进程进行进程间通信,常见于父子进程。 1.父进程创建管道 以读方式打开一次,以写方式打开一次,文件描述符3,4就会指向同一个文件 2.父进程fork出子进程 子进程写时拷贝父进程 3.父进程关闭fd[0],子进程关闭fd[1] 父子进程各自关闭不需要的文件描述符,来达到构建单向通信的信道目的,父进程写入,子进程读取 为什么曾经读写端都要打开?因为不打开rw,子进程拿到的文件打开方式必定和父进程一样,两个都是r或者两个都是w,无法通信;父进程w子进程r或者子进程w父进程r更加灵活。 为什么一定要关闭呢?因为放置误操作。 管道为什么只能单向通信呢?因为读写位只有一个。
例子:从键盘读取数据,写入管道,读取管道,写到屏幕
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( void )
{
int fds[2];
char buf[100];
int len;
if ( pipe(fds) == -1 )
perror("make pipe"),exit(1);
while ( fgets(buf, 100, stdin) ) {
len = strlen(buf);
if ( write(fds[1], buf, len) != len ) {
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
if ( (len=read(fds[0], buf, 100)) == -1 ) {
perror("read from pipe");
break;
}
if ( write(1, buf, len) != len ) {
perror("write to stdout");
break;
}
}
}
二.匿名管道的特性
如果管道里面没有消息,父进程(读端)就会等待,等子进程写入,管道内部有数据就绪;如果管道里面写端已经写满了,不能继续写了,等待父进程读取,管道内部有空闲空间;如果读端在读,但是写端不写并且关闭,读端读取到0,文件结束;如果读端不读,而且读端关闭,一直写毫无意义,一直写本质就是在浪费系统资源,写进程会立马被OS终止掉,此时写进是子进程,通过发送信号的方式 (1)管道自带同步机制 (2)管道是单向通信的 (3)管道是面向字节流的 (4)管道只能保证是具有血缘关系的进程通信,常用于父子 (5)管道可以保证一定程度的数据读取的原子性 (6)进程退出,曾经打开的文件也会被关掉,管道也是文件,管道的声明周期是随进程的
三.命名管道
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:$ mkfifo filename 命名管道也可以从程序里创建,相关函数有:int mkfifo(const char *filename,mode_t mode); 创建命名管道:
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}
匿名管道和命名管道的区别: (1)匿名管道由pipe函数创建并打开。 (2)命名管道由mkfifo函数创建,打开用open (3)FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
四.用命名管道实现server&client通信
# ll
total 12
-rw-r--r--. 1 root root 46 Sep 18 22:37 clientPipe.c
-rw-r--r--. 1 root root 164 Sep 18 22:37 Makefile
-rw-r--r--. 1 root root 46 Sep 18 22:38 serverPipe.c
# cat Makefile
.PHONY:all
all:clientPipe serverPipe
serverPipe.c
clientPipe:clientPipe.c
gcc -o $@ $^
serverPipe:serverPipe.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f clientPipe serverPipe
serverPipe.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
umask(0);
if(mkfifo("mypipe", 0644) < 0){
ERR_EXIT("mkfifo");
}
int rfd = open("mypipe", O_RDONLY);
if(rfd < 0){
ERR_EXIT("open");
}
char buf[1024];
while(1){
buf[0] = 0;
printf("Please wait...\n");
ssize_t s = read(rfd, buf, sizeof(buf)-1);
if(s > 0 ){
buf[s-1] = 0;
printf("client say# %s\n", buf);
}else if(s == 0){
printf("client quit, exit now!\n");
exit(EXIT_SUCCESS);
}else{
ERR_EXIT("read");
}
}
close(rfd);
return 0;
}
clientPipe.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
int wfd = open("mypipe", O_WRONLY);
if(wfd < 0){
ERR_EXIT("open");
}
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 if(s <= 0){
ERR_EXIT("read");
}
}
close(wfd);
return 0;
}
|