linux内核中的文件描述符(六)--fd的分配--expand_files

Kernel version:2.6.14

CPU architecture:ARM920T

Author:ce123(http://blog.csdn.net/ce123)

我们先贴出expand_files函数的源码:

int expand_files(struct files_struct *files, int nr)
{
	int err, expand = 0;
	struct fdtable *fdt;

	fdt = files_fdtable(files);
	if (nr >= fdt->max_fdset || nr >= fdt->max_fds) {   //我们在前面的文章中已经分析过,初始时max_fdset = 1024,max_fds = 32
		if (fdt->max_fdset >= NR_OPEN ||   //#define NR_OPEN (1024*1024)	/* Absolute upper limit on fd num */
			fdt->max_fds >= NR_OPEN || nr >= NR_OPEN) {
			err = -EMFILE;  //max_fdset和max_fds都不能大于 NR_OPEN,否则返回 -EMFILE,即打开太多的文件

			goto out;
		}
		expand = 1;
		if ((err = expand_fdtable(files, nr)))//真正进行扩展
			goto out;
	}
	err = expand;
out:
	return err;
}

expand_files函数进行一些检查后调用expand_fdtable进行文件描述符表的扩展,下面分析expand_fdtable函数。

static int expand_fdtable(struct files_struct *files, int nr)
	__releases(files->file_lock)
	__acquires(files->file_lock)
{
	int error = 0;
	struct fdtable *fdt;
	struct fdtable *nfdt = NULL;

	spin_unlock(&files->file_lock);
	nfdt = alloc_fdtable(nr);//根据nr重新创建一个新的fdtable
	if (!nfdt) {
		error = -ENOMEM;
		spin_lock(&files->file_lock);
		goto out;
	}

	spin_lock(&files->file_lock);
	fdt = files_fdtable(files);
	/*
	 * Check again since another task may have expanded the
	 * fd table while we dropped the lock
	 */
	if (nr >= fdt->max_fds || nr >= fdt->max_fdset) { //nr值必须大于max_fds和max_fdset值,这里再次进行检查是防止另一个进程进行了expand
		copy_fdtable(nfdt, fdt); //将旧的fdtable中的内容拷贝至新的fdtable
	} else {
		/* Somebody expanded while we dropped file_lock */
		spin_unlock(&files->file_lock);
		__free_fdtable(nfdt);
		spin_lock(&files->file_lock);
		goto out;
	}
	rcu_assign_pointer(files->fdt, nfdt);//用新的fdtable替换旧的fdtable  
	free_fdtable(fdt);//释放旧的fdtable 
out:
	return error;
}
我们再来看一下扩展文件描述符表的关键函数alloc_fdtable,其定义如下:

static struct fdtable *alloc_fdtable(int nr)
{
	struct fdtable *fdt = NULL;
	int nfds = 0;
  	fd_set *new_openset = NULL, *new_execset = NULL;
	struct file **new_fds;

	fdt = kmalloc(sizeof(*fdt), GFP_KERNEL);
	if (!fdt)
  		goto out;
	memset(fdt, 0, sizeof(*fdt));

	nfds = __FD_SETSIZE;  //#define __FD_SETSIZE	1024
    		      //  #define PAGE_SHIFT		12
    		      //  #define PAGE_SIZE		(1UL << PAGE_SHIFT)
  	/* Expand to the max in easy steps */
  	do {
		if (nfds < (PAGE_SIZE * 8))//dfds = 1024
			nfds = PAGE_SIZE * 8;
		else {
			nfds = nfds * 2;
			if (nfds > NR_OPEN)
				nfds = NR_OPEN;
		}
	} while (nfds <= nr);//第一次expand时,nr应该等于32

  	new_openset = alloc_fdset(nfds);//分配打开文件位图
  	new_execset = alloc_fdset(nfds);
  	if (!new_openset || !new_execset)
  		goto out;
	fdt->open_fds = new_openset;
	fdt->close_on_exec = new_execset;
	fdt->max_fdset = nfds;//更新max_fdset值,此时这个值为32k

	nfds = NR_OPEN_DEFAULT;//nfds = 32
	/*
	 * Expand to the max in easy steps, and keep expanding it until
	 * we have enough for the requested fd array size.
	 */
	do {
#if NR_OPEN_DEFAULT < 256
		if (nfds < 256)
			nfds = 256;//nfds = 256(32->256->1024)
			//无法超过1024,因为在最开始的就进行了检查,一定要小于current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
		else
#endif
		if (nfds < (PAGE_SIZE / sizeof(struct file *)))
			nfds = PAGE_SIZE / sizeof(struct file *);
		else {
			nfds = nfds * 2;
			if (nfds > NR_OPEN)
				nfds = NR_OPEN;
  		}
	} while (nfds <= nr);
	new_fds = alloc_fd_array(nfds);//分配文件描述符数组
	if (!new_fds)
		goto out;
	fdt->fd = new_fds;
	fdt->max_fds = nfds;//更新max_fds
	fdt->free_files = NULL;
	return fdt;
out:
  	if (new_openset)
  		free_fdset(new_openset, nfds);
  	if (new_execset)
  		free_fdset(new_execset, nfds);
	kfree(fdt);
	return NULL;
}
alloc_fd_array和alloc_fdset采用kmalloc或者vmalloc进行内存分配。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐