Linux — 管道
引入管道:
每个进程各自有不同的PCB(task_struct),各自拥有不同的用户地址空间,所以进程的运行是具有独立性的,当操作系统需要完成某项功能时需要进程间进行交互,这时内核协助各进程完成相互访问。
管道的本质:
1.管道是内核开辟的专门用来进程间通信的一块文件缓冲区,一个进程连接到另一个进程在管道中传输的数据是以字符流的形式;
2.管道是单向的,一个进程在管道的一端进行数据的读取,另一个进程在管道的另一端进行数据的写入,当一个进程将管道中的数据读走之后,数据就从管道中删除;
3.管道内部自动提供同步机制,使读写有序;
4.当进程退出了管道的生命周期也就结束了,内核会对管道操作进行同步和互斥;
5.如果想要进行双向通信,需要建立两个管道。
管道的分类
管道分为匿名管道和命名管道。
匿名管道
匿名管道应用于具有血缘关系的父子进程之间,当一个进程创建一个管道之后,再进行fork创建子进程,这时父进程和子进程可以通过管道进行通信。
创建一个匿名管道:
函数—— int pipe(int fd[2])
参数:fd—代表文件描述符数组;fd[0]代表读端,fd[1]代表写端。
返回值:成功返回0,失败返回错误代码。
//mypipe.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main()
{
//创建管道
int fd[2];
if(pipe(fd)==-1){
perror("pipe"),exit(1);
}
//创建子进程
pid_t pid;
pid=fork();
if(pid==-1){
perror("fork"),exit(1);
}else if(pid==0){
//子进程在管道进行写数据
close(fd[0]);
size_t size=write(fd[1],"PIPE",4);
if(size==0){
perror("write"),exit(1);
}
close(fd[1]);
}else{
//父进程从管道读数据
close(fd[1]);
char buf[10]={0};
size_t size=read(fd[0],buf,10);
if(size==0){
perror("read"),exit(1);
}
close(fd[0]);
printf("%s\n",buf);
}
return 0;
}
顺便写一下Makefile:
//依赖文件
mypipe:mypipe.c
//编译
gcc -o mypipe mypipe.c
// gcc -o aaa@qq.com $^
.PHONY:clean //创建伪目标
clean:
//保证执行完之后删除目标文件
rm -f mypipe
父进程从管道中读出了子进程向管道中写的数据:
匿名管道的读写规则:
1.当所有管道写端对应的文件描述符被关闭,则read返回0;
2.当所有进程读端对应的文件描述符被关闭,则write操作返回信号SIG_PIPE,进而导致write进程退出;
3.当写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性;当写入的数据量大于PIPE_BUF时Linux将不能保证写入的原子性;
4.当管道里没有数据,进程有两种解决方案:进程暂停运行,read调用阻塞,直到有数据再停止阻塞;进程返回-1,erron的值为EAGAIN;
5.当管道中数据满的时候,进程有两种解决方案:进程暂停运行,write调用阻塞,直到有数据被读走;进程返回-1,erron的值为EAGAIN。
命名管道:
命名管道适用于不相关进程之间进行通信,是一种特殊的文件。
创建命名管道
利用命令进行创建:mkfifo
(提醒一下使用rm pipename
可以删除命名管道。)
利用函数进行创建:
int mkfifo(const char* filename,mode_t mode)
参数:filename是创建管道的名字,mode是文件权限(mode%~umask)。
函数调用成功返回0,失败返回-1
//server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
umask(0);
//创建管道
if(mkfifo("./myfifo",S_IFIFO|0666)==-1){
perror("myfifo"),exit(1);
}
int fd=open("./myfifo",O_RDONLY);
if(fd<0){
perror("open"),exit(1);
}
char buf[1024];
while(1){
//从管道中读数据,读成功输出
size_t size=read(fd,buf,sizeof(buf)-1);
if(size<0){
perror("read"),exit(1);
}else if(size==0){
printf("client is quit! server begin quit!\n ");
}else{
buf[size]=0;
printf("client:%s\n",buf);
}
}
close(fd);
}
//client.c
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
int main()
{
int fd=open("./myfifo",O_WRONLY);
if(fd<0){
perror("open"),exit(1);
}
char buf[1024];
while(1){
//从键盘输入,并刷新
printf("Please enter:\n");
fflush(stdout);
//将标准输入的数据读到数组中,读成功就写到管道
size_t size=read(0,buf,sizeof(buf)-1);
if(size>0){
buf[size]=0;
write(fd,buf,strlen(buf));
}else{
perror("write"),exit(1);
}
}
close(fd);
}
//Makefile
.PHONY:all
all:server client
server server.c
gcc -o aaa@qq.com $^
client client.c
gcc -o aaa@qq.com $^
.PHONY:clean
clean:
rm -f server client myfifo
如何测试管道的默认大小?
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
int fd[2];
int p=pipe(fd);
if(p==-1){
perror("pipe"),exit(1);
}
pid_t pid=fork();
long long count=0;
if(pid==-1){
perror("fork"),exit(1);
}else if(pid==0){
close(fd[0]);
char* msg="1";
while(1){
write(fd[1],msg,1);
++count;
printf("count=%d\n",count);
}
}else{
char buf[2];
while(1){
memset(buf,'\0',sizeof(buf));
}
}
return 0;
}
管道默认大小:
上一篇: git比较本地仓库和远程仓库的差异并更新本地仓库(实践版)
下一篇: git的安装
推荐阅读
-
在VMware虚拟机中安装redhat linux操作系统图文详解教程
-
在VMware虚拟机中安装Linux(Fedora16)的方法
-
虚拟机中的Linux安装VMware Tools的方法
-
虚拟机VMware中安装VMware Tools for Linux的方法(Fedora16)
-
在VMware中为Linux系统安装vmware tools的详解教程
-
Linux centos7环境下MySQL安装教程
-
ASP.NET Core程序发布到Linux生产环境详解
-
linux命令详解之useradd命令使用方法
-
使用scp获取远程linux服务器上的文件 linux远程拷贝文件
-
shell监控linux系统进程创建脚本分享