关闭

Unix系统编程(2) - 文件系统

标签: unix编程文件系统
1425人阅读 评论(0) 收藏 举报
分类:

文件系统需要解答以下两个问题:

  • 1 文件系统在内核中时如何实现的?如何呈现给用户一个树状的目录结构?如何处理用户的文件和目录操作请求?

  • 2 磁盘是一种顺序的存储介质,一个树状的目录结构如何存到磁盘上?怎样设计文件系统的存储格式使访问磁盘的效率最高?各种文件和目录操作在磁盘上的实际效果是什么?

在Windows系统中一般我们使用的是NTFS文件系统(早期的windows中使用的时FAT32系统)。目前U盘的文件系统也大多时FAT32。但是Linux的文件系统是完全不一样的。Linux文件系统有ext2, ext3, ext4。ext3和ext4也只是在原来的文件系统上作了一些扩展。所以学习的角度来看一般都学习ext2文件系统。

0. 从设计文件系统来看

假设现在我们有一个名字为hello的文件需要存储到磁盘中,那么我们应该怎样去设计文件系统来存储这个文件呢?
用最简单的方式考虑,很自然的我们会想到使用一种类似键值对的形式进行存储,每个文件存储他的文件名,文件存储的起始位置以及文件的大小。乍一看好像这样确实能将我们的文件存到磁盘中。随之的问题又来了,存储了这个文件之后,假设我们现在想修改这个文件。由于该文件的随后的磁盘位置很可能已经被占用了,我们就不能把追加的内容直接写在原文件的后面。
从设计文件系统的角度来看,文件系统设计的目的就是方便我们将文件存储到磁盘,方便我们从磁盘中去取得文件。FAT32,NTFS,ext2等各种文件系统都是基于着一个目标的。

1. ext2文件系统

ext2文件系统中磁盘的按照下图的方式进行分割:

ext2文件系统
详细描述

1.1 Boot Block(启动块)

所有的文件系统都有这个块(而且这个块的大小都是一样的(1KB),肯定要一样,不然怎么读取磁盘的内容),这个块存储着磁盘的分区信息和启动信息,任何文件都不能使用启动块。windows系统安装的时候有个MBR,其实就是这个块。

1.2 Super Block (超级块)

超级块描述整个分区的文件系统信息,例如:块大小、文件系统版本号,上次mount时间等等。
超级块在每个块组的开头都有一份拷贝

1.3 Group Description Table (GDT, 块组描述符表)

块组描述符由很多的块组描述符组成,整个分区分成多少个块组就对应有多少块组描述符。每个块组描述符存储一个块组的描述信息,例如这个块组从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。
和超级块类似,块组描述表在每个块组的开头都有一个拷贝,这些信息非常的重要,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此他们都有多份拷贝。
通常,内核只用地第0个块组中的拷贝,当执行e2fsck检查文件系统一致性时,第0个块组中的超级块组中的超级块和块组描述符就会拷贝到其他块组,这样当第0个块组的开头意外损坏的时候就可以从用其他的拷贝来恢复,从而减少损失。

1.4 Block Bitmap (块位图)

用来标注那些块使用了,哪些快没有使用。在文件删除的时候就是在此操作的,文件删除的时候并不是去写掉数据块的内容,而是将块位图写为未用,这样下次有数据要写入的时候就可以使用了,而且这样“删除”的速度很快。这也就是磁盘数据恢复的原理。

1.5 inode Bitmap(inode位图)

用来标注inode的使用情况。

1.6 inode Table (inode表)

由若干个inode组成,文件属性都是在inode里面。

1.7 数据块

保存文件的数据。前面说到一个数据块的大小是固定的,那么如果有一个非常大的文件想要存储到磁盘中该怎么解决呢?这就涉及到了数据的寻址,ext2文件系统inode中的数据指针共有15个,前面的12个位直接寻址指针,后面3个分别为一级,二级和三级间接寻址:


数据块寻址

那按照这样的放射能不能存大文件呢?可以计算假设块大小为1KB,那么最大的文件大小可以达到16GB。
这里就有一个问题:在磁盘的分区的时候如何分配合适的块大小来满足我们的使用?也就是说当我们系统需要存的都是很小的文件,那么我们就需要更多的文件描述符,块大小就可以设置较小;当我们系统存储大多数都是大文件的时候我们就可以将块设置较大。

数据块根据不同的文件类型有以下几种情况:
- 1 对于常规文件,文件的数据存储在数据块中。
- 2 于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外, ls -l 命令看到的其它信息都保存在该文件的inode中。
注意: 目录也是一种文件,是一种特殊类型的文件。
- 3 对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
- 4 设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。

2 文件和目录访问相关API函数

2.1 stat, fstat, lstat, fstatat文件相关信息

NAME
       stat, fstat, lstat, fstatat - get file status

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       int stat(const char *pathname, struct stat *buf);
       int fstat(int fd, struct stat *buf);
       int lstat(const char *pathname, struct stat *buf);

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <sys/stat.h>

       int fstatat(int dirfd, const char *pathname, struct stat *buf,
                   int flags);

使用时一般带入文件路径或者文件描述就可以获取到该文件的属性,获得的结果以结构体stat的形式给出。

系统内核中给struct stat 结构体定义了:

       struct stat 
           {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* inode number */
               mode_t    st_mode;        /* protection */
               nlink_t   st_nlink;       /* number of hard links */
               uid_t     st_uid;         /* user ID of owner */
               gid_t     st_gid;         /* group ID of owner */
               dev_t     st_rdev;        /* device ID (if special file) */
               off_t     st_size;        /* total size, in bytes */
               blksize_t st_blksize;     /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */
               struct timespec st_mtim;  /* time of last modification */
               struct timespec st_ctim;  /* time of last status change */

               #define st_atime st_atim.tv_sec      /* Backward compatibility */
               #define st_mtime st_mtim.tv_sec
               #define st_ctime st_ctim.tv_sec
           };

mode_t 文件类型

           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO


      使用下述函数来判断文件类型,实际上就是判断某位上的值是否为1.

           S_ISREG(m)  is it a regular file?
           S_ISDIR(m)  directory?
           S_ISCHR(m)  character device?
           S_ISBLK(m)  block device?
           S_ISFIFO(m) FIFO (named pipe)?
           S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)
           S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)
  • example: 使用stat函数查看文件属性
/*************************************************************************
    > File Name: stat.c
    > Author: Robin
    > Mail: chou_robin@163.com 
    > Created Time: 2016年06月01日 星期三 14时56分55秒
 ************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
    struct stat sb;
    if(argc != 2)
    {
        fprintf(stderr, "Usage: %s <pathname>\n",argv[0]);
        exit(1);
    }

    if(stat(argv[1], &sb) == -1)
    {
        perror("stat");
        exit(1);
    }

    printf("File type:       ");

    switch(sb.st_mode & S_IFMT)
    {
        case S_IFBLK:  printf("block device\n"); break;
        case S_IFCHR:  printf("character device\n"); break;
        case S_IFDIR:  printf("dirctory\n"); break;
        case S_IFIFO:  printf("FIFO/pipe\n"); break;
        case S_IFLNK:  printf("symlink\n"); break;
        case S_IFREG:  printf("regular file\n"); break;
        case S_IFSOCK: printf("socket\n"); break;
        default: printf("unkonwn\n"); break;
    }

    printf("I-node number:%ld\n", (long) sb.st_ino);
    printf("Mode: %lo (octal)\n",  (unsigned long) sb.st_mode);
    printf("Link count:  %ld\n", (long) sb.st_nlink);
    printf("Ownership: UID=%ld   GID=%ld\n",(long) sb.st_uid, (long) sb.st_gid);
    printf("Preferred I/O block size: %ld bytes\n",(long) sb.st_blksize);
    printf("File size: %lld bytes\n",(long long) sb.st_size);
    printf("Blocks allocated: %lld\n",(long long) sb.st_blocks);
    printf("Last status change:       %s", ctime(&sb.st_ctime));
    printf("Last file access:         %s", ctime(&sb.st_atime));
    printf("Last file modification:   %s", ctime(&sb.st_mtime));
    exit(EXIT_SUCCESS);                 
}


运行结果

2.2 access, faccessat 检查用户对文件的操作

NAME
       access, faccessat - check user's permissions for a file

SYNOPSIS
       #include <unistd.h>

       int access(const char *pathname, int mode);

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <unistd.h>

       int faccessat(int dirfd, const char *pathname, int mode, int flags);

mode 说明
R_OK 测试读权限
W_OK 测试写权限
X_OK 测试执行权限
F_OK 文件是否存在

example

/*************************************************************************
    > File Name: access.c
    > Author: Robin
    > Mail: chou_robin@163.com 
    > Created Time: 2016年06月01日 星期三 15时52分50秒
 ************************************************************************/
#include<stdio.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        fprintf(stderr,"args num: %d error\n", argc);
        exit(1);
    }
    int rt;
    if((rt = access(argv[1], R_OK)) == -1)
    {
        fprintf(stderr,"Error: ", perror);
        exit(1);
    }
    printf("Read:%d\n",rt);
    return 0;
}
2
0
查看评论

linux/unix系统编程

年前换了工作,主要是因为懒,所以没怎么写博客了。这段时间主要是在看,看新工作项目中的代码,还有一些开源代码,例如glide。现在没有三个移动端(android,ios,h5)一起写了,专注于安卓。最近在看linux系统编程方面的东西。之前看了不少文章,很多大牛修问题都是从系统的角度来解决问题,刚好新...
  • yeshennet
  • yeshennet
  • 2017-02-21 14:44
  • 709

UNIX系统编程.pdf

  • 2014-01-29 11:14
  • 24.00MB
  • 下载

UNIX系统编程

  • 2008-07-13 14:50
  • 23.97MB
  • 下载

Linux_UNIX系统编程手册pdf(上下册)

  • 2017-04-25 08:39
  • 394.23MB
  • 下载

TLPI UNIX linux系统编程手册源代码运行

Title: TLPI-源代码运行 date: 2017-09-20 23:55 url: TLPI-begin 上周末开始看《UNIX/Linux系统编程手册》这本书,周末的时间看到第四章,觉得这本书比直接看Linux内核要好接受的多,因为从代码入手,可以更好的理解,比如第三章系统调用的讲...
  • persever
  • persever
  • 2017-09-21 20:37
  • 374

Linux-Unix系统编程手册学习笔记

Linux-Unix系统编程手册学习笔记
  • wp1603710463
  • wp1603710463
  • 2015-12-19 22:45
  • 1204

《Linux/Unix系统编程手册》中的例子在Linux下运行的方法

楼主菜鸟,从《Linux/Unix系统编程手册》学起,学程序肯定先运行例子进行玩耍,可是搞了1个多小时也不知道书中的例子该怎么运行,百度不出来,随便打了个make命令喝了杯水,发现了多出了个XX.a文件,把问题解决了!拿出来和大家分享下,不喜勿喷,O(∩_∩)O哈哈~ 环境:win7下VMware...
  • NeOIOIIOIO
  • NeOIOIIOIO
  • 2016-08-03 22:14
  • 411

读书笔记之linux/unix系统编程手册(20)

信号:基本概念 1.信号是事件发生时对进程的通知机制。有时也成为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程,大多数情况下,无法预测信号到达的精确时间 2.一个进程能够向另一个进程发送信号。信号的这一用法可作为一种同步技术,甚至是进程间通信的(IPC)的原始形式。进程也可向自身发送...
  • yangkaikwill
  • yangkaikwill
  • 2015-08-23 16:15
  • 764

Unix式图形化操作系统开发笔记1:环境搭建

为了让读者可以方便地编译运行本教程的代码,笔者将在此介绍如何搭建开发环境,使用统一的linux操作系统和编译器。因为笔者使用的是mac os,所以一直到安装虚拟机之前都会在mac os下操作。第一部分:Ubuntu虚拟机第一步:虚拟机 1 下载安装Virtualbox。 Virtualbox有w...
  • szhou42
  • szhou42
  • 2016-06-07 02:42
  • 364

深入理解Linux文件系统编程(一)

深入理解Linux文件系统编程(一)    Jiangdg_VIP http://blog.csdn.net/u012637501        文件系统是linux操作系统组织系统资源的一种方式,操作系统的可用性...
  • u012637501
  • u012637501
  • 2014-11-12 10:33
  • 1482
    个人资料
    • 访问:227889次
    • 积分:3542
    • 等级:
    • 排名:第11087名
    • 原创:108篇
    • 转载:10篇
    • 译文:0篇
    • 评论:106条
    博客专栏
    最新评论