麒麟在线

当前位置:网站首页 / 技术 / 正文

Linux系统的/dev/zero文件是什么,怎么实现的

/dev/zero概述

在类UNIX 操作系统中, /dev/zero 是一个特殊的文件,当你读它的时候,它会提供无限的空字符(NULL, ASCII NUL, 0x00)。其中的一个典型用法是用它提供的字符流来覆盖信息,另一个常见用法是产生一个特定大小的空白文件。BSD就是通过mmap把/dev/zero映射到虚地址空间实现共享内存的。可以使用mmap将/dev/zero映射到一个虚拟的内存空间,这个操作的效果等同于使用一段匿名的内存(没有和任何文件相关)。

操作系统给用户新分配的内容(通过mmap或者brk)都是清零过的,但是这些虚拟地址通常都是按需分配物理页面。这里的“按需”的需求可能是读取,也可能是写入。如果只是读取,只要保证读取内容是零即可,在MMU的基础上,可以让“所有”虚拟地址都映射到内容为0的物理页面中。这样如果申请的内存大部分情况下都只是读取操作,那么并不会增加系统物理内存消耗量。

dd if=/dev/zero of=sun.txt bs=1M count=1
该命令创建了一个 1M 大小的文件 sun.txt,并将文件清零,其中参数解释:

if 代表输入文件。如果不指定 if,默认就会从 stdin 中读取输入。
of 代表输出文件。如果不指定 of,默认就会将 stdout 作为默认输出。
bs 代表字节为单位的块大小。
count 代表被复制的块数。
/dev/zero 是一个字符设备,会不断返回 0 值字节(\0)。

linux万物皆是文件:

  1. Linux内核对世界的定义,是把CPU认为是自我,除此以外所有的东西,都是外部。

  2. 内核与自我的交互,通过直接的机器指令实现,例如陷入中断(int)等。

  3. 内核与外部的交互,是通过把所有的外部事物抽象成统一的文件对象来实现的。内存是个文件(/dev/mem),硬盘是个文件(/dev/sda1),硬盘上的一个数据块是个文件(/home/xxx.dat,此处很神奇,需要细细品),鼠标是个文件(/dev/psaux),网卡、光盘等等都是文件。凡是支持读数据和写数据的东西,都是文件,万物皆文件。所有的文件组成了内核的虚拟文件系统(VFS),这个系统中记录内核可以读和写的所有文件。

  4. 文件这个概念这么好用,于是内核开始疯狂的使用文件概念来实现特定功能。需要个读出来都是空数据、写入数据没有任何side-effect的文件,就命名为/dev/null;需要个读出来都是0字符的文件,就是/dev/zero;等等。

2 /dev/zero的实现

2.1 shmem_zero_setup

/**
 * shmem_zero_setup - setup a shared anonymous mapping
 * @vma: the vma to be mmapped is prepared by do_mmap_pgoff
 */
int shmem_zero_setup(struct vm_area_struct *vma)
{
	struct file *file;
	loff_t size = vma->vm_end - vma->vm_start;
	/*
	 * Cloning a new file under mmap_sem leads to a lock ordering conflict
	 * between XFS directory reading and selinux: since this file is only
	 * accessible to the user through its mapping, use S_PRIVATE flag to
	 * bypass file security, in the same way as shmem_kernel_file_setup().
	 */
	file = __shmem_file_setup("dev/zero", size, vma->vm_flags, S_PRIVATE);
	vma->vm_file = file;
	vma->vm_ops = &shmem_vm_ops;
	return 0;
}

2.2 shmem_kernel_file_setup

struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags)
{
	return __shmem_file_setup(shm_mnt, name, size, flags, S_PRIVATE);
}

static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, loff_t size,
				       unsigned long flags, unsigned int i_flags)
{
	struct file *res;
	struct inode *inode;
	struct path path;
	struct super_block *sb;
	struct qstr this;

	shmem_acct_size(flags, size);
	this.name = name;
	this.len = strlen(name);
	this.hash = 0; /* will go */
	sb = mnt->mnt_sb;
	path.mnt = mntget(mnt);
	path.dentry = d_alloc_pseudo(sb, &this);//虚拟dentry
	d_set_d_op(path.dentry, &anon_ops);
	inode = shmem_get_inode(sb, NULL, S_IFREG | S_IRWXUGO, 0, flags);//获取file	inode->i_flags |= i_flags;
	d_instantiate(path.dentry, inode);
	inode->i_size = size;
	clear_nlink(inode);	/* It is unlinked */
	ramfs_nommu_expand_for_mapping(inode, size);
	res = alloc_file(&path, FMODE_WRITE | FMODE_READ, &shmem_file_operations);
	return res;
}

2.3 alloc_file

struct file *alloc_file(const struct path *path, fmode_t mode, const struct file_operations *fop)
{
	struct file *file;

	file = get_empty_filp();
	file->f_path = *path;
	file->f_inode = path->dentry->d_inode;
	file->f_mapping = path->dentry->d_inode->i_mapping;
	file->f_wb_err = filemap_sample_wb_err(file->f_mapping);
	if ((mode & FMODE_READ) &&
	     likely(fop->read || fop->read_iter))
		mode |= FMODE_CAN_READ;
	if ((mode & FMODE_WRITE) &&
	     likely(fop->write || fop->write_iter))
		mode |= FMODE_CAN_WRITE;
	file->f_mode = mode;
	file->f_op = fop;
	if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
		i_readcount_inc(path->dentry->d_inode);
	return file;
}

struct file *get_empty_filp(void)
{
	const struct cred *cred = current_cred();
	struct file *f;
	//f 清零
	f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
	percpu_counter_inc(&nr_files);
	f->f_cred = get_cred(cred);
	error = security_file_alloc(f);
	atomic_long_set(&f->f_count, 1);
	rwlock_init(&f->f_owner.lock);
	spin_lock_init(&f->f_lock);
	mutex_init(&f->f_pos_lock);
	eventpoll_init_file(f);
	return f;
}

3 mmap共享匿名映射

mmap 的共享匿名映射其实本质上还是共享文件映射,只不过这个文件比较特殊,创建于 dev/zero 目录下的 tmpfs 文件系统中。

unsigned long mmap_region(struct file *file, unsigned long addr,
		unsigned long len, vm_flags_t vm_flags, unsigned long pgoff,
		struct list_head *uf)
{
	if (file) {
		...
	} else if (vm_flags & VM_SHARED) {
		error = shmem_zero_setup(vma);
	}
}

推荐阅读

文章标签:

版权声明: 本文除特别说明外均由原创

本文链接: https://www.70ol.com/jishu/298.html,尊重共享,欢迎转载,请自觉添加本文链接,谢谢!

分享本文: 请填写您的分享代码。

呃 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。