linux-关闭文件
1.打开参考:
http://q.cnblogs.com/q/39275/
http://hi.baidu.com/auxor/item/49b6e929fdf16dc7ed10f197
2.关闭参考:
(google:linux close 实现)
http://blog.csdn.net/lkqboy2599/article/details/9978561
http://blog.chinaunix.net/uid-28362602-id-3426896.html
http://hi.baidu.com/mystone7/item/71f341714eb76a46ee1e5332
http://eastsun.blogbus.com/logs/7873908.html
3.传统关闭--2.6.11
3.1. 函数原型与参数
int close (int f i l e d e s)
关闭一个文件时也释放该进程加在该文件上的所有记录锁。当一个进程终止时,它所有的打开文件都由内核自动关闭。很多程序都使用这一功能而不显式地用c l o s e关闭打开的文件。对于对个任务同时操作该文件时,需要先释放该句柄,才能关闭。
3.2. 主要函数调用关系图
sys_close
| ------------- filp_close
SYSCALL_DEFINE1(close, unsigned int, fd)
{{//这里SYSCALL_DEFINE1 close到sys_close的转换请参看前面的文章Linux 编程中的API函数和系统调用的关系
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)
goto out_unlock;
rcu_assign_pointer(fdt->fd[fd], NULL);
__clear_close_on_exec(fd, fdt);
__put_unused_fd(files, fd);
spin_unlock(&files->file_lock);
retval = filp_close(filp, files);
…//省略次要
}
函数不长,流程主要如下
(1)根据用户空间传入的文件描述符fd取出对应的struct file结构体
方便理解,我写成Struct file filt=current->files_struct->fdtable->files[fd]; (current是当前task_struct)
(2)清空进程的文件描述符fd所对应的标准位,如果要关闭的这个的文件描述符对应的fd小于下一次的文件描述符起点,则根系下一次本进程的文件描述符起点为fd
__clear_close_on_exec(fd, fdt);//执行__clear_bit(fd, fdt->close_on_exec);操作,即在close_on_exec中把fd位的设置为0
__put_unused_fd(files, fd);//执行__clear_open_fd(fd, fdt);,进一步执行__clear_bit(fd, fdt->open_fds);,即在open_fds位图中把这个fd位也设置为0;并且还会执行if (fd < files->next_fd)files->next_fd = fd;
至此,进程关联的所有文件描述符中已经不存在这个文件描述符了(根据不存在struct file及其相关dentry,inode,vfsmount了)
- void fput(struct file *file)
- {
- if (atomic_long_dec_and_test(&file->f_count)) {
- struct task_struct *task = current;
- file_sb_list_del(file);
- if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) {
- unsigned long flags;
- spin_lock_irqsave(&delayed_fput_lock, flags);
- list_add(&file->f_u.fu_list, &delayed_fput_list);
- schedule_work(&delayed_fput_work);
- spin_unlock_irqrestore(&delayed_fput_lock, flags);
- return;
- }
- init_task_work(&file->f_u.fu_rcuhead, ____fput);
- task_work_add(task, &file->f_u.fu_rcuhead, true);
- }
- }
这里如果当前进程在检查struct file的引用等于1,那么就把这个struct file结构体从超级块的文件链表中也删除掉(但是struct file结构体此时还没有从内存中释放)。
释放操作实际只是注册了一个回调函数,通过下面两行
init_task_work(&file->f_u.fu_rcuhead, ____fput); //____fput是一个实际释放操作的回调函数
task_work_add(task, &file->f_u.fu_rcuhead, true);
其中,____fput函数会释放struct file结构体,以及尝试释放起对应的dentry,mnt(之所以叫尝试是因为调用dput(dentry),dput(mnt),而dput(denty),dput(mnt)会继续检查dentry,mnt是否还在被使用,如果没有任何引用则真正释放所占内存,否则仅减少其引用计数)。
init_task_work中,file->f_u.fu_rcuhead是一个rcu_head节点,内核中
struct callback_head {
struct callback_head *next;
void (*func)(struct callback_head *head);
};
#define rcu_head callback_head
而task_work_add(task, &file->f_u.fu_rcuhead, true);会吧这个rcu_head节点加入task->task_works, 并且会调用set_notify_resume(task)把进程的thread_info的标识里设置上TIF_NOTIFY_RESUME
这样,在进程从内核态返回用户态的时候会调用tracehook_notify_resume把task->task_works链表中的所有注册好的函数都会执行一遍(此时___fput函数就会被调用到了),并且清除TIF_NOTIFY_RESUME标识位
所以,struct file结构体要释放也是在内核返回用户态的时候才执行的,在内核态的时候一直还保留着。
注意,这里__fput中执行的释放操作并没有把进程所拥有的这个文件描述符及其在位图中的占位清空,如果执行了__fput只是这个文件描述符对应的的struct file=NULL了而已,文件描述符还站着呢。这需要后面用户空间再发个sys_close调用才能完成后续清除文件描述符等任务。详见下一篇
注意,这些释放都是内存操作,磁盘上面的文件,inode等并没有释放。
5.selff
1.调用层次
1.1close——》sys_close——》SYSCALL_DEFINE1——》_close_fd——》 filp_close——》 fput
1.2关键代码及注释(linux-3.1.1)
1.2.1
//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 == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK))
retval
= -EINTR;
return
retval;
}
1.2.2
//File.c
int
__close_fd(struct files_struct *files, unsigned fd)
{
struct
file *file;
struct
fdtable *fdt;
spin_lock(&files->file_lock);
fdt
= files_fdtable(files);
//参数有效性判断
if
(fd >= fdt->max_fds)
goto
out_unlock;
//取得文件描述符对应的file
file
= fdt->fd[fd];
if
(!file)
goto
out_unlock;
//将文件描述符对应的file置空
rcu_assign_pointer(fdt->fd[fd],
NULL);
//清除close_on_exec的标志位,表示进程结束时不应该关闭对应位的文件描述对象
__clear_close_on_exec(fd,
fdt);
//清除文件描述的分配位图
__put_unused_fd(files,
fd);
spin_unlock(&files->file_lock);
return
filp_close(file, files);
out_unlock:
spin_unlock(&files->file_lock);
return
-EBADF;
}
1.2.3
//Open.c
int
filp_close(struct file *filp, fl_owner_t id)
{
int
retval = 0;
//file引用计数为零.已经无效了
if
(!file_count(filp)) {
printk(KERN_ERR
"VFS: Close: file count is 0\n");
return
0;
}
//如果文件对象有flush()操作,调用之
if
(filp->f_op && filp->f_op->flush)
retval
= filp->f_op->flush(filp, id);
if
(likely(!(filp->f_mode & FMODE_PATH))) {
//发出flush通告
dnotify_flush(filp,
id);
//文件要关闭了,将进程拥有的文件的强制锁清除掉
locks_remove_posix(filp,
id);
}
//释放file对象
fput(filp);
return
retval;
}
1.2.4
//File_table.c
void
fput(struct file *file)
{
if
(atomic_long_dec_and_test(&file->f_count)) {
struct
task_struct *task = current;
file_sb_list_del(file);
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;
/*
* After this task has run exit_task_work(),
* task_work_add() will fail. free_ipc_ns()->
* shm_destroy() can do this. Fall through to delayed
* fput to avoid leaking *file.
*/
}
if
(llist_add(&file->f_u.fu_llist, &delayed_fput_list))
schedule_work(&delayed_fput_work);
}
}
2.分析
2.1主要流程:
2.1.1根据用户空间传入的文件描述符fd取出对应的struct
file结构体
便于理解(详见1.2.2),可以写成Struct
file filt=current->files_struct->fdtable->files[fd];
(current是当前task_struct)
2.1.2清空进程的文件描述符fd所对应的标准位,如果要关闭的这个的文件描述符对应的fd小于下一次的文件描述符起点,则根系下一次本进程的文件描述符起点为fd
__clear_close_on_exec(fd,
fdt);//执行__clear_bit(fd,
fdt->close_on_exec);
__put_unused_fd(files,
fd);//执行__clear_open_fd(fd,
fdt);,进一步执行__clear_bit(fd,
fdt->open_fds);,即在open_fds位图中把这个fd位也设置为0;并且还会执行if
(fd < files->next_fd)files->next_fd = fd;
至此,进程关联的所有文件描述符中已经不存在这个文件描述符了(根据不存在struct
file及其相关dentry,inode,vfsmount了)
2.2对于fput(filp)的理解:
2.2.1这里如果当前进程在检查struct
file,那么就把这个struct
file结构体从超级块的文件链表中也删除掉(但是struct
file结构体此时还没有从内存中释放)。
释放操作实际只是注册了一个回调函数,通过下面两行
init_task_work(&file->f_u.fu_rcuhead,
____fput); //____fput是一个实际释放操作的回调函数
task_work_add(task,
&file->f_u.fu_rcuhead, true);
其中,____fput函数会释放struct
file结构体,以及尝试释放起对应的dentry,mnt(之所以叫尝试是因为调用dput(dentry),dput(mnt),而dput(denty),dput(mnt)会继续检查dentry,mnt是否还在被使用,如果没有任何引用则真正释放所占内存,否则仅减少其引用计数)。
init_task_work中,file->f_u.fu_rcuhead是一个rcu_head节点,内核中
struct
callback_head {
struct callback_head *next;
void (*func)(struct
callback_head *head);
};
#define rcu_head callback_head
总之,init_task_work把____fput函数进行file->f_u.fu_rcuhead->func=___fput设置
而task_work_add(task,
&file->f_u.fu_rcuhead, true);会把这个rcu_head节点加入task->task_works,
并且会调用set_notify_resume(task)把进程的thread_info的标识里设置上TIF_NOTIFY_RESUME
这样,在进程从内核态返回用户态的时候会调用tracehook_notify_resume把task->task_works链表中的所有注册好的函数都会执行一遍(此时___fput函数就会被调用到了),并且清除TIF_NOTIFY_RESUME标识位
所以,struct
file结构体要释放也是在内核返回用户态的时候才执行的,在内核态的时候一直还保留着。
注意,这里__fput中执行的释放操作并没有把进程所拥有的这个文件描述符及其在位图中的占位清空,如果执行了__fput只是这个文件描述符对应的的struct
file=NULL了而已,文件描述符还站着呢。这需要后面用户空间再发个sys_close调用才能完成后续清除文件描述符等任务。
2.2.2只有调用了sys_close系统调用,进程的所有文件列表中才会把这个文件删除Sys_read(v),sys_write(v)可能调用了fput(filp),也可能没有调用fput(filp).但是sys_read(v),sys_write(v)调用了fput(filp)也不影响close中的这个地方的调用fput(filp),因为fput(filp)本身会先执行if(atomic_long_dec_and_test(&file->f_count))
条件判断,如果不成立,则什么都不用做。
linux-关闭文件的更多相关文章
- 深入理解linux关闭文件和删除文件
背景介绍 最近看了linux系统编程(linux system programming)一书,结合深入理解linux内核(understanding the linux kernel)一书,深入理解了 ...
- Linux C 文件与目录2 文件的打开与关闭
文件的打开与关闭 open和close 文件的打开指的是从磁盘中找到一个文件,返回一个整形的打开文件顺序的编号.打开的文件处于可读.可写状态.文件的关闭指的是释放打开的文件,是文件处于不可读写的状态. ...
- linux文件操作篇 (二) 打开和关闭文件
2.1 打开文件和关闭文件 #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h> 头文件 i ...
- Linux C 文件操作,系统调用 -- open()、read() 和 标准I/O库 -- fopen()、fread()
函数汇总: open().write().read().close() fopen().fwrite().fread().fclose() 一.什么是文件 在讲述文件操作之前,我们首先要知道什么是文件 ...
- Linux - 日志文件
Linux日志文件绝大多数存放在/var/log目录,其中一些日志文件由应用程序创建,其他的则通过syslog来创建. Linux系统日志文件通过syslog守护程序在syslog套接字/dev/lo ...
- Linux大文件已删除,但df查看已使用的空间并未减少解决
在我的生活当中遇到磁盘快满了,这时候准备去删除一些大文件 于是我使用ncdu 查看了一下当前系统占用资源比较多的是那些文件,结果一看是elasticsearch的日志文件,好吧,竟然找到源头了,那就把 ...
- Linux core 文件介绍
Linux core 文件介绍 http://www.cnblogs.com/dongzhiquan/archive/2012/01/20/2328355.html 1. core文件的简单介绍在一个 ...
- Linux中文件描述符fd和文件指针flip的理解
转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...
- linux库文件编写入门(笔记)
linux库文件的编写 作者: laomai地址: http://blog.csdn.net/laomai 本文主要参考了如下资料⑴hcj写的"Linux静态/动态链接库的创建和使用&quo ...
- Linux关闭防火墙、SELinux
使用root权限: Linux关闭防火墙: 1. chkconfig –list|grep iptables 2. chkconfig iptables off 永久关闭防火墙 3. chkconfi ...
随机推荐
- Laravel中Contracts的理解和使用
Laravel 的 Contracts 是一组定义了框架核心服务的接口.说白了就是一组接口.使用它就是为了降低耦合性. 即便如此,是不是也有同学会搞不清楚Contracts在lavarel体系中的到底 ...
- Navicat连接虚拟机上的mysql
刚刚在虚拟主机上安装mysql, 想使用Navicat 操作mysql. 但是连接不上 报错: 2003 - can't connect to MySQL server on '192.168.1 ...
- Xilinx的IP核接口命名说明
s_axis中的s表示:slave(从); m_axis中的m表示:master(主). axis表示AXI(一种总线协议) Signal.
- 013_STM32程序移植之_DS18B20
013_STM32程序移植之_DS18B20 1. 测试环境:STM32C8T6 2. 测试模块:DS18B20模块 3. 测试接口: 1. DS18B20模块接口: DS18B20引脚 ...
- topcoder13444
CountTables TopCoder - 13444 sol:题意和题解都丢在上面了,自己XJByy了一下 先保证行不同,然后对列容斥,dp[i]表示i列的答案 行不同时i列的答案显然是C(c^i ...
- hdu6568 Math (概率dp)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6568 题意: 在$0$到$L$的坐标轴运输货物,在每个整数点可能丢失货物,丢失概率为$p$,丢失后可 ...
- airflow自动生成dag
def auto_create_dag(): dag_list=[] dag = DAG() dag_list.append(dag) return dag_list dags = auto_crea ...
- mysql5.7 彻底解决sql_mode=only_full_group_by
ONLY_FULL_GROUP_BY是mysql默认的一种sql模式,其作用是约束sql语句:要求select中的所有字段,除复合函数外,全部要出现在group by中. 默认这种模式是有原因的,因为 ...
- PIMPL(private implementantion)模式(转载)
前记:请搜索PIMPL(private implementantion)模式和桥接模式, PIMPL是桥接模式的一种典型实现 以下转自:http://blog.csdn.net/nrc_douning ...
- ThinkPHP空操作与命名空间
命名空间:相当于一个虚拟的目录 正常管理文件使用文件夹--物理区分 TP框架的初始命名空间是:ThinkPHP\Library 在TP框架下命名空间里面使用\代表的是初始命名空间(ThinkPHP\L ...