精华内容
下载资源
问答
  • 什么是指针? 举个生活中的例子,当一个人披上护士服,我们把这个人叫做护士,当这个人披上军服,我们又会把这个人...在计算机里,内存是以字节为单位进行组织的,每一个字节都会对应一个内存地址。内存地址中,保...

    什么是指针?

    举个生活中的例子,当一个人披上护士服,我们把这个人叫做护士,当这个人披上军服,我们又会把这个人叫做军人。

    同样的道理,当一个变量保存的是内存中的一个值的时候,那么这个变量就叫做普通变量,而当这个变量保存的是一个内存地址的时候,那么这个变量就叫做指针。如果还是不理解,那么就来看下面的图。

    在计算机里,内存是以字节为单位进行组织的,每一个字节都会对应一个内存地址。内存地址中,保存的则是具体的数据,很明显,我们想要获取某个数据,就必须找到它所在的内存地址,通过内存地址去找它。

    上面我说过,指针,本质上就是一个保存着内存地址的变量。下面我们就通过代码来看,在C语言里,指针是如何通过代码体现的。

    先定义一个int类型的变量,变量名是number

    int number = 10;

    接下来创建一个指针

    int    *p   ;

    注意这里的p就是指针的名字,其实就是一个变量名,只不过它保存的是一个内存地址,为了和普通变量以示区分,所以才叫指针。

    那么接下来,我们就往p里保存内存地址。

    上面我说过,一个数据既然保存在内存中,那么该数据就必然有它对应的内存地址,比如变量number 所保存的10,这个10它所在 的内存地址要怎么获取呢?

    答案很简单,只需要通过&number,就表示获取10所在的内存地址。因为10是保存在变量里的,所以&number就相当于是获取10所在的内存地址。明白了这点后,那么就好办了,如下代码就表示,将&number的地址保存到指针p里。

    p = &number;

    下面我们就来通过完整的程序,将p里面的内存地址输出来看一下

    #include "CMakeProject1.h"
    
    using namespace std;
    
    int main()
    {
    	int number = 10;
    
    	int   *p = &number;
    	
    	printf("%x", p);
    	system("pause");
    	return 0;
    }

    运行结果如下

    这里说明下,%x,表示将p里的值以16进制的形式展示。

    注意这里的12ff92c就是number的内存地址,数据10就是保存在这个内存地址里。

    我们可以通过内存分析工具,亲自去看一下这个内存地址是不是真的保存了一个10。

    现在跟我做,根据下图,依次点击到内存1

    在打开的窗口,按照如下输入

    现在你明白指针是什么了吧,它其实就是一个保存着内存地址的变量,我们将这种变量叫做指针。

    现在再来学习一个操作,就是通过指针获取number中的值,代码如下

    这里需要注意的是,%d是一个占位符,它表示将*p获取到的值以整数展示。

    展开全文
  • Linux多线程与同步

    千次阅读 2013-04-19 22:40:55
    在Linux进程基础中提到,Linux进程为单位组织操作,Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统,但Linux的多线程在逻辑和使用上与真正的多线程并没有差别。   1. 多进程 我们先来看一下...

    典型的UNIX系统都支持一个进程创建多个线程(thread)。在Linux进程基础中提到,Linux以进程为单位组织操作,Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统,但Linux的多线程在逻辑和使用上与真正的多线程并没有差别。

     

    1. 多进程

    我们先来看一下什么是多线程。在Linux从程序到进程中,我们看到了一个程序在内存中的表示。这个程序的整个运行过程中,只有一个控制权的存在。当函数被调用的时候,该函数获得控制权,成为激活(active)函数,然后运行该函数中的指令。与此同时,其它的函数处于离场状态,并不运行。如下图所示:

    Linux从程序到进程

     

    我们看到,各个方块之间由箭头连接。各个函数就像是连在一根线上一样,计算机像一条流水线一样执行各个函数中定义的操作。这样的一个程序叫做单线程程序。


    多线程就是允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行。即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。如下图所示,就是一个多线程的流程:

    main()到func3()再到main()构成一个线程,此外func1()和func2()构成另外两个线程。操作系统一般都有一些系统调用来让你将一个函数运行成为一个新的线程。

     

    回忆我们在Linux从程序到进程中提到的stack的功能和用途。由于stack中只有最下方的stack frame可以被读取,所以我们只能有该frame对应的单一函数处于激活状态。为了实现多线程,我们必须绕开stack带给我们的限制。为此,当我们创建一个新的线程的时候,我们为这个线程创建一个新的stack。当该stack执行到全部弹出的时候,该线程完成任务并结束。所以,多线程的进程在内存中会有多个stack,相互之间以一定的空白区域隔开,以备stack的增长。每个线程随时可以使用自己stack中最下方的stack frame中的参数和变量,并与其它线程共享内存中的Text,heap和global data区域。在上面的例子中,我们将在进程空间中有三个stack。

    (要注意的是,对于多线程来说,由于同一个进程空间中存在多个stack,任何一个空白区域被填满都会导致stack overflow的问题。)

     

    2. 并发

    多线程相当于一个并发(concunrrency)系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时写入某个变量的时候,就需要解决同步的问题。比如说,我们有一个多线程火车售票系统,用全局变量i存储剩余的票数。多个线程不断地卖票(i = i - 1),直到剩余票数为0。所以每个都需要执行如下操作:

    复制代码
    /*mu is a global mutex*/

    while
    (1) {    /*infinite loop*/ if (i != 0) i = i -1 else { printf("no more tickets"); exit(); } }
    复制代码

    如果只有一个线程执行上面的程序的时候(相当于一个窗口售票),则没有问题。但如果多个线程都执行上面的程序(相当于多个窗口售票), 我们就会出现问题。我们会看到,其根本原因在于同时发生的各个线程都可以对i读取和写入。

    我们这里的if结构会给CPU两个指令, 一个是判断是否有剩余的票(i != 0), 一个是卖票 (i = i -1)。某个线程会先判断是否有票(比如说此时i为1),但两个指令之间存在一个时间窗口,其它线程可能在此时间窗口内执行卖票操作(i = i -1),导致该线程卖票的条件不再成立。但该线程由于已经执行过了判断指令,所以无从知道i发生了变化,所以继续执行卖票指令,以至于卖出不存在的票 (i成为负数)。对于一个真实的售票系统来说,这将成为一个严重的错误 (售出了过多的票,火车爆满)。

    在并发情况下,指令执行的先后顺序由内核决定。同一个线程内部,指令按照先后顺序执行,但不同线程之间的指令很难说清除哪一个会先执行。如果运行的结果依赖于不同线程执行的先后的话,那么就会造成竞争条件(race condition),在这样的状况下,计算机的结果很难预知。我们应该尽量避免竞争条件的形成。最常见的解决竞争条件的方法是将原先分离的两个指令构成不可分隔的一个原子操作(atomic operation),而其它任务不能插入到原子操作中。

     

    3. 多进程同步(synchronization)

    对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源 。而在此时间内,不允许其它的线程访问该资源。我们可以通过互斥锁(mutex)条件变量(condition variable)读写锁(reader-writer lock)来同步资源。

     

    1) mutex

    mutex是一个特殊的变量,它有锁上(lock)和打开(unlock)两个状态。mutex一般被设置成全局变量。打开的mutex可以由某个线程获得。一旦获得,这个mutex会锁上,此后只有该线程有权打开。其它想要获得mutex的线程,会等待直到mutex再次打开的时候。我们可以将mutex想像成为一个只能容纳一个人的洗手间,当某个人进入洗手间的时候,可以从里面将洗手间锁上。其它人只能在mutex外面等待那个人出来,才能进去。在外面等候的人并没有排队,谁先看到洗手间空了,就可以首先冲进去。

    上面的问题很容易使用mutex的问题解决,每个进程的程序可以改为:

    复制代码
    /*mu is a global mutex*/

    while (1) { /*infinite loop*/ mutex_lock(mu);       /*aquire mutex and lock it, if cannot, wait until mutex is unblocked*/ if (i != 0) i = i - 1; else { printf("no more tickets"); exit(); } mutex_unlock(mu);     /*release mutex, make it unblocked*/ }
    复制代码

    第一个执行mutex_lock()的线程会先获得mu。其它想要获得mu的线程必须等待,直到第一个线程执行到mutex_unlock()释放mu,才可以获得mu,并继续执行线程。所以线程在mutex_lock()和mutex_unlock()之间的操作时,不会被其它线程影响,就构成了一个原子操作

    需要注意的时候,如果存在某个进程依然使用原先的程序 (即不尝试获得mu,而直接修改i),mutex不能阻止该程序修改i,mutex就失去了保护资源的意义。所以,mutex机制需要程序员自己来写出完善的程序来实现mutex的功能。我们下面讲的其它机制也是如此。

     

    2) condition variable

    condition variable是另一种常用的变量。它也常常被保存为全局变量,并和mutex合作。

     

    假设我们有这样一个状况: 我们有100个工人,每人负责装修一个房间。当有10个房间装修完成的时候,我们就通知相应的十个工人一起去喝啤酒。我们如何实现呢?我们可以让工人在装修好房间之后,去检查已经装修好的房间数。但多线程条件下,会有竞争条件的危险(其他工人会在该工人装修好房子和检查之间完成工作)。

    复制代码
    /*mu: global mutex, cond: global codition variable, num: global int*/
    mutex_lock(mu) num
    = num + 1; /*worker build the room*/ if (num <= 10) { /*worker is within the first 10 to finish*/ cond_wait(mu, cond);     /*wait*/ printf("drink beer"); } else if (num = 11) { /*workder is the 11th to finish*/ cond_broadcast(mu, cond);        /*inform the other 9 to wake up*/ } mutex_unlock(mu);
    复制代码

    通常, condition variable除了要和mutex配合之外,还需要和另一个全局变量配合(这里的num, 也就是装修好的房间数)。这个全局变量用来构成各个条件。

    我们让工人在装修好房间(num = num + 1)之后,去检查已经装修好的房间数( num < 10 )。由于mu被锁上,所以不会有其他工人在此期间装修房间(改变num的值)。如果该工人是前十个完成的人,那么我们就调用cond_wait()函数。
    cond_wait()做两件事情,一个是释放mu,从而让别的工人可以建房。另一个是等待,直到cond的通知。这样的话,符合条件的线程就开始等待。当有通知(第十个房间已经修建好)到达的时候,condwait()会再次锁上mu,并恢复线程的运行,我们会执行下一句prinft("drink beer") (我们以此来代表喝啤酒)。此后直到mutex_unlock()就构成了另一个mutex结构。

    那么如何让前面十个调用cond_wait()的线程得到通知呢?我们注意到if还有另一种可能,也就是修建好第11个房间的人负责调用cond_broadcast()。它会给所有调用cond_wait()的进程放送通知,以便让那些进程恢复运行。

     

    Condition variable特别适用于多个线程等待某个条件的发生。如果不使用Condition variable,那么每个进程就需要不断尝试获得mutex并检查条件是否发生,这样大大浪费了系统的资源。

     

    3) reader-writer lock

    Reader-writer lock与mutex非常相似。r、RW lock有三种状态: 共享读取锁(shared-read)互斥写入锁(exclusive-write lock), 打开(unlock)。后两种状态与之前的mutex两种状态完全相同。

    一个unlock的RW lock可以被某个进程获取R锁或者W锁。

    如果被一个进程获得R锁,RW lock可以被其它进程继续获得R锁,而不必等待该进程释放R锁。但是,如果此时有其它进程想要获得W锁,它必须等到所有持有共享读取锁的进程释放掉各自的R锁。

    如果一个锁被一个进程获得W锁,那么其它进程,无论是想要获取R锁还是W锁,都必须等待该进程释放W锁。

    这样,多个进程就可以同时读取共享资源。而具有危险性的写入操作则得到了互斥锁的保护。

     

    我们需要同步并发系统,这为程序员编程带来了难度。但是多线程系统可以很好的解决许多IO瓶颈的问题。比如我们监听网络端口。如果我们只有一个线程,那么我们必须监听,接收请求,处理,回复,再监听。如果我们使用多线程系统,则可以让多个线程监听。当我们的某个线程进行处理的时候,我们还可以有其他的线程继续监听,这样,就大大提高了系统的利用率。在数据越来越大,服务器读写操作越来越多的今天,这具有相当的意义。多线程还可以更有效地利用多CPU的环境。

    (就像做饭一样,不断切换去处理不同的菜。)

     

    本文中所使用的程序采用伪C的写法。不同的语言有不同的函数名(比如mutex_lock)。这里关注的是逻辑上的概念,而不是具体的实现和语言规范。

     

    总结:

    multiple threads, multiple stacks

    race condition

    mutex, condition variable, RW lock

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!
    展开全文
  • 持久性的数据是存储在外部磁盘上的【注1】,如果没有文件系统,访问这些...因为磁盘上的数据要和内存交互,而内存通常是以4KB为单位的,所以从逻辑上,把磁盘按照4KB划分比较方便(称为一个block)。现在假设由一个...

    951dc5cac8e3a30a86eeb9996896fd28.png

    持久性的数据是存储在外部磁盘上的【注1】,如果没有文件系统,访问这些数据就需要直接读写磁盘的sector,实在太不方便了。而文件系统存在的意义,就是能更有效的组织、管理和使用磁盘上的这些raw data。

    文件系统的组成

    要管理,就得先划分,那按照什么粒度划分呢?因为磁盘上的数据要和内存交互,而内存通常是以4KB为单位的,所以从逻辑上,把磁盘按照4KB划分比较方便(称为一个block)。现在假设由一个文件系统管理64个blocks的一个磁盘区域:

    c42d2e608d206c5827730b7aaec14593.png
    empty disk

    那在这些blocks中应该存储些什么?文件系统的基础要素自然是文件,而文件作为一个数据容器的逻辑概念,本质上是一些字节构成的集合,这些字节就是文件的user data(对应下图的"D")。

    ca972d974ba32eba60b315b8fc3f2b97.png
    user data

    除了文件本身包含的数据,还有文件的访问权限、大小和创建时间等控制信息,这些信息被称为meta data。meta data可理解为是关于文件user data的data,这些meta data存储的数据结构就是inode(对应下图的"I",有些文件系统称之为dnode或fnode)。

    inode是"index node"的简称,在早期的Unix系统中,这些nodes是通过数组组织起来的,因此需要依靠index来索引数组中的node。假设一个inode占据256字节,那么一个4KB的block可以存放16个inodes,使用5个blocks可以存放80个inodes,也就是最多支持80个文件。

    94e723362e1b839172ec66a991c0b2f8.png
    meta data + user data

    同内存分配一样,当有了新的数据产生时,我们需要选择一个空闲的block来存放数据,此外还需要一个空闲的inode。所以,需要追踪这些inodes和data blocks的分配和释放情况,以判断哪些是已用的,哪些是空闲的。

    最简单的办法就是使用bitmap,包括记录inode使用情况的bitmap(对应下图的"i"),和记录data block使用情况的bitmap(对应下图的"d")。空闲就标记为0,正在使用就标记为1。

    f6e10be29433d9f8903e6615687196fc.png
    bitmap + meta data + user date

    因为block是最小划分单位,所以这里使用了两个blocks来分别存储inode bitmap和data block bitmap,每个block可以容纳的bits数量是4096*8。而这里我们只有80个inodes和56个data blocks,所以是绰绰有余的。

    还剩下开头的一个block,这个block是留给superblock的(对应下图的"S")。

    b6430b662bf3e67450cd6988e4fc5da7.png
    superblock + bitmap + meta data + user date

    这个superblock包含了一个文件系统所有的控制信息,比如文件系统中有多少个inodes和data blocks,inode的信息起始于哪个block(这里是第3个),可能还有一个区别不同文件系统类型的magic number。因此,superblock可理解为是文件系统的meta data。

    363dc17be5cf4a796d932fb75543c502.png

    文件寻址

    这5个blocks中的80个inodes构成了一个inode table。假设一个inode的大小是256字节,现在我们要访问第32个文件,那就要先找到这个文件的控制信息,也就是第32个inode所在的磁盘位置。它应该在相对inode table起始地址的8KB处(32*256=8192),而inode table的起始地址是12KB,所以实际位置是20KB。

    4b47e7cae83434189515811f82beab62.png
    inode table

    磁盘同内存不同,它在物理上不是按字节寻址的,而是按sector。一个sector的大小通常是512字节,因此换算一下就是第40个sector(20*1024/512)。

    对于ext2/3/4文件系统,以上介绍的这些inode bitmap, data block bitmap和inode table,都可以通过一个名为"dumpe2fs"的工具来查看其在磁盘上的具体位置:

    1b142e04c9900c0efb6fc987bf62ce53.png

    如果只需要查看inode的使用情况,那么直接使用"df -i"命令即可:

    8b77cfd7d4877c01df6a16dbc6c1c0ef.png
    inode usage

    寻址方式

    那通过inode如何找到对应的文件呢?根据大小的不同,一个文件需要占据若干个blocks,这些blocks可能是磁盘上连续分布的,也可能不是。所以,比较好的办法是使用指针,指针存储在inode中,一个指针指向一个block。

    不过,在这个简化的示例里,并不需要C语言里那种指针,只需要一个block的编号就可以了。如果文件比较小,占有的blocks数目就比较少,那么一个256字节的inode就能存储这些指针。假设一个inode最多能包含12个指针,那么文件的大小不能超过48KB。

    那如果超过了怎么办?可由inode先指向一个block,这个block再指向分散的data block,这种方法称为multi-level index。inode在指向中间block的同时,也可以直接指向data block。假设一个指针占据4个字节,那么一个中间block可存储1024个指针,二级index的寻址范围就可超过4MB,三级index则可超过4GB。

    4de5c14b8b02e52858f1d6d5e5ed1d56.png

    有没有觉得很像内存管理中的多级页表?事实上,它们的原理可以说是一样的,文件在磁盘上的实际block分布对等于内存的物理地址,而各级index就对等于内存的虚拟地址。

    这种只使用block指针的方式被ext2和ext3文件系统所采用,但它存在一个问题,对于各种大小的文件,都需要较多的meta data。而在实际的应用中,大多数文件的体积都很小,meta data相对user data的占比就较大。

    另一种实现是使用一个block指针加上一个length来表示一组物理上连续的blocks(称为一个extent,其中length以block为单位计),一个文件由若干个extents构成。这样,小文件所需要的meta data就较少,这种更灵活的实现方式被后来的ext4文件系统所采用。

    struct ext4_extent {
        __le32  ee_block;   /* first logical block extent covers */
        __le16  ee_len;     /* number of blocks covered by extent */
        ...
    };
    

    目录和路径

    各级目录构成了访问文件的路径,不同于windows操作系统的drive分区,类Unix系统中的"mount"操作让所有的文件系统的挂载点都是一个路径,形成了树形结构。从抽象的角度,目录也可视作一种文件,只是这种文件比较特殊,它的user data存储的是该路径下的普通文件的inode编号。

    849127896e957d257dcf231834d29106.png

    所以,如下图所示的这样一个路径结构,假设要在"/foo"目录下创建一个文件"bar.txt",那么需要从inode bitmap中分配一个空闲的inode,并在"/foo"这个目录中分配一个entry,以关联这个inode号。

    b49a4654c33c2bb65706ff04247b1de1.png

    接下来,我们要读取刚才创建的这个"/foo/bar.txt"文件,那么先得找到"/"这个目录文件的inode号(这必须是事先知道的,假设是2)。然后访问这个inode指向的data block,从中找到一个名为"foo"的entry,得到目录文件"foo"的inode号(假设是44)。重复此过程,按图索骥,直到找到文本文件"bar.txt"的inode号。

    小结

    现代文件系统的实现是很庞杂的,但文件系统的原理本身并不深奥,基本的构成要素就是file data,管理各个文件的inode和管理整个文件系统的superblock。基于这些要素,下文将开始介绍读写一个文件的具体方式和接口。

    注1:所谓持久性(persistance),就是指即便面对困难、挑战,依然可以持续,对于设备来说,就是面对掉电、操作系统crash,依然可以保持数据的持久存在。

    参考:

    http://pages.cs.wisc.edu/~remzi/OSTEP/file-implementation.pdf

    https://ext4.wiki.kernel.org/index.php/Ext4_Design

    原创文章,转载请注明出处。

    展开全文
  • 持久性的数据是存储在外部磁盘上的,如果没有文件系统,访问这些数据就需要直接...因为磁盘上的数据要和内存交互,而内存通常是以4KB为单位的,所以从逻辑上,把磁盘按照4KB划分比较方便(称为一个block)。现在假设...
    持久性的数据是存储在外部磁盘上的,如果没有文件系统,访问这些数据就需要直接读写磁盘的sector,实在太不方便了。而文件系统存在的意义,就是能更有效的组织、管理和使用磁盘上的这些raw data。【文件系统的组成】要管理,就得先划分,那按照什么粒度划分呢?因为磁盘上的数据要和内存交互,而内存通常是以4KB为单位的,所以从逻辑上,把磁盘按照4KB划分比较方便(称为一个block)。现在假设由一个文件系统管理64个blocks的一个磁盘区域:

    0c0fe945c2850df50cbc4f4d915c54ec.png

    那在这些blocks中应该存储些什么?文件系统的基础要素自然是文件,而文件作为一个数据容器的逻辑概念,本质上是一些字节构成的集合,这些字节就是文件的user data(对应下图的"D")。

    1ebc731dd52500e9a41a4120a1599e0f.png

    除了文件本身包含的数据,还有文件的访问权限、大小和创建时间等控制信息,这些信息被称为meta data。meta data可理解为是关于文件user data的data,这些meta data存储的数据结构就是inode(对应下图的"I")。inode是"index node"的简称,在早期的Unix系统中,这些nodes是通过数组组织起来的,因此需要依靠index来索引数组中的node。假设一个inode占据256字节,那么一个4KB的block可以存放16个inodes,使用5个blocks可以存放80个inodes,也就是最多支持80个文件。

    7c05800bbd5782a184cf664640e948e5.png

    同内存分配一样,当有了新的数据产生时,我们需要选择一个空闲的block来存放数据。此外,还需要一个空闲的inode。所以需要追踪这些inodes和data blocks的分配和释放情况,以判断哪些是已用的,哪些是空闲的。最简单的办法就是使用bitmap,包括记录inode使用情况的bitmap(对应下图的"i"),和记录data block使用情况的bitmap(对应下图的"d")。空闲就标记为0,正在使用就标记为1。

    848427687bd44ebdd837b495382b5bc2.png

    因为block是最小划分单位,所以这里使用了两个blocks来分别存储inode bitmap和data block bitmap,每个block可以容纳的bits数量是4096*8。而这里我们只有80个inodes和56个data blocks,所以是绰绰有余的。还剩下开头的一个block,这个block是留给superblock的(对应下图的"S")。

    64f294668a189d401faf6d055fcd7555.png

    这个superblock包含了一个文件系统所有的控制信息,比如文件系统中有多少个inodes和data blocks,inode的信息起始于哪个block(这里是第3个),可能还有一个区别不同文件系统类型的magic number。因此,superblock可理解为是文件系统的meta data。

    03006b7db21bc4167673fc57c6006f43.png

    【文件寻址】这5个blocks中的80个inodes构成了一个inode table。假设一个inode的大小是256字节,现在我们要访问第32个文件,那就要先找到这个文件的控制信息,也就是第32个inode所在的磁盘位置。它应该在相对inode table起始地址的8KB处(32*256=8192),而inode table的起始地址是12KB,所以实际位置是20KB。

    a246f3463133770b6b9a1e52e7d81903.png

    磁盘同内存不同,它在物理上不是按字节寻址的,而是按sector。一个sector的大小通常是512字节,因此换算一下就是第40个sector(20*1024/512)。那通过inode如何找到对应的文件呢?根据大小的不同,一个文件需要占据若干个blocks,这些blocks可能是磁盘上连续分布的,也可能不是。所以,比较好的办法是使用指针,指针存储在inode中,一个指针指向一个block。不过,在这个简化的示例里,并不需要C语言里那种指针,只需要一个block的编号就可以了。如果文件比较小,占有的blocks数目就比较少,那么一个256字节的inode就能存储这些指针。假设一个inode最多能包含12个指针,那么文件的大小不能超过48KB。那如果超过了怎么办?可由inode先指向一个block,这个block再指向分散的data block,这种方法称为multi-level index。inode在指向中间block的同时,也可以直接指向data block。假设一个指针占据4个字节,那么一个中间block可存储1024个指针,二级index的寻址范围就可超过4MB,三级index则可超过4GB。

    266268a399e25265096b271949a67a24.png

    有没有觉得很像内存管理中的多级页表?事实上,它们的原理可以说是一样的,文件在磁盘上的实际block分布对等于内存的物理地址,而各级index就对等于内存的虚拟地址。这种只使用block指针的方式被ext2和ext3文件系统所采用,但它存在一个问题,对于各种大小的文件,都需要较多的meta data。而在实际的应用中,大多数文件的体积都很小,meta data相对user data的占比就较大。另一种实现是使用一个block指针加上一个length来表示一组物理上连续的blocks(称为一个extent,其中length以block为单位计),一个文件由若干个extents构成。这样,小文件所需要的meta data就较少,这种更灵活的实现方式被后来的ext4文件系统所采用。【目录和路径】各级目录构成了访问文件的路径,不同于windows操作系统的drive分区,类Unix系统中的"mount"操作让所有的文件系统的挂载点都是一个路径,形成了树形结构。从抽象的角度,目录也可视作一种文件,只是这种文件比较特殊,它的user data存储的是该路径下的普通文件的inode编号。

    b3662827b9f16ae4903373f5f542c11d.png

    所以,如下图所示的这样一个路径结构,假设要在"/foo"目录下创建一个文件"bar.txt",那么需要从inode bitmap中分配一个空闲的inode,并在"/foo"这个目录中分配一个entry,以关联这个inode号。

    3e1a71625e0703a02b4e39c71a2725a8.png

    接下来,我们要读取刚才创建的这个"/foo/bar.txt"文件,那么先得找到"/"这个目录文件的inode号(这必须是事先知道的,假设是2)。然后访问这个inode指向的data block,从中找到一个名为"foo"的entry,得到目录文件"foo"的inode号(假设是44)。重复此过程,按图索骥,直到找到文本文件"bar.txt"的inode号。【小结】现代文件系统的实现是很庞杂的,但文件系统的原理本身并不深奥,基本的构成要素就是file data,管理各个文件的inode和管理整个文件系统的superblock。基于这些要素,下文将开始介绍读写一个文件的具体方式和接口。
    展开全文
  • 文件系统的原理

    2020-11-05 18:28:49
    持久性的数据是存储在外部磁盘上的【注1】,如果没有文件...因为磁盘上的数据要和内存交互,而内存通常是以4KB为单位的,所以从逻辑上,把磁盘按照4KB划分比较方便(称为一个block)。现在假设由一个文件系统管理64个
  • 文件系统需要有严格的组织形式,使文件能够为单位存储 文件系统需要有索引区,方便查找一个文件分成的多个块存在了什么位置 如果有文件近期经常被读写,需要有缓存层 文件应该用文件夹的形式组织起来方便...
  • Linux的文件系统特点文件系统要有严格的组织形式,使得文件能够为单位进行存储。文件系统中也要有索引区,用来方便查找一个文件分成的多个块都存放在了什么位置。如果文件系统中有的文件热点文件,近期经常被...
  • Linux的文件系统特点文件系统要有严格的组织形式,使得文件能够为单位进行存储。文件系统中也要有索引区,用来方便查找一个文件分成的多个块都存放在了什么位置。如果文件系统中有的文件热点文件,近期经常被...
  • C/C++学习笔记(一)

    2013-09-13 14:02:36
    一、数据类型: 基本数据类型(Fundamental Data types) ...计算机的内存是以字节(byte)为单位组织的。一个字节(byte)是我们在C++中能够操作的最小的内存单位。一个字节(byte)可以存储相对较小
  •  GC垃圾收集的意思(Gabage Collection),内存处理编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收...
  • Linux并发与同步

    2021-02-22 19:21:21
    在Linux进程基础中提到,Linux进程为单位组织操作,Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统,但Linux的多线程在逻辑和使用上与真正的多线程并没有差别。 多线程 我们先来看一下什么是多...
  • 在Linux进程基础中提到,Linux进程为单位组织操作,Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统,但Linux的多线程在逻辑和使用上与真正的多线程并没有差别。 linux 多线程我们先来看一下什么是...
  • Linux文件系统

    2020-07-20 21:36:29
    文件系统要有严格的组织形式,使得文件能够为单位进行存储,这个区域称为存放原始资料的仓库区。 文件系统中也要有索引区,用来方便查找一个文件分成的多个块都存放在了什么位置。 如果文件系统中有的文件热点...
  • 第一点,文件系统要有严格的组织形式,使得文件能够为单位进行存储。 第二点,文件系统中也要有索引区,用来方便查找一个文件分成的多个块都存放在了什么位置。 第三点,如果文件系统中有的文件热点文件,近期...
  • 线程与进程

    2020-06-08 01:19:21
    一、进程 1、什么是进程? 进程一种操作系统中非常重要的软件资源,同时也操作系统中的分配资源的最小... linux 例,内核中使用一个 task_struct / PCB(进程控制块)结构体来描述进程,每创建一个进程,就同
  • (3)速度和容量的“时空”矛盾,虛存量的“扩大”是以牺牲CPU工作时间以及内外存交换时间代价的。 •存储管理的目的及功能 目的是方便用户,提高内存资源的利用率,实现内存共享。 功能主要有...
  • C++程序员面试宝典

    热门讨论 2013-04-01 13:36:19
    而招聘单位为了得到高素质的员工往往采用各种形式的面试考察求职者,这让面试难度大大增加。求职者要想成功应聘,不仅需要扎实的基本功,还需要经受情商和智商的考验。 本书通过380余个面试题,对企业招聘C/C++...
  • java 面试题 总结

    2009-09-16 08:45:34
     GC垃圾收集的意思(Gabage Collection),内存处理编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收...
  • T=0、T=1协议的不同之处在于它们数据传输的单位和格式不一样,T=0协议单字节的字符基本单位,T=1协议则有一定长度的数据块传输的基本单位。  如果传送管理器认为对命令的接收正确的,那么,它一般只将...
  • 内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。 现代的操作系统为了最大利用内存,在内存中存放了缓存,因此内存利用...
  • 精读内核源码当然件很困难的事,时间上要月甚至年为单位,但正因为很难才值得去做! 干困难事,必有所得. 专注聚焦,必有所获. 致敬内核开发者,从内核一行行的代码中能深深体会到开发者各中艰辛与坚持,及鸿蒙生态对...
  • oracle数据库经典题目

    2011-02-17 15:05:20
    模式一系列逻辑数据结构或对象的集合,数据库中对象的组织和管理单位。 2. 简要游标的作用和游标操作的基本步骤。 答案: 游标的作用将数据库的中数据检索出来后缓存,可以被PL/SQL程序一行一行的读取并处理...

空空如也

空空如也

1 2 3
收藏数 51
精华内容 20
关键字:

内存是以什么为单位组织的