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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

由unix socket想到的部分文件系统问题  

2012-09-22 23:29:28|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、unix socket
这种套接口感觉在文件系统和套接口中都是一种异类,就好像蝙蝠是兽中的鸟、鸟中的兽一样。它的特点在于一个地址是否被占用是通过一个文件是否存在来确定。这其实是一个比较危险的操作,因为这个文件的存在是持久性创建的文件,会给底层的文件系统造成持久的影响。现在假设说一个程序非正常结束,例如遇到段错误、被管理员通过SIGKILL杀死等原因而异常退出,那么在下次启动的时候,这个文件还是存在的,这就意味着当前即使没有任何进程在使用(侦听)这个套接口,依然没有任何程序可以使用这个地址。
通常程序在使用相同的unix套接口的时候,都会在bind该套接口之前对套接口执行unlink来删除这个文件,这一点在syslog-ng和fastcgi的实现模块中都是这么做的。
迄今为止,还是没有什么问题。但是对于syslog-ng来说,按照linux下syslog函数的实现规则,它们都是打印到/dev/log套接口文件中,然后系统的日志管理工具来侦听这个套接口。同样滴、在执行bind功能之前,日志工具会删除这个文件,然后自己bind的时候在创建这个文件。迄今为止、还是没有问题。同一个系统可能使用多个不同系统日志收集工具,例如syslogd、rsyslogd、syslog-ng,按照syslog的协议,它们都会尝试在/dev/log文件上进行侦听,为了避免绑定失败,它们都会在bind前删除文件。根据linux文件系统的惯例,如果一个文件正在被使用,那么删除是可以的,但是它只是从内存中删除,而不会通过文件系统立即删除。所以每个日志工具启动的时候,之前的日志管理工具同样还是使用之前自己绑定的/dev/log文件,而新的日志工具在unlink之后通过bind再次创建一个新的/dev/log设备文件。
这看起来也没有什么问题。
在C库的syslog实现中,它会在openlog的时候打开这个侦听的套接口,之后每次执行syslog的时候都是使用这个已经打开的文件进行发送操作,只要客户端不重启,它就一直使用第一次打开时使用的文件描述符。同样只要之前的系统日志工具没有重启,它们之间的连接就会一直有效,而此后新启动的任务将会使用新的侦听进程。这就会出现日志记录不一致的问题。
为了说明这个问题,看一下我的系统的一个输出:
[root@Harry bash-4.1]# ps aux | grep syslog
root      5090  0.0  0.0   4220   692 pts/1    S+   22:48   0:00 grep syslog
root     22276  0.0  0.1  45688  1244 ?        Sl   18:16   0:01 rsyslogd
root     23186  0.0  0.0   3564   712 ?        S    18:21   0:00 supervising syslog-ng                                             
root     23187  0.0  0.2   7452  2668 ?        Ss   18:21   0:00 /home/tsecer/Downloads/syslog-ng-3.2.3/syslog-ng/.libs/lt-syslog-ng
root     23303  0.0  0.0   3564   704 ?        S    18:22   0:00 supervising syslog-ng                                             
root     23304  0.0  0.2   7452  2660 ?        Ss   18:22   0:00 /home/tsecer/Downloads/syslog-ng-3.2.3/syslog-ng/.libs/lt-syslog-ng
root     27058  0.0  0.0   3564   712 ?        S    18:44   0:00 supervising syslog-ng                                             
root     27059  0.0  0.2   7452  2668 ?        Ss   18:44   0:00 /home/tsecer/Downloads/syslog-ng-3.2.3/syslog-ng/.libs/lt-syslog-ng
root     27199  0.0  1.5  28580 16008 pts/5    S+   18:44   0:00 gdb /home/tsecer/Downloads/syslog-ng-3.2.3/syslog-ng/.libs/lt-syslog-ng
root     27233  0.0  0.0      0     0 ?        Z    18:45   0:00 [lt-syslog-ng] <defunct>
root     27265  0.0  0.0   3564   576 ?        S    18:45   0:00 supervising syslog-ng                                             
root     27266  0.0  0.2   7452  2644 ?        Ss   18:45   0:00 /home/tsecer/Downloads/syslog-ng-3.2.3/syslog-ng/.libs/lt-syslog-ng
[root@Harry bash-4.1]# netstat -anp | grep /dev/log
unix  2      [ ]         DGRAM                    830241 27266/lt-syslog-ng  /dev/log
unix  3      [ ]         DGRAM                    917054 22276/rsyslogd      /dev/log
unix  2      [ ]         DGRAM                    829315 27059/lt-syslog-ng  /dev/log
unix  2      [ ]         DGRAM                    815520 23304/lt-syslog-ng  /dev/log
unix  2      [ ]         DGRAM                    815017 23187/lt-syslog-ng  /dev/log
[root@Harry bash-4.1]#
这里可以看到,对于同一个/dev/log文件,其中对应了六个不同的inode编号,也就是说系统的内存中当前存在6个不同的进程在侦听不同的系统日志输入。
二、文件延迟删除可能导致的一致性问题
linux中正在使用的文件可以被删除,解决可能一些流氓程序长运行而拒绝被杀死的问题,但是这样的操作可能带来文件系统的不一致。和内存泄露一样,可能会存在inode的泄露。在文件系统中,目录中的目录项dentry就相当于指针,而inode相当于指向的数据结构头,文件内容则相当于数据体。在用文件被删除的时候,相当于该文件在文件系统中的dentry会被优先删除,删除之后该文件就从文件系统中消失,从而可以在文件系统中相同文件夹创建新的同名文件。而该文件使用的inode在磁盘上依然处于占用状态,以保证文件的真正内容在磁盘上一直存在到文件使用周期结束。
假设在这个时候文件系统发生断电,此时文件系统就可能出现不一致的情况。因为文件已经被从文件系统中删除,inode没有,相当于出现了文件泄露,inode本身在磁盘上占据的空间并不多,但是文件本身可能占用了大量的磁盘空间,再加上如果这样的文件很多,那么文件系统就相当于浪费了大量的存储空间。
为了验证这个问题,我在一个minix文件系统中进行了测试。为了使用这么不常见的文件系统呢?当然不是怀旧,更不是为了装那啥,而是在busybox中自带的mkfs中没有ext2的格式化工具,只有dos和minix的,由于ext文件系统是基于minix基础上开发的,所以两者本质上是一样的。

 
由unix socket想到的部分文件系统问题 - Tsecer - Tsecer的回音岛
 其中minix是一个干净的minix分区,然后把busybox拷贝到该文件夹下,后台执行sleep命令以确保该文件长期运行,然后从磁盘中删除该可执行文件。可以看到,删除文件之后,文件系统中的inode使用数量并没有减少,也就是说可执行文件的索引已经从硬盘中删除,而inode没有回收,此时如果断电系统,那么这个不一致将会一直保存。
之后我断电(不是重启)了系统,然后重启,再次挂载该文件系统的时候,minix系统会有下面提示:
MINIX-fs: mounting unchecked file system,running fsck is recommended
也就是说文件系统并没有默默的接收这一错误,而是在挂载文件的时候给出了善意的提示。看了一下ext2文件系统的实现,同样的操作步骤也会造成ext的这样提示。
三、文件系统如何做到这种检测
搜索一下内核中关于这个提示的位置,其位置位于
static int minix_fill_super(struct super_block *s, void *data, int silent)
    if (!(s->s_flags & MS_RDONLY)) {
        if (sbi->s_version != MINIX_V3) /* s_state is now out from V3 sb */
            ms->s_state &= ~MINIX_VALID_FS;如果不是以只读形式挂载的文件系统,则在文件系统挂载的时候将硬盘超级块的状态在内存中复制一份,然后无条件清除硬盘中超级块的状态为不一致状态
        mark_buffer_dirty(bh);
    }
    if (!(sbi->s_mount_state & MINIX_VALID_FS))如果硬盘中上次保存的状态为不一致状态,给出提示
        printk("MINIX-fs: mounting unchecked file system, "
            "running fsck is recommended\n");
     else if (sbi->s_mount_state & MINIX_ERROR_FS)
        printk("MINIX-fs: mounting file system with errors, "
            "running fsck is recommended\n");
那么硬盘中的这种状态是在什么时候清除的呢?对应的(不是明显地)清除是在超级块的删除,也就是文件系统的卸载中修改为一致状态。
static void minix_put_super(struct super_block *sb)

    int i;
    struct minix_sb_info *sbi = minix_sb(sb);

    if (!(sb->s_flags & MS_RDONLY)) {
        if (sbi->s_version != MINIX_V3)     /* s_state is now out from V3 sb */
            sbi->s_ms->s_state = sbi->s_mount_state;
        mark_buffer_dirty(sbi->s_sbh);
    }
在卸载文件系统的时候,挂载前文件系统中保存的状态将会被重新写回到硬盘中。由于新的硬盘在格式化之后是一致状态,如果之后每次挂载和卸载一一对应,那么任意多次挂在和卸载之后,文件系统的状态应该和起始状态一致,那就是出于合法状态。但是如果某此挂载之后没有正常卸载,那么之后硬盘中就会记录这种不一致状态。
四、df如何实现
通过strace可以看到,df是通过fstatfs工具来获得文件系统的特征的,包括inode的总量和在用量。这里需要提醒的是,这个df是disk filesystem的意思,而不是disk format,所以大家不用担心这个工具会格式化分区。
minix文件系统的底层实现比较简单
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf)
{
    struct minix_sb_info *sbi = minix_sb(dentry->d_sb);
    buf->f_type = dentry->d_sb->s_magic;
    buf->f_bsize = dentry->d_sb->s_blocksize;
    buf->f_blocks = (sbi->s_nzones - sbi->s_firstdatazone) << sbi->s_log_zone_size;
    buf->f_bfree = minix_count_free_blocks(sbi);
    buf->f_bavail = buf->f_bfree;
    buf->f_files = sbi->s_ninodes;
    buf->f_ffree = minix_count_free_inodes(sbi);
    buf->f_namelen = sbi->s_namelen;
    return 0;
}
五、简单流程整理
1、文件删除是目录项删除
ext2_unlink--->>ext2_delete_entry
    if (pde)
        from = (char*)pde - (char*)page_address(page);
    lock_page(page);
    err = mapping->a_ops->prepare_write(NULL, page, from, to);
    BUG_ON(err);
    if (pde)
        pde->rec_len = cpu_to_le16(to-from);dentry内容被合并入前一dentry中
    dir->inode = 0;应用的inode清零
此时在iput的时候文件应用不为零,所以文件不能删除。
2、文件删除
当使用该文件的进程退出之后,关闭文件描述符,内存inode引用为零,并且nlink为零,所以需要从文件系统中删除该文件,大致流程为
minix_delete_inode--->>minix_free_inode
    bh = sbi->s_imap[ino];
    lock_kernel();
    if (!minix_test_and_clear_bit(bit, bh->b_data))
        printk("minix_free_inode: bit %lu already cleared\n", bit);
其中sbi->imap就是statfs中minix_count_free_inodes中扫描的数据结构位图。
  评论这张
 
阅读(1034)| 评论(0)
推荐 转载

历史上的今天

评论

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

页脚

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