问题引出:为什么有了匿名管道还需要命名管道呢?
因为匿名管道是只用于具有亲缘关系的,例如父子进程间通信可以用匿名管道,但是如果要使得毫无关系的两个进程间进行通信,匿名管道就派不上用场了,就需要用命名管道来实现。命名管道是一种特殊的文件,它可以使的进程之间共享同一块资源,通过对该资源的读写来实现通信。命名管道解除了管道只能在具有亲缘关系的进程间使用的限制。
第一种:通过命令行方式进行创建
mkfifo +"文件名"方式
第二种:通过代码方式( 调用函数mkfifo() )创建
定义:
第一个参数是要创建的管道文件的路径(当前路径下的名称),第二个参数是权限的设置 一般都是0xxx的形式
返回值:如果创建成功返回0 不成功返回-1 可以以此来判断是否创建成功
例:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
int main()
{
if(makfifo("myfifo",0644)<0)
{
printf("mkfifo fail!\n");
return -1;
}
return 0;
}
只读打开用open(“文件名”,O_RDONLY);
只写打开用open(“文件名”,O_WRONLY);
打开还有O_CREAT
要实现不同的打开方式,使用 或 | 将上面的混合使用即可
//两个.c 文件 client.c serve.c 一个充当客户端 一个充当服务端 引用同一个头文件serve.h
//serve.h
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#define File_Name "fifo"
-----------------------------------
//serve.c
#include"sever.h"
int main()
{
if(mkfifo(File_Name,0644)<0)
{
perror("mkfifo");
return -1;
}
//通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
//接下来就是通过对管进行读写实现两个进程间的通信了
int fd = open(File_Name,O_RDONLY);//打开文件 只读
if(fd < 0)
{
perror("open");
return -2;
}
char srt[128];
while(1)
{
srt[0]=0;//清空文件
int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
if(s>0)//还没有读完的情况
{
srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0
printf("client send message# %s\n",srt);
}
else if(s==0)
{
printf("client quit!\n");
break;
}
else
{
//说明读取错误 返回的是-1
perror("read");
break;
}
}
close(fd);//打开了文件最后要将其关闭
return 0;
}
----------------------------------------
//client.c
#include"serve.h"
int main()
{
//先打开管道文件
int fd=open(File_Name,O_WRONLY);
if(fd<0)
{
perror("open");
return -1;
}
//打开了文件后就要从键盘读取数据 然后把读到的数据写到管道中去
char msg[128];
while(1)
{
msg[0]=0;
printf("please Enter# ");
fflush(stdout);//上面的打印没有带回车 全缓冲 所以要手动刷新缓冲区
int ret=read(0,msg,sizeof(msg)-1);
if(ret>0)
{
msg[ret]=0;
write(fd,msg,strlen(msg));
}
}
close(fd);
return 0;
}
代码:(改变的额只有服务端的代码,就是创建了一个子进程 然后用进程替换 把客户端发的字符当成命令来执行 只有非常小的改动)
#include"serve.h"
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
if(mkfifo(File_Name,0644)<0)
{
perror("mkfifo");
return -1;
}
//通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
//接下来就是通过对管进行读写实现两个进程间的通信了
int fd = open(File_Name,O_RDONLY);//打开文件 只读
if(fd < 0)
{
perror("open");
return -2;
}
char srt[128];
while(1)
{
srt[0]=0;//清空文件
int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
if(s>0)//还没有读完的情况
{
srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0
// printf("client send message# %s\n",srt);
printf("client send command/message# %s\n",srt);
fflush(stdout);
// 改变的地方 /
int pid = fork();//创建子进程来完成输入的命令
if(pid==0)
{
//child
execlp(srt,srt,NULL);//进程替换
exit(1);//子进程退出
}
waitpid(pid,NULL,0);//阻塞式等待子进程退出
}
else if(s==0)
{
printf("client quit!\n");
break;
}
else
{
//说明读取错误 返回的是-1
perror("read");
break;
}
}
close(fd);//打开了文件最后要将其关闭
return 0;
}
补充:
这里的进程间通信通过管道实现 其中的数据是不涉及磁盘的 也就是说数据没有被刷新到磁盘里,而是在内存中!!!!
代码:(改动的也只有服务端)
#include"serve.h"
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
if(mkfifo(File_Name,0644)<0)
{
perror("mkfifo");
return -1;
}
//通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
//接下来就是通过对管进行读写实现两个进程间的通信了
int fd = open(File_Name,O_RDONLY);//打开文件 只读
if(fd < 0)
{
perror("open");
return -2;
}
char srt[128];
while(1)
{
srt[0]=0;//清空文件
int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
if(s>0)//还没有读完的情况
{
srt[s-1]=0;//如果读了127 个字节就返回127 把改下标设为0 在打印出读到的内容 读是读s个 其中包括了\n 然后把 s-1 对应的\n 设为0
printf("client send message# %s\n",srt);
char* st=srt;
int flag=0;
while(*st)//找运算符号
{
switch(*st)
{
case'+':
flag=1;
break;
case'-':
flag=2;
break;
case'*':
flag=3;
break;
case'/':
flag=4;
break;
case'%':
flag=5;
break;
}
st++;
}
int a,b,c;
const char* str=" +-*/%";
if(flag)//如果找到了运算符号就进行拆分子串
{
char* s1=strtok(srt,str);
char* s2=strtok(NULL,str);
a=atoi(s1);//字符转数字
b=atoi(s2);
switch(flag)//计算
{
case 1:
c=a+b;
break;
case 2:
c=a-b;
break;
case 3:
c=a*b;
break;
case 4:
c=a/b;
break;
case 5:
c=a%b;
break;
}
printf("%d %c %d = %d\n",a,str[flag],b,c);//打印
}
}
else if(s==0)
{
printf("client quit!\n");
break;
}
else
{
//说明读取错误 返回的是-1
perror("read");
break;
}
}
close(fd);//打开了文件最后要将其关闭
return 0;
}
代码:
//客户端
#include"serve.h"
int main()
{
//先打开管道文件
int fd=open(File_Name,O_WRONLY);
if(fd<0)
{
perror("open");
return -1;
}
int fd1=open("file.txt",O_RDONLY);//打开文件 从管道中读取file.txt的数据 再往file-bk.txt里面写就可以实现文件的拷贝了
if(fd1<0)
{
perror("open file.txt");
return -2;
}
//打开了文件后就要从键盘读取数据 然后把读到的数据写到管道中去
char msg[128];
while(1)
{
msg[0]=0;
dup2(fd1,0);//重定向fd为0 也就是相当于把stdin关了 再创建file-bk 实现了从标准输入中读入数据就是从file-bk中读入数据
ssize_t ret=read(0,msg,sizeof(msg));
if(ret==sizeof(msg))
{
// msg[ret-1]=0;//设置\0使得文件可以在输出的时候可以结束
write(fd,msg,ret);//把数据写到新打开的文件中去
}
else if(ret<sizeof(msg))
{
write(fd,msg,ret);//把不足期望的大小的数据写到文件中
printf("read end of file!\n");
break;
}
else{
printf("read error!\n");
break;
}
}
close(fd);
close(fd1);
return 0;
}
//服务端
#include"serve.h"
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
if(mkfifo(File_Name,0644)<0)
{
perror("mkfifo");
return -1;
}
//通过mkfifo 创建了命名管道 接下来就是通过管道让服务端和客户端进行通信
//接下来就是通过对管进行读写实现两个进程间的通信了
int fd = open(File_Name,O_RDONLY);//打开文件 只读
if(fd < 0)
{
perror("open");
return -2;
}
int fd1=open("file-bk.txt",O_CREAT|O_WRONLY,0644);//以只读的方式打开已经存在的file.txt文件 一定是可以打开的 不用判断
char srt[128];
while(1)
{
srt[0]=0;
int s= read(fd,srt,sizeof(srt));//open的第一个参数是文件名 而read的第一个参数是文件描述符
if(s>0)//还没有读完的情况
{
// write(fd1,srt,sizeof(srt));
write(fd1,srt,s);//不是sizeof(srt)
}
else if(s==0)
{
printf("client quit!\n");
break;
}
else
{
//说明读取错误 返回的是-1
perror("read");
break;
}
}
close(fd);//打开了文件最后要将其关闭
close(fd1);
return 0;
}
总结:命名管道其实就是一个文件,只不过是操作系统对其进行了加工处理,使其具有特定的功能,这个管道文件就是在毫无关系的进程间共享的,使得毫无关系的进程能看到同一份资源,进程对该资源进行文件读写,实现信息的交互,达到通信的效果!!!(只要创建出了命名管道,接下来的操作就是对文件的读写处理了)