详解sys_close

    内核源码:linux-2.6.38.8.tar.bz2

    目标平台:ARM体系结构

 

    在Linux内核中,系统调用close的定义如下所示:

/* fs/open.c */
SYSCALL_DEFINE1(close, unsigned int, fd)
{
	struct file * filp;
	struct files_struct *files = current->files;
	struct fdtable *fdt;
	int retval;

	spin_lock(&files->file_lock);
	fdt = files_fdtable(files);
	if (fd >= fdt->max_fds) //无效的文件描述符
		goto out_unlock;
	filp = fdt->fd[fd]; //获得文件指针
	if (!filp) //filp为NULL
		goto out_unlock;
	rcu_assign_pointer(fdt->fd[fd], NULL); //将fdt->fd[fd]置零
	FD_CLR(fd, fdt->close_on_exec); //清除相应的close_on_exec比特位
	__put_unused_fd(files, fd); //清除相应的open_fds比特位
	spin_unlock(&files->file_lock);
	
	retval = filp_close(filp, files); //执行close的主要操作
	
	if (unlikely(retval == -ERESTARTSYS ||
		     retval == -ERESTARTNOINTR ||
		     retval == -ERESTARTNOHAND ||
		     retval == -ERESTART_RESTARTBLOCK))
		retval = -EINTR;

	return retval;

out_unlock:
	spin_unlock(&files->file_lock);
	return -EBADF;
}
    filp_close函数用于完成close系统调用的主要操作。源代码如下所示:

/* include/linux/fs.h */
typedef struct files_struct *fl_owner_t;

/* fs/open.c */
int filp_close(struct file *filp, fl_owner_t id)
{
	int retval = 0;

	if (!file_count(filp)) { //文件指针的引用计数已经为零
		printk(KERN_ERR "VFS: Close: file count is 0\n");
		return 0;
	}

	if (filp->f_op && filp->f_op->flush) //flush函数指针为真则调用它
		retval = filp->f_op->flush(filp, id);

	dnotify_flush(filp, id); //对于目录文件,释放dnotify mark资源
	locks_remove_posix(filp, id); //清除POSIX文件锁
	fput(filp);
	return retval;
}
    fput函数在文件指针引用计数file->f_count为零时释放在open等系统调用中所使用的资源。源代码如下所示:

/* fs/file_table.c */
void fput(struct file *file)
{
	if (atomic_long_dec_and_test(&file->f_count)) //引用计数为零则执行__fput函数销毁文件指针
		__fput(file);
}

static void __fput(struct file *file)
{
	struct dentry *dentry = file->f_path.dentry;
	struct vfsmount *mnt = file->f_path.mnt;
	struct inode *inode = dentry->d_inode;

	might_sleep(); //可能休眠并记录相应信息以便调试

	fsnotify_close(file); //实现文件系统事件监控的IN_CLOSE_WRITE或IN_CLOSE_NOWRITE事件
	
	eventpoll_release(file); //释放epoll资源
	locks_remove_flock(file); //清除flock文件锁

	if (unlikely(file->f_flags & FASYNC)) { //标志FASYNC通过fcntl系统调用设置
		if (file->f_op && file->f_op->fasync) //当fasync函数指针为真时则调用它,执行异步通知
			file->f_op->fasync(-1, file, 0);
	}
	
	//当release函数指针为真时则调用它,执行与file->f_op->open相反的操作
	if (file->f_op && file->f_op->release)
		file->f_op->release(inode, file);
	security_file_free(file); //安全模块检查
	ima_file_free(file); //释放相应i节点的IMA数据
	if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) //字符设备文件
		cdev_put(inode->i_cdev);
	//处理操作函数模块(当以模块加载时),递增file->f_op->owner->refptr->decs计数,并唤醒等待该模块的进程
	fops_put(file->f_op); 
	put_pid(file->f_owner.pid); //释放捕捉SIGIO信号的进程的相关资源
	file_sb_list_del(file); //从超级块文件指针链表中移除该项
	if (file->f_mode & FMODE_WRITE)
		//递减一些相关的引用计数并将file->f_mnt_write_state从FILE_MNT_WRITE_TAKEN状态更改为FILE_MNT_WRITE_RELEASED
		drop_file_write_access(file); 
	file->f_path.dentry = NULL;
	file->f_path.mnt = NULL;
	file_free(file); //释放file->f_cred和文件指针自身
	dput(dentry); //释放相应的目录项资源(引用计数为零时)
	mntput(mnt); //释放相应的文件系统资源(引用计数为零时)
}

### epoll_wait 函数详解 `epoll_wait` 是 `epoll` 系列系统调用中的一个重要函数,用于阻塞当前线程直到指定的文件描述符上发生感兴趣的事件或者超时时间到达。以下是关于该函数的详细说明: #### 原型定义 ```c #include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); ``` - **`epfd`**: 这是由 `epoll_create()` 或者 `epoll_create1()` 返回的 epoll 文件描述符[^2]。 - **`struct epoll_event *events`**: 一个指向存储返回事件数组的指针。这些事件是在监控的文件描述符上发生的事件[^3]。 结构体 `epoll_event` 定义如下: ```c typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; ``` - **`maxevents`**: 指定要监听的最大事件数量。此值必须大于零[^2]。 - **`timeout`**: 超时时间,单位为毫秒。如果设置为 `-1`,则表示无限期等待;如果是 `0`,则立即返回。 #### 返回值 成功时返回准备好的文件描述符的数量(即已准备好处理的事件数),失败时返回 `-1` 并设置相应的错误码。 #### 错误代码 常见的错误代码包括但不限于以下几种: - **EBADF**: 参数 `epfd` 不是一个有效的文件描述符[^2]。 - **EINTR**: 在任何事件之前发生了信号中断[^2]。 - **EINVAL**: 参数 `maxevents` 小于等于零。 #### 使用示例 下面是一段简单的代码片段展示如何使用 `epoll_wait` 来检测多个 socket 上的数据到来情况: ```c #define MAX_EVENTS 10 int main() { int nfds, epollfd; struct epoll_event ev, events[MAX_EVENTS]; int sfd, sfd2; epollfd = epoll_create1(0); if (epollfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } // 设置并注册第一个socket sfd = create_and_bind(); listen(sfd, SOMAXCONN); ev.events = EPOLLIN; ev.data.fd = sfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) == -1) { perror("epoll_ctl: add"); close(epollfd); exit(EXIT_FAILURE); } // 主循环 while (1) { printf("Waiting for incoming connections...\n"); nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_pwait"); break; } for (int n = 0; n < nfds; ++n) { if ((events[n].data.fd == sfd)) { handle_new_connection(events[n].data.fd); } } } close(epollfd); } ``` #### 性能优势 相比传统的 `select` 和 `poll` 方法,`epoll` 提供了更高的性能和可扩展性。特别是当监控大量文件描述符时,由于其基于事件通知的设计模式而非轮询方式,因此具有 O(1) 的时间复杂度[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值