管道
目录
管道
1、管道的特点
2、无名管道的使用步骤
(1)在进程中使用 pipe 函数来获取管道的文件描述符
(2)使用 fork 函数来创建子进程
(3)通过获取到的文件描述符来进行数据的传输
(4)当管道不再使用的时候 需要进行关闭
3、有名管道的使用步骤
(1)创建一个有名管道出来
(2)打开有名管道 (有名管道的使用方式)
(3)通过打开的有名管道进行数据的操作
(4)关闭有名管道
4、删除有名管道函数
使用有名管道实现双向通信,用两根管道
引入:
-
信号可以实现进程间的交互 但是没法发送数据。
-
管道可以实现数据的传输。
-
linux 下一切都是文件,管道也是一种文件 我们可以通过文件操作来进行数据的传输
-- 什么是进程间通信?
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
1、管道的特点
-- 1 先进先出
- 数据被一个进程读出后,将被从管道中删除,其他读进程将不能再读到这些数据。管道的内容读完后不会保存。
-- 2 单向
- 数据的流通方向的单向的,一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
-- 3 管道为空
- 无法读出数据 会读阻塞(进程试图读空管道时,进程将阻塞)
-- 4 管道为满
-
写堵塞(管道已经满时,进程再试图向管道写入数据,进程将阻塞。)
-
管道大小 64KB
- 直接将1024这一块区域写进文件,并没有对该地址的空间做出改变,所以是可以的。
-- 5 管道有两种
-
无名管道
-- 没有名字的管道 只能让亲缘(父子)进程使用 -
有名管道
-- 在同一个操作系统下 用任意的两个进程都可以使用
-- 6 管道是单向的,一边要么读,一边要么写,不可以又读又写,想要一边读一边写,那就创建2个管道
-- 当进程退出之时,管道也随之释放,与文件保持一致
2、无名管道的使用步骤
(1)在进程中使用 pipe 函数来获取管道的文件描述符
-- 函数头文件
- #include <unistd.h>
-- 函数的原型
- int pipe(int pipefd[2]);
-- 函数的作用:
- 调用该函数可以创建一个无名管道用于父子进程间通信
- !!!该函数需要在 fork 之前来调用否则会创建两根管道
-- 函数的参数
- 函数的参数用于返回无名管道的读端口和写端口对应的文件描述符
- int pipefd[2]:一个 int 类型的数组
- pipefd[0] 对应读端口
- pipefd[1] 对应写端口
-- 函数的返回值:
- 成功返回:0
- 失败返回:-1
(2)使用 fork 函数来创建子进程
- pid_t fork(void);
(3)通过获取到的文件描述符来进行数据的传输
-- 因为我们获取的是文件描述符,所以可以直接使用 read 和 write 函数来进行数据的写入和读取
- ssize_t read(int fd, void *buf, size_t count)
- ssize_t write(int fd, const void *buf, size_t count)
注:任何的数据传输,接收方接收的数据类型尽量的要跟发送方的一致
(4)当管道不再使用的时候 需要进行关闭
-- 调用 close 函数即可
- 1 写端口彻底关闭 但是管道中还有数据 读端口可以正常读取 数据读取完毕之后 管道自动关闭
- 2 读端口彻底关闭 写端口不可以正常写入
- 要想彻底关闭管道,需要父子进程都进行 close 操作
父进程关闭读写端口,子进程关闭读写端口
-- 向管道中写数据,从管道中读数据
3、有名管道的使用步骤
-- 因为无名管道使用范围有限(只能在父子进程之间),为了让任意的两个进程都可以使用管道通信,我们就可以用有名管道.
-- 有名管道的特点跟无名管道无区别
(1)创建一个有名管道出来
-- 使用指令创建
-- 使用函数创建
-- 函数头文件
- #include <sys/types.h>
- #include <sys/stat.h>
-- 函数原型
- int mkfifo(const char *pathname, mode_t mode)
-- 函数的作用:
- 根据参数来创建一个有名管道文件
-- 函数的参数:
- pathname:路径和文件名
- mode:有名管道的文件权限
-- 函数的返回值:
- 成功返回 0
- 失败返回 -1
(2)打开有名管道 (有名管道的使用方式)
- 使用 open 函数来对有名管道进行打开
- 有名管道必须要让两个进程同时调用 open 函数才可以打开成功
- 而且一个进程以只读的方式 一个进程以只写的方式
- 要想打开有名管道 必须要凑够可读可写这两个权限
(3)通过打开的有名管道进行数据的操作
- read 和 write 函数来进行数据的写入和读取
- ssize_t read(int fd, void *buf, size_t count)
- ssize_t write(int fd, const void *buf, size_t count)
- 任何的数据传输,接收方接收的数据类型尽量的要跟发送方的一致
(4)关闭有名管道
- 读端先关闭 写端无法使用
- 写端先关闭 但是管道中有数据 读端口仍可进行读取
4、删除有名管道函数
-- 函数头文件
- #include <unistd.h>
-- 函数原型
- int unlink(const char *pathname)
-- 函数的作用
- 删除有名管道文件
-- 函数的参数:
- pathname:填写有名管道文件
-- 函数的返回值:
- 成功返回 0
- 失败返回 -1
使用有名管道实现双向通信,用两根管道
-- rr.c
#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "unistd.h"
int main(int argc,char*argv[])
{
if(argc<3)
{
printf("格式输入有误!\n");
return -1;
}
char buff1[30]={0};
char buff2[30]={0};
pid_t pid = fork();
if(pid == 0)
{
while(1){
//sleep(1);
int fd_r = open(argv[1],O_RDONLY);
int ret = read(fd_r,buff1,30);
if(ret>0){
printf("接收到%d个字节\n",ret);
printf("buff1:%s\n",buff1);
}else {
break;
}
memset(buff1,0,30);
close(fd_r);
}
}else if(pid >0){
while(1){
int fd_w = open(argv[2],O_WRONLY);
scanf("%s",buff2);
write(fd_w,buff2,strlen(buff2));
perror("write");
close(fd_w);
}
}
return 0;
}
-- ww.c
#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "unistd.h"
#include <string.h>
int main(int argc,char*argv[])
{
if(argc<3)
{
printf("格式输入有误!\n");
return -1;
}
char buff1[30] ={0};
char buff2[30]={0};
pid_t pid =fork();
if(pid == 0){
while(1){
int fd_w = open(argv[1],O_WRONLY);
scanf("%s",buff1);
write(fd_w,buff1,strlen(buff1));
perror("write");
close(fd_w);
}
}else if(pid > 0){
while(1){
int fd_r = open(argv[2],O_RDONLY);
//sleep(2);
int ret = read(fd_r,buff2,30);
if(ret>0)
{
printf("接收到%d个字节\n",ret);
printf("buff2:%s\n",buff2);
}else {
break;
}
memset(buff2,0,30);
close(fd_r);
}
}
return 0;
}