注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

linux下文件的删除与重命名  

2012-06-10 19:14:32|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、文件删除
文件的删除在linux下实现非常简单,你几乎没有办法来禁止用户删除一个文件(记得之前一个日志中大致说了flock来禁止删除文件,总之还是很麻烦的)。这一点在windows中就不是这样,例如你再使用一个文件的时候删除文件会提示文件正在使用,所以无法删除,这些使用包括可执行文件正在被执行、一些文本文件正在被编辑等现象。但是在linux下可执行文件即使正在被执行,它同样可以被删除,没有关系。删除之后是一个双赢的效果,那就是这个文件在系统中还是存在的,但是通过ls或者其它的接口再也看不到该文件的存在,相当于对于VFS来说该文件不再可见。
简单测试一个可执行文件
[tsecer@Harry rmandtouch]$ gcc  sleeper.c -o sleeper.c.exe
[tsecer@Harry rmandtouch]$ ./sleeper.c.exe &
[1] 10869
[tsecer@Harry rmandtouch]$ rm sleeper.c.exe
[tsecer@Harry rmandtouch]$ cat /proc/10869/maps
001e8000-00206000 r-xp 00000000 fd:00 1280       /lib/ld-2.11.2.so
00206000-00207000 r--p 0001d000 fd:00 1280       /lib/ld-2.11.2.so
00207000-00208000 rw-p 0001e000 fd:00 1280       /lib/ld-2.11.2.so
0020a000-0037c000 r-xp 00000000 fd:00 1282       /lib/libc-2.11.2.so
0037c000-0037d000 ---p 00172000 fd:00 1282       /lib/libc-2.11.2.so
0037d000-0037f000 r--p 00172000 fd:00 1282       /lib/libc-2.11.2.so
0037f000-00380000 rw-p 00174000 fd:00 1282       /lib/libc-2.11.2.so
00380000-00383000 rw-p 00000000 00:00 0
007a8000-007a9000 r-xp 00000000 00:00 0          [vdso]
08048000-08049000 r-xp 00000000 fd:00 559043     /home/tsecer/CodeTest/rmandtouch/sleeper.c.exe (deleted)
08049000-0804a000 rw-p 00000000 fd:00 559043     /home/tsecer/CodeTest/rmandtouch/sleeper.c.exe (deleted)
b77a2000-b77a3000 rw-p 00000000 00:00 0
b77b8000-b77b9000 rw-p 00000000 00:00 0
bff12000-bff27000 rw-p 00000000 00:00 0          [stack]
[tsecer@Harry rmandtouch]$
这里有一个问题,那就是这个文件还在系统中存在,只是通过ls之类的命令看不到而已,那么如果此时我们使用 touch再创建一个同名的文件,那么哪个残存的目录项应该怎么办呢,会不会提示一个莫名其妙的EBUSY错误?
简单试一下,没有这种提示,也就是说同名的文件(这里的sleeper.c.exe)是可以创建成功的,那么此时的残存文件和新生的同名文件在系统中是如何和平共处的呢?
二、删除时内核执行的动作
通过strace可以知道rm使用的API是unlinkat,这个函数的操作流程大致为
sys_unlinkat--->>>do_unlinkat---->>d_delete--->>>__d_drop
static inline void __d_drop(struct dentry *dentry)
{
    if (!(dentry->d_flags & DCACHE_UNHASHED)) {
        dentry->d_flags |= DCACHE_UNHASHED;
        hlist_del_rcu(&dentry->d_hash);
    }
}
这里的两个操作都比较重要,
1、设置标志位
首先设置标志表明这个目录已经被删除了,当这个dentry占用的内存空间被回收的时候会检验这个标志,进行inode磁盘空间的释放(这里的操作都是内存中VFS通用结构的操作)。另一个就是我们之前看到的文件处于  “(deleted)” 状态的判断由来__d_path:
static inline int d_unhashed(struct dentry *dentry)
{
    return (dentry->d_flags & DCACHE_UNHASHED);
}
    if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
        buflen -= 10;
        end -= 10;
        if (buflen < 0)
            goto Elong;
        memcpy(end, " (deleted)", 10);
    }
2、对文件系统不可见
hlist_del_rcu(&dentry->d_hash);
这个操作将dentry结构从整个系统的dentry hash表中脱链,这个脱链之后,当系统执行所有的VFS操作的时候,它首先通过cache中搜索这个dentry,但是由于它已经从这个hash链表中脱离,所以这个残存的dentry对整个VFS系统不再可见。
那么此时这个dentry如何被应用呢?因为这个结构的引用计数不为零,说明一定有一些结构通过指针或者其它包含等方式在包含着这个dentry的位置,以后这个dentry脱离组织,自生自灭,它的释放就是当最后那个使用了这个文件的进程结束了对这个文件的使用的时候再回收。
3、磁盘上的处理
vfs_unlink--->>>ext2_unlink
    de = ext2_find_entry (dir, dentry, &page);
    if (!de)
        goto out;

    err = ext2_delete_entry (de, page);
在ext2_delete_entry函数中执行了
    dir->inode = 0;
注意,这里的dir是一个ext2_dir_entry_2结构,而且是和ext2物理存储单位对应的一个结构,所以这个修改会体现在ext2的磁盘上(当然也是首先在磁盘的缓冲上)。然后第二次创建文件从磁盘上搜索ext2_dir_entry_2缓冲的时候,那个被删除的文件同样也不存在,所以可以创建新的同名文件而和原始文件不冲突。
这里有一个问题,如果说这里的文件dir->inode清零之后,然后这个文件还在被使用,此时断点,那么这个目录项对应的磁盘文件文件将会在磁盘中一直存在,但是无法被引用,相当于内存中的内存泄露,这里可以认为是“磁盘泄露”,我想应该有一些工具来处理这种情况吧,就像是内存检测工具一样,原理可能类似。
三、rename文件
[tsecer@Harry rmandtouch]$ gcc  sleeper.c -o sleeper.c.exe
[tsecer@Harry rmandtouch]$ ./sleeper.c.exe &
[1] 11003
[tsecer@Harry rmandtouch]$ strace mv sleeper.c.exe ../..
execve("/bin/mv", ["mv", "sleeper.c.exe", "../.."], [/* 46 vars */]) = 0
brk(0)                                  = 0x8fc4000
……
rename("sleeper.c.exe", "../../sleeper.c.exe") = 0
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
[tsecer@Harry rmandtouch]$ cat /proc/11003/maps
001e8000-00206000 r-xp 00000000 fd:00 1280       /lib/ld-2.11.2.so
00206000-00207000 r--p 0001d000 fd:00 1280       /lib/ld-2.11.2.so
00207000-00208000 rw-p 0001e000 fd:00 1280       /lib/ld-2.11.2.so
0020a000-0037c000 r-xp 00000000 fd:00 1282       /lib/libc-2.11.2.so
0037c000-0037d000 ---p 00172000 fd:00 1282       /lib/libc-2.11.2.so
0037d000-0037f000 r--p 00172000 fd:00 1282       /lib/libc-2.11.2.so
0037f000-00380000 rw-p 00174000 fd:00 1282       /lib/libc-2.11.2.so
00380000-00383000 rw-p 00000000 00:00 0
00682000-00683000 r-xp 00000000 00:00 0          [vdso]
08048000-08049000 r-xp 00000000 fd:00 559043     /home/tsecer/sleeper.c.exe
08049000-0804a000 rw-p 00000000 fd:00 559043     /home/tsecer/sleeper.c.exe
b7822000-b7823000 rw-p 00000000 00:00 0
b7838000-b7839000 rw-p 00000000 00:00 0
bfe76000-bfe8b000 rw-p 00000000 00:00 0          [stack]
[tsecer@Harry rmandtouch]$
这里比较有意思的是当文件位置变化之后,maps文件中显示的文件路径也随之发生了变化。
1、maps显示处理
    if (file) {
        pad_len_spaces(m, len);
        seq_path(m, file->f_path.mnt, file->f_path.dentry, "\n");

struct path {
    struct vfsmount *mnt;
    struct dentry *dentry;
};
这里可以看到,dentry是一个指针,可以预测这个指针指向的内容当重命名的时候是不会改变的,因为dentry并不知道有哪些使用者在指向自己,所以无法通知。
2、rename时的处理
sys_rename--->>>sys_renameat--->>>do_rename---->>>vfs_rename--->>vfs_rename_other---->>>d_move--->>>d_move_locked

#define do_switch(x,y) do { \
    __typeof__ (x) __tmp = x; \
    x = y; y = __tmp; } while (0)

……
    /* Switch the names.. */
    switch_names(dentry, target);
    do_switch(dentry->d_name.len, target->d_name.len);
    do_switch(dentry->d_name.hash, target->d_name.hash);

    /* ... and switch the parents */
    if (IS_ROOT(dentry)) {
        dentry->d_parent = target->d_parent;
        target->d_parent = target;
        INIT_LIST_HEAD(&target->d_u.d_child);
    } else {
        do_switch(dentry->d_parent, target->d_parent);

        /* And add them back to the (new) parent lists */
        list_add(&target->d_u.d_child, &target->d_parent->d_subdirs);
    }

    list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
这里各种switch,就是字符串的赋值,总之原始的结构是还没有释放,并且将新dentry结构的很多内置属性拷贝到了自己的结构中。
  评论这张
 
阅读(1152)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017