Linux内核源代码情景分析-进程间通信-命名管道

    建立命名管道,mknod mypipe p。命名管道存在硬盘上,而管道不是。

    通过open打开这个命名管道,在内核中通过sys_open()实现,filename是"***/mypipe "。

    相关部分,请参考Linux内核源代码情景分析-文件的打开。

    sys_open进入filp_open,然后在open_namei中调用一个函数path_walk(),根据文件的路径名在文件系统中找到代表这个文件的inode。在将磁盘上的inode读入内存时,要根据文件的类型(FIFO文件的S_IFIFO标志位为1),将inode中的i_op指针和i_fop指针设置成指向相应的inode_operations数据结构和file_operations数据结构,但是对于像FIFO这样的特殊文件则调用init_special_inode()来加以初始化。这段代码在ext2_read_inode()中,如下:

if (inode->i_ino == EXT2_ACL_IDX_INO ||
	    inode->i_ino == EXT2_ACL_DATA_INO)
		/* Nothing to do */ ;
	else if (S_ISREG(inode->i_mode)) {
		inode->i_op = &ext2_file_inode_operations;
		inode->i_fop = &ext2_file_operations;
		inode->i_mapping->a_ops = &ext2_aops;
	} else if (S_ISDIR(inode->i_mode)) {
		inode->i_op = &ext2_dir_inode_operations;
		inode->i_fop = &ext2_dir_operations;
	} else if (S_ISLNK(inode->i_mode)) {
		if (!inode->i_blocks)
			inode->i_op = &ext2_fast_symlink_inode_operations;
		else {
			inode->i_op = &page_symlink_inode_operations;
			inode->i_mapping->a_ops = &ext2_aops;
		}
	} else 
		init_special_inode(inode, inode->i_mode,
				   le32_to_cpu(raw_inode->i_block[0]));
    只要文件的类型不是ACL索引活数据,不是普通文件,不是目录,不是符号连接,就属于特殊文件,就要通过init_special_inode()来初始化其inode结构:

    sys_open()>filp_open()>open_namei()>path_walk()>real_lookup()>ext2_lookup()>iget()>get_new_inode() >ext2_read_inode()>init_special_inode()

void init_special_inode(struct inode *inode, umode_t mode, int rdev)
{
	inode->i_mode = mode;
	if (S_ISCHR(mode)) {
		inode->i_fop = &def_chr_fops;
		inode->i_rdev = to_kdev_t(rdev);
	} else if (S_ISBLK(mode)) {
		inode->i_fop = &def_blk_fops;
		inode->i_rdev = to_kdev_t(rdev);
		inode->i_bdev = bdget(rdev);
	} else if (S_ISFIFO(mode))
		inode->i_fop = &def_fifo_fops;
	else if (S_ISSOCK(mode))
		inode->i_fop = &bad_sock_fops;
	else
		printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
}

    显然,对于FIFO文件,其inode结构中的inode_operations结构指针i_op为0,而file_operations结构指针i_fop则指向def_fifo_fops,定义如下:

struct file_operations def_fifo_fops = {
	open:		fifo_open,	/* will set read or write pipe_fops */
};
    与管道pipe文件的inode结构作一比较,就可以看出对于pipe文件中的inode结构并没有走过这么一个过程,与init_special_inode()也豪无关系。这是因为pipe文件的inode结构不是通过ext2_read_inode()从磁盘上读入,而是临时生成出来的(因而是无名的、无形的)。

    随后,在dentry_open中将inode结构中的这个file_operations结构指针复制到file数据结构中。这样,对于命名管道,在打开文件时经由数据结构def_fifo_fops,就可以得到函数指针fifo_open,从而进入函数fifo_open(),代码如下:

    f->f_op = fops_get(inode->i_fop);//f->f_op被赋值为inode_i_fop  
    if (inode->i_sb)  
        file_move(f, &inode->i_sb->s_files);//将其从中间队列脱链而挂入该文件所在设备的super_block结构中的file结构队列s_files  
    if (f->f_op && f->f_op->open) {  
        error = f->f_op->open(inode,f);  
        if (error)  
            goto cleanup_all;  
    } 
    f->f_op->open(inode,f)对应fifo_open,代码如下:
static int fifo_open(struct inode *inode, struct file *filp)
{
	int ret;

	ret = -ERESTARTSYS;
	lock_kernel();
	if (down_interruptible(PIPE_SEM(*inode)))
		goto err_nolock_nocleanup;

	if (!inode->i_pipe) {
		ret = -ENOMEM;
		if(!pipe_new(inode))//当首次打开这个FIFO文件的进程来到fifo_open时,该管道的缓冲页面尚未分配,所以通过pipe_new分配所需的pipe_inode_info数据结构和缓冲页面,以后再打开同一FIFO文件的进程就会跳过这一段
			goto err_nocleanup;
	}
	filp->f_version = 0;

	switch (filp->f_mode) {
	case 1://读端
	/*
	 *  O_RDONLY
	 *  POSIX.1 says that O_NONBLOCK means return with the FIFO
	 *  opened, even when there is no process writing the FIFO.
	 */
		filp->f_op = &read_fifo_fops;//和写端不同
		PIPE_RCOUNTER(*inode)++;//(inode).i_pipe->r_counter++
		if (PIPE_READERS(*inode)++ == 0)//(inode).i_pipe->readers++
			wake_up_partner(inode);//如果写端已经打开,那么读端的打开就完成了命名管道的建立过程。在这种情况下写端的进程,也就是"生产者"进程,一般都是正在睡眠中,等待着命名管道建立过程的完成,所以要把它唤醒

		if (!PIPE_WRITERS(*inode)) {//(inode).i_pipe->writer
			if ((filp->f_flags & O_NONBLOCK)) {
				/* suppress POLLHUP until we have
				 * seen a writer */
				filp->f_version = PIPE_WCOUNTER(*inode);
			} else 
			{
				wait_for_partner(inode, &PIPE_WCOUNTER(*inode));//如果命名管道的写端尚未打开,读端的打开只是完成了命名管道建立过程的一半,所以"消费者"进程要通过wait_for_partner()进入睡眠,等待某个"生产者"进程来打开命名管道的写端以完成其建立过程
				if(signal_pending(current))
					goto err_rd;
			}
		}
		break;
	
	case 2://写端
	/*
	 *  O_WRONLY
	 *  POSIX.1 says that O_NONBLOCK means return -1 with
	 *  errno=ENXIO when there is no process reading the FIFO.
	 */
		ret = -ENXIO;
		if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode))
			goto err;

		filp->f_op = &write_fifo_fops;//和读端不同
		PIPE_WCOUNTER(*inode)++;//(inode).i_pipe->w_counter++
		if (!PIPE_WRITERS(*inode)++)//(inode).i_pipe->writer++
			wake_up_partner(inode);//如果读端已经打开,那么写端的打开就完成了命名管道的建立过程。在这种情况下读端的进程,也就是"消费者"进程,一般都是正在睡眠中,等待着命名管道建立过程的完成,所以要把它唤醒

		if (!PIPE_READERS(*inode)) {//(inode).i_pipe->reader
			wait_for_partner(inode, &PIPE_RCOUNTER(*inode));//如果命名管道的读端尚未打开,写端的打开只是完成了命名管道建立过程的一半,所以"生产者"进程要通过wait_for_partner()进入睡眠,等待某个"消费者"进程来打开命名管道的读端以完成其建立过程
			if (signal_pending(current))
				goto err_wr;
		}
		break;
	
	case 3:
	/*
	 *  O_RDWR
	 *  POSIX.1 leaves this case "undefined" when O_NONBLOCK is set.
	 *  This implementation will NEVER block on a O_RDWR open, since
	 *  the process can at least talk to itself.
	 */
		filp->f_op = &rdwr_fifo_fops;

		PIPE_READERS(*inode)++;
		PIPE_WRITERS(*inode)++;
		PIPE_RCOUNTER(*inode)++;
		PIPE_WCOUNTER(*inode)++;
		if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1)
			wake_up_partner(inode);
		break;

	default:
		ret = -EINVAL;
		goto err;
	}

	/* Ok! */
	up(PIPE_SEM(*inode));
	unlock_kernel();
	return 0;

err_rd:
	if (!--PIPE_READERS(*inode))
		wake_up_interruptible(PIPE_WAIT(*inode));
	ret = -ERESTARTSYS;
	goto err;

err_wr:
	if (!--PIPE_WRITERS(*inode))
		wake_up_interruptible(PIPE_WAIT(*inode));
	ret = -ERESTARTSYS;
	goto err;

err:
	if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
		struct pipe_inode_info *info = inode->i_pipe;
		inode->i_pipe = NULL;
		free_page((unsigned long)info->base);
		kfree(info);
	}

err_nocleanup:
	up(PIPE_SEM(*inode));

err_nolock_nocleanup:
	unlock_kernel();
	return ret;
}

你可能感兴趣的:(Linux内核源代码情景分析)