<think>我们讨论的是Linux内核3.10.14版本中close系统调用的内核执行流程。
在Linux中,close系统调用用于关闭一个文件描述符。它的调用流程大致如下:
1. 用户空间调用close(fd) -> 触发系统调用,进入内核空间。
2. 内核中,系统调用入口(如x86架构下使用syscall指令)会根据系统调用号(__NR_close)找到对应的处理函数。
3. 在Linux 3.10.14中,close系统调用的处理函数是sys_close()。
具体流程:
用户态调用close(fd) -> 系统调用陷入内核 -> 调用sys_close() -> 调用filp_close() -> 调用fput() -> 如果文件引用计数降为0,则调用__fput() -> 最终调用文件操作表中的release方法(即file_operations->release)。
详细步骤:
- sys_close() 函数位于fs/open.c中。
- sys_close() 会调用filp_close(),然后调用fput()。
- 当文件的引用计数变为0时,会调用__fput(),在这个函数中会调用具体文件系统的release方法(通过file->f_op->release)。
因此,最终调用的函数是file_operations结构体中的release函数指针,这个函数指针根据文件类型(例如普通文件、套接字、管道等)而不同。
例如:
- 对于普通文件,release方法通常是fuse_file_release(如果使用FUSE)或者ext4_file_release(如果使用ext4)等。
- 对于套接字,release方法指向sock_close。
所以,close系统调用最终会调用到具体文件系统或设备驱动中定义的release函数。
下面给出一个简化的调用链:
用户空间: close(fd)
内核空间:
SYSCALL_DEFINE1(close, unsigned int, fd)
-> struct file *file = file_close(fd); // 实际上,在sys_close中直接调用了filp_close
-> filp_close(file, current->files)
-> fput(file)
-> if (file_count(file) == 0) 进入__fput
-> __fput(file)
-> file->f_op->release(inode, file)
注意:在fput()中,如果引用计数不为0,则只是减少引用计数,不会调用release。
因此,最终调用的内核函数是file->f_op->release(inode, file)。
但是,我们需要知道,这个release函数指针是动态绑定的,取决于文件描述符所对应的文件类型。
示例代码(内核源码片段,来自Linux 3.10.14):
fs/open.c:
```
SYSCALL_DEFINE1(close, unsigned int, fd)
{
int retval = __close_fd(current->files, fd);
/* can't restart close syscall because file table entry was cleared */
if (unlikely(retval == -ERESTARTSYS))
retval = -EINTR;
return retval;
}
```
但是,实际上__close_fd会调用filp_close,然后调用fput。而filp_close也在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)
retval = filp->f_op->flush(filp, id);
fput(filp);
return retval;
}
```
而fput()定义在fs/file_table.c中:
```
void fput(struct file *file)
{
if (atomic_long_dec_and_test(&file->f_count)) {
struct task_struct *task = current;
if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
init_task_work(&file->f_u.fu_rcuhead, ____fput);
if (!task_work_add(task, &file->f_u.fu_rcuhead, true))
return;
}
____fput(&file->f_u.fu_rcuhead);
}
}
```
在____fput中:
```
static void ____fput(struct callback_head *work)
{
__fput(container_of(work, struct file, f_u.fu_rcuhead));
}
```
然后,在__fput中:
```
void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
struct inode *inode = file->f_inode;
might_sleep();
fsnotify_close(file);
/*
* The function eventpoll_release() should be the first called
* in the file cleanup chain.
*/
eventpoll_release(file);
locks_remove_file(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op && file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
}
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
security_file_free(file);
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
cdev_put(inode->i_cdev);
fops_put(file->f_op);
put_pid(file->f_owner.pid);
file_sb_list_del(file);
if (file->f_mode & FMODE_WRITE)
drop_file_write_access(file);
file->f_path.dentry = NULL;
file->f_path.mnt = NULL;
file->f_inode = NULL;
file_free(file);
dput(dentry);
mntput(mnt);
}
```
从上面的代码可以看到,在__fput函数中,会调用file->f_op->release(inode, file)。
因此,总结:close系统调用最终会调用具体文件操作表(file_operations)中定义的release函数。
注意:不同的文件系统、设备类型,其release函数不同。例如,对于ext4文件系统,release函数可能是ext4_release_file(在fs/ext4/file.c中定义);对于socket,则是sock_close(在net/socket.c中定义)。
所以,我们不能说一个具体的函数名,而是一个函数指针,它指向具体文件对应的release函数。
但是,我们可以说,在close的内核调用流程中,最终会调用到file_operations->release函数。
因此,回答的重点在于:close系统调用最终会调用具体文件操作表(file_operations)中的release函数。
根据以上分析,我们给出回答。</think>### Linux 3.10.14内核中close()系统调用流程分析
#### close()系统调用完整调用链
```mermaid
graph TD
A[用户空间 close(fd)] --> B[系统调用入口]
B --> C[sys_close]
C --> D[__close_fd]
D --> E[filp_close]
E --> F[fput]
F --> G[__fput]
G --> H[file->f_op->release]
```
#### 关键函数解析
1. **sys_close()** (位于`fs/open.c`)
```c
SYSCALL_DEFINE1(close, unsigned int, fd)
{
int retval = __close_fd(current->files, fd);
// 处理错误和中断
return retval;
}
```
2. **__close_fd()** (位于`fs/file.c`)
```c
int __close_fd(struct files_struct *files, unsigned fd)
{
struct file *file;
file = pick_file(files, fd); // 获取文件对象
return filp_close(file, files); // 继续关闭流程
}
```
3. **filp_close()** (位于`fs/open.c`)
```c
int filp_close(struct file *filp, fl_owner_t id)
{
// 刷新数据缓冲区
if (filp->f_op->flush)
filp->f_op->flush(filp, id);
// 核心释放操作
fput(filp);
return 0;
}
```
4. **fput()** (位于`fs/file_table.c`)
```c
void fput(struct file *file)
{
if (atomic_long_dec_and_test(&file->f_count)) { // 引用计数减1
struct task_struct *task = current;
// 异步处理机制
____fput(file); // 最终释放
}
}
```
#### 最终调用函数
**核心释放函数**:`____fput()` → `__fput()` (位于`fs/file_table.c`)
```c
static void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
struct inode *inode = file->f_inode;
// 关键步骤:调用文件操作表的release方法
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file); // ⭐ 最终调用点 ⭐
// 释放相关资源
security_file_free(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
}
file_sb_list_del(file);
file_free(file);
dput(dentry);
mntput(mnt);
}
```
#### 文件类型对应的release函数
| 文件类型 | release函数 | 源码位置 |
|---------------|-----------------------------|---------------------|
| 普通文件 | `ext4_release_file()` | `fs/ext4/file.c` |
| 套接字(Socket) | `sock_close()` | `net/socket.c` |
| 管道(Pipe) | `pipe_release()` | `fs/pipe.c` |
| 字符设备 | `chrdev_release()` | `fs/char_dev.c` |
| TTY终端 | `tty_release_dev()` | `drivers/tty/tty_io.c` |
#### 关键数据结构
```c
struct file_operations {
// ...
int (*release) (struct inode *, struct file *);
// ...
};
```
#### 执行流程特点
1. **引用计数机制**:通过`f_count`管理文件对象的生命周期
2. **异步释放**:使用`task_work_add()`实现安全异步释放
3. **类型特定操作**:通过`file_operations`函数表实现多态
4. **资源清理**:依次释放dentry、inode、vfsmount等关联资源
---