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 ...
随机推荐
- Spring Security 自定义 登陆 权限验证
转载于:https://www.jianshu.com/p/6b8fb59b614b 项目简介 基于Spring Cloud 的项目,Spring Cloud是在Spring Boot上搭建的所以按照 ...
- ...cURL error 60: SSL certificate problem: unable to get local issuer certificate...
问题描述: 在做PHP爬虫的时候, 安装了 guzzle 和 dom-crawler 之后, 调用的时候出现问题, 如下 报错内容: Fatal error: Uncaught GuzzleHttp ...
- java读取 xml文件
java读取xml文件的四种方法 转自https://www.cnblogs.com/lingyao/p/5708929.html Xml代码 1 <?xml version="1. ...
- luogu 3698 [CQOI2017]小Q的棋盘 树形dp
Code: #include <bits/stdc++.h> #define N 107 #define setIO(s) freopen(s".in","r ...
- 获取link后的参数值
getQueryString:function(name){ var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i ...
- Gym 100548F Color 给花染色 容斥+组合数学+逆元 铜牌题
Problem F. ColorDescriptionRecently, Mr. Big recieved n flowers from his fans. He wants to recolor th ...
- word标题前出现黑块解决方案
1,将光标定位到出现问题的标题前面 2,点击最上方的标题,然后点击修改-->格式-->编号-->无-->确定. 3,重新设置标题即可.
- ida 下载
链接:https://pan.baidu.com/s/1Rs2UOSfroBGf_MIkJv0cGw 提取码:kjqd 官网:https://www.hex-rays.com/updida.shtml
- 引发了未经处理的异常:读取访问权限冲突。 _First 是 nullptr。
1.问题:程序崩溃出现错误 引发了未经处理的异常:读取访问权限冲突. _First 是 nullptr. string strreponse=0: 定义这条语句,字符串初始化错误. 自己开发了一个股票 ...
- solr系列之solr-5.5.5 window单机版默认Jetty安装
Solr5.5.5单机部署 Solr5和Solr4有很大区别,最为明显的就是Solr5已经可以独立部署,从Solr5开始,Solr已经不再以war包形式部署,Solr已经成为了一个独立的java服务端 ...