Linux0.12源码阅读 —— 管道

参考资料

1.Linux内核完全注释 v5.0修正版

实验环境

bochs模拟x86硬件平台下的Linux0.12操作系统
实验环境地址:http://www.oldlinux.org/Linux.old/bochs/ ,该路径下选择一个Linux0.12版本就可进行实验。

函数声明

管道的创建是通过sys_pipe()系统调用实现的,其声明如下:

  创建管道系统调用。

 //fildes用于返回创建的读写文件句柄(描述符)。这对文件句柄指向同一管道i节点。

 // 参数:filedes -文件句柄数组。fildes[0]用于读管道数据,fildes[1]向管道写入数据。

 // 成功时返回0,出错时返回-1。

 int sys_pipe(unsigned long * fildes);

执行流程

  • 获取一空闲 i 节点以及一页空闲物理页用作管道缓冲, i 节点的i_size指向该物理页,i_zone[0]用作管道缓冲头指针,i_zone[1]用作管道缓冲尾指针。
  • 从struct file file_table[NR_FILE]获取两空闲项,这两项都指向上面申请得到的i节点,其中一项用作读操作,另一项用作写操作。
  • 从第一项开始搜索任务结构体的struct file * filp[NR_OPEN]以获取两空闲项,并让这两项分别指向上面得到的file结构体。
  • 搜索filp得到的第一个空闲项用作管道读句柄,第二个用作管道写句柄。

注意:在申请管道句柄时并没有置上close_on_exec,这也就意味着管道句柄在子进程执行execve后仍然有效。

 

代码测试

测试内容:父进程创建管道并向管道写数据,子进程执行execve函数后从管道读数据。

/* @file pipe.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

void warn(char *s)
{
	printf("%s,errno:%d\n",s,errno);
}

/* 父进程向管道写的数据 */
char display[]="hello world";
#define display_len (sizeof(display))

/* 子进程从管道读出数据时所用的缓冲 */
char display2[display_len];

void main(int argc,char **argv)
{
	/* 存放申请的管道句柄 */
	int fd[2];
	/* 用于构建子进程执行execve时所需要的参数char **argv */
	char child_argv1[2]={0,0};
	char child_argv2[2]={0,0};
	char *child_argv[3]={"",child_argv1,child_argv2};
	
	/* 打印下执行的进程,以此区分是子进程还是父进程在执行 */
	printf("pid:%d\n",getpid());
	
	/* 执行父进程时,我们没有给额外的参数,因此*(argv+1)应该 */
	/* 为null,不为null说明是子进程。 */
	if(*(argv+1))
	{
		printf("child *********************\n");
		printf("pid:%d\n",getpid());
		
		/* 解析子进程执行execve函数时给定的参数 */
		/* 这两个参数分别是管道读、写句柄 */
		fd[0]=(int)**(argv+1);
		fd[1]=(int)**(argv+2);
		/* 查看这两个句柄是否和父进程的一致 */
		printf("read fd:%d,write fd:%d\n",fd[0],fd[1]);
		
		/* 从管道读数据 */
		if(read(fd[0],display2,display_len)<0)
		{
			warn("read error");
		}
		printf("%s\n",display2);
		while(1);
	}
	
	/* 创建管道 */
	if(pipe(fd)<0)
	{
		warn("pipe error");
	}
	/* 查看创建的管道句柄 */
	printf("read fd:%d,write fd:%d\n",fd[0],fd[1]);
	
	/* 把读写句柄号作为参数给到子进程 */
	child_argv1[0]=fd[0];
	child_argv2[0]=fd[1];
	
	/* 向管道写数据 */
	write(fd[1],display,display_len);
	
	if(fork()==0)
	{
		/* 加载子进程执行文件 */
		/* 这里的执行文件还是本执行文件,即父进程和子进程是同一执行文件 */
		execve(*argv,child_argv,0);	
		warn("execve error");
	}
	while(1);
}

 

执行结果

从执行结果看出:

  • 以./pipe执行不要带任何的参数,这样 *(argv+1) 就能以此区分父进程和子进程。
  • 父进程pid为15,创建的管道读句柄为3,写句柄为4。
  • 子进程pid为16,从参数中解析出的管道句柄和父进程一致,读句柄为3,写句柄为4。
  • 子进程成功地从管道读出了相应的数据。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值