栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

c语言系统编程六:Linux进程间通信之无名管道

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c语言系统编程六:Linux进程间通信之无名管道

Linux进程间通信之无名管道
  • 一 文件描述符复制
    • 1.1 dup函数(复制文件描述符)
    • 1.2 dup2函数(复制文件描述符)
  • 二 无名管道的概述
  • 三 无名管道的特点
  • 四 无名管道的创建和使用
    • 4.1 无名管道创建函数pipe
    • 4.2 无名管道使用实例一
    • 4.3 无名管道的读写特点
    • 4.4 无名管道的默认大小
    • 4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash

一 文件描述符复制

让新的文件描述符指向旧的文件描述符,新旧文件描述符指向同一文件;
使用函数dup,dup2;

1.1 dup函数(复制文件描述符)
  1. 需要的头文件和函数原型
#include 

int dup(int oldfd);
int dup2(int fildes, int fildes2);

  1. 函数功能
    从系统中寻找最小的可用文件描述符,作为已有文件描述符oldfd的副本,新文件描述符和旧文件描述符指向同一文件,新文件描述符提供dup的返回值返回。

  2. dup函数案例

#include 
#include 
#include 
#include 

int main(int arg,char *args[])
{
	int newfd = 0;
	newfd = dup(1);
	if(newfd == -1)
	{
		perror("dup");
		return -1;
	}
	printf("新文件描述符是:%dn",newfd);
	printf("向文件描述符1输出n");
	write(newfd,"向新文件描述符输出n",strlen("向新文件描述符输出n"));
	while(1)
	{
		sleep(1);
	}
	return 0;

}

1.2 dup2函数(复制文件描述符)
  1. 需要的头文件和函数原型
#include 

int dup2(int oldfd, int newfd);

  1. 功能
    将newfd作为oldfd的副本。
    如果newfd事先存在,dup2会先close掉newfd,然后将newfd作为oldfd的副本。

  2. dup2函数实例

#include 
#include 
#include 
#include 

int main(int arg,char *args[])
{
	int newfd = 0;
	newfd = dup2(1,100);
	if(newfd == -1)
	{
		perror("dup");
		return -1;
	}
	printf("新文件描述符是:%dn",newfd);
	printf("向文件描述符1输出n");
	write(newfd,"向新文件描述符输出n",strlen("向新文件描述符输出n"));
	while(1)
	{
		sleep(1);
	}
	close(newfd);
	return 0;

}

二 无名管道的概述

管道(pipe)又称无名管道。无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符,实际上它是一块逻辑内存。

三 无名管道的特点
  1. 半双工,创建一个管道,这个管道上数据只能在一个方向上流动;
  2. 数据只能从管道的一端写入,从另一端读出;
  3. 写入管道中的数据遵循先入先出的规则;
  4. 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节一个消息等;
  5. 管道不是普通文件,不属于某个文件系统,其只存在于内存中;
  6. 管道在内存中对应一个缓冲区,不同的系统其大小不一定相同;
  7. 从管道读取数据是一次性操作,数据一旦被读走,他就从管道中被抛弃,释放空间以便写更多的数据;
  8. 管道没有名字,只能在有血缘关系的进程之间使用。
四 无名管道的创建和使用 4.1 无名管道创建函数pipe
  1. 需要的头文件和函数原型
#include  
  1. 函数功能
    经由参数filedes返回两个文件描述符

  2. 参数

filedes为int型数组的首地址,其中存放了管道文件描述符fd[0],fd[1];
filedes[0]为读而打开;filedes[1]为写而打开管道。filedes[0]的输出是filedes[1]的输入。
  1. 返回值
成功:返回0
失败:返回-1
  1. 注意
    使用无名管道时,必须事先确定谁发,谁收的问题。
4.2 无名管道使用实例一


#include 
#include 
#include 
#include 
#include 
#include 

int main(int arg, char *argv[])
{
	//创建无名管道
	int fd[2];
	pipe(fd);

	//创建一个子进程
	//父进程发;子进程收
	pid_t pid = fork();
	if(pid == 0) //子进程
	{
		//子进程写端无意义(可以事先关闭)
		close(fd[1]);
		//子进程接收父进程消息
		printf("子进程%d正在等待父进程的消息n",getpid());
		unsigned char buf[128] = "";
		read(fd[0],buf,sizeof(buf));
		printf("子进程%d读到的消息为:%sn",getpid(),buf);
		//子进程读完数据,应该关闭读端
		close(fd[0]);
		//显示退出
		_exit(-1);
	}
	else if(pid > 0) //父进程
	{
		//父进程的读端无意义(可以事先关闭)
		close(fd[0]);
		//写端写入数据
		printf("父进程:%d 3秒后写入数据hello pipen",getpid());
		sleep(3);
		write(fd[1],"hello pipe",strlen("hello pipe"));
		printf("父进程:%d完成写入n",getpid());
		//通信完成,应该关闭写端
		close(fd[1]);
		//等待子进程退出
		wait(NULL);
	}
	return 0;
}

4.3 无名管道的读写特点
  1. 默认用read函数从管道中读数据是阻塞的;
  2. 用write函数向管道中写数据,当缓冲区已满时也是阻塞的;
  3. 通信过程中如果读端口全部关闭后,写进程向管道写数据时,写进程会收到SIGPIPE信号而退出;
  4. 可以通过fcntl函数来设置管道的阻塞特性:
设置为非阻塞:fcntl(fd,FSETFL, O_NONBLOCK);
设置为阻塞:fcntl(fd,FSETFL, 0);
4.4 无名管道的默认大小
  1. 用命令ulimit -a 查看系统中无名管道的默认大小

  2. 验证无名管道有大小限制

4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash

#include 
#include 
#include 
#include 
#include 
#include 

int main(int arg, char *argv[])
{
	//创建无名管道
	int fd[2];
	int res = 0;
	res = pipe(fd);
	if(res == -1)
	{
		perror("pipe");
		return 1;
	}
	//创建两个子进程
	int i=0;
	for(i=0;i<2;i++)
	{
		pid_t pid = fork();
		if(pid ==  0)
			break;
	}
	if(i==0) //子进程1
	{
		//ps -A 写端
		//关闭管道的读端口
		close(fd[0]);
		//将管道的读端口的文件描述符复制给文件描述符0
		int res1 = dup2(fd[1],1);
		if(res1 == -1)
		{
			perror("dup2");
			return 1;
		}
		int res2 = execl("/bin/ps","ps","-A",NULL);
		if(res2 == -1)
		{
			perror("execl");
		}
		_exit(0);


	}
	else if(i == 1)//子进程2
	{
		//子进程2负责执行grep bash
		//关闭管道的读端口
		close(fd[1]);
		//将管道的写端口的文件描述符复制给文件描述符1
		int res1 = dup2(fd[0],0);
		if(res1 == -1)
		{
			perror("dup2");
			return 1;
		}

		int res2 = execl("/bin/grep","grep","bash",NULL);
		if(res2 == -1)
			perror("exec");
		_exit(0);

	}
	else if(i == 2)//父进程
	{
		//关闭管道读写端口
		close(fd[0]);
		close(fd[1]);
		while(1)
		{
			pid_t pid = waitpid(-1,NULL,WNOHANG);
			if(pid>0)
			{
				printf("子进程%d被主进程回收n",pid);
			}
			else if(pid == 0)
			{
				continue;
			}
			else if(pid<0)
			{
				break;
			}
		}
	}
	return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/971428.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号