Chinaunix首页 | 论坛 | 博客
  • 博客访问: 105854
  • 博文数量: 10
  • 博客积分: 207
  • 博客等级: 入伍新兵
  • 技术积分: 380
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-10 12:44
文章存档

2015年(2)

2013年(1)

2012年(7)

我的朋友

分类: LINUX

2012-11-30 15:07:45

目录

close系统调用在内核里面的入口函数为sys_close. 1

根据用户空间传入的文件描述符fd取出对应的struct file结构体... 1

清空进程的文件描述符fd所对应的标准位,如果要关闭的这个的文件描述符对应的fd小于下一次的文件描述符起点,则根系下一次本进程的文件描述符起点为fd. 2

释放struct file结构体... 2

所以,只有调用了sys_close系统调用,进程的所有文件列表中才会把这个文件删除... 3

 

 

WORD里面的目录复制过来似乎不能直接用。。还是放在这里当主线看吧..

 

close系统调用在内核里面的入口函数为sys_close

[root@syslab ~]# grep close /usr/include/asm/unistd_64.h

#define __NR_close                              3

__SYSCALL(__NR_close, sys_close)

 

SYSCALL_DEFINE1(close, unsigned int, fd)

{{//这里SYSCALL_DEFINE1 closesys_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);

 

…//省略次要

}

函数不长,流程主要如下

根据用户空间传入的文件描述符fd取出对应的struct file结构体

         方便理解,我写成Struct file filt=current->files_struct->fdtable->files[fd]; current是当前task_struct

清空进程的文件描述符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及其相关dentryinodevfsmount了)

 

释放struct file结构体

fput(filp);

 

这个函数执行的和sys_read中释放struct file结构体一样的操。


 

点击(此处)折叠或打开

  1. void fput(struct file *file)
  2. {
  3.     if (atomic_long_dec_and_test(&file->f_count)) {
  4.         struct task_struct *task = current;
  5.         file_sb_list_del(file);
  6.         if (unlikely(in_interrupt() || task->flags & PF_KTHREAD)) {
  7.             unsigned long flags;
  8.             spin_lock_irqsave(&delayed_fput_lock, flags);
  9.             list_add(&file->f_u.fu_list, &delayed_fput_list);
  10.             schedule_work(&delayed_fput_work);
  11.             spin_unlock_irqrestore(&delayed_fput_lock, flags);
  12.             return;
  13.         }
  14.         init_task_work(&file->f_u.fu_rcuhead, ____fput);
  15.         task_work_add(task, &file->f_u.fu_rcuhead, true);
  16.     }
  17. }


 

但是sys_read中是按需调用此fput(filp)函数,sys_read是按需释放(if判断),详见Linux VFS中read系统调用实现原理 ,这里是直接调用fput(filp)

Fput(filp)执行下面的操作

(如果sys_readsys_write里面已经把这个结构体释放掉了或者struct file结构体的引用计数大于1sys_close调用fput(filp)函数里面就会什么都不做,这里并不冲突,文章结尾会再次说这个问题)

   这里如果当前进程在检查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)会继续检查dentrymnt是否还在被使用,如果没有任何引用则真正释放所占内存,否则仅减少其引用计数)。

    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_resumetask->task_works链表中的所有注册好的函数都会执行一遍(此时___fput函数就会被调用到了),并且清除TIF_NOTIFY_RESUME标识位

    所以,struct file结构体要释放也是在内核返回用户态的时候才执行的,在内核态的时候一直还保留着。

    注意,这里__fput中执行的释放操作并没有把进程所拥有的这个文件描述符及其在位图中的占位清空,如果执行了__fput只是这个文件描述符对应的的struct file=NULL了而已,文件描述符还站着呢。这需要后面用户空间再发个sys_close调用才能完成后续清除文件描述符等任务。详见下一篇

    注意,这些释放都是内存操作,磁盘上面的文件,inode等并没有释放。

 

所以,只有调用了sys_close系统调用,进程的所有文件列表中才会把这个文件删除

         Sys_read(v),sys_write(v)可能调用了fput(filp)(调用条件如Linux VFSread系统调用实现原理所述;也可能没有调用fput(filp).

       但是sys_read(v),sys_write(v)调用了fput(filp)也不影响close中的这个地方的调用fput(filp),因为fput(filp)本身会先执行if(atomic_long_dec_and_test(&file->f_count)) 条件判断,如果不成立,则什么都不用做。

 

参考:kernel 3.6.7 源码

阅读(5733) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~