深入理解Lustre文件系统-第3篇 lustre lite

【Lustre相关】应用部署-02-Lustre软件源码编译 本篇主要介绍Lustre软件源码编译相关过程及问题处理,供以后参考使用 阅读详情

在这一节,我们将描述Lustre Lite怎样接入Linux VFS并与之融为一体,这对于支持VFS语义和POSIX接口非常必要。作为概要,Lustre Lite以方法表(method table)的形式提供了如下函数:

  • Lustre特有的文件操作,通过ll_file_operations表。
  • Lustre特有的dentry操作,通过ll_d_ops和它的cache。
  • Lustre特有的directory操作,通过ll_dir_operations表。
  • Lustre特有的索引节点操作,通过ll_dir_inode_operations和file_inode_operations表。
  • Lustre特有的文件映射操作,通过ll_file_vm_ops表。
  • 和其他的,诸如Lustre超级块操作、地址空间等。

3.1    VFS连接

图3给出了不同的数据结构怎样连接起来的整体视图。一些重要的事情将在下面解释。

一个进程维护了一个相关开启文件(associated open files)列表。每个打开的文件中都以一个在内存中的称为file的对象作为代表。它存储了在打开的文件和进程之间进行交互所需要的信息。对用户(To the userland),它以文件句柄fd的形式给出。这个数据结构包含一个字段(field),f_op,它类似于电话总机或者指向方法列表的指针,给出了每个文件系统各自特定的函数。这样,一个使用系统调用sys_read()的文件读操作就变为:

file->f_op->read(...);


3.1.1   Dentry对象

在file结构体中定义的另外一个字段是f_dentry,它指向一个存储在dentry cache(即所谓dcache)中的dentry对象(struct dentry)。实质上,VFS在文件和文件夹将被首次访问的时候就会创建一个dentry对象。如果这是一个不存在的文件/文件夹,那么将会创建一个无效的dentry。例如,对于如下路径名:home/bob/research08;它由四个路径部件构成:/,home,bob和research08。相应的,路径查找(lookup)将创建四个dentry,每个部件一个。每个dentry对象通过字段 d_inode指定的索引节点与各自对应的部件相联系。

索引节点对象以一个唯一的索引节点号作为标识,存储了一个特定文件的信息。ULK3为索引节点结构体提供全面的字段定义列表。非常重要的是,第一,i_sb指向了VFS超级块,第二,i_op是一个诸如如下索引节点操作的switchboard:

create(dir,dentry, mode, nameidata)

mkdir(dir, dentry,mode)

lookup(dir,dentry,nameidata)

...

第一个方法为一个常规文件创建了新的磁盘索引节点,该索引节点与某个文件夹中的dentry对象相关联。第二个方法为文件夹创建了新索引节点,该索引节点与某个文件夹下的某个dentry对象相关联。而第三个方法在一个文件夹中搜索一个索引节点,该索引节点与一个dentry对象中包含的文件名相关联。

4.1.2   Lustre超级块

VFS层定义了一个一般性的超级块对象(struct super_block),它存储了所挂载(mount)的文件系统的信息。特定的字段 s_fs_info指向属于特定文件系统的超级块信息。特别地,在Lustre中,这种文件系统特有的信息以结构体 lustre_sb_info的形式表现出来,其中存储了挂载和卸载文件系统所需要的信息。它进一步地连接到另外一个结构体ll_sb_info,其中包含了更多Lustre Lite特有的关于文件系统状态的信息,这些信息只为客户端准备。

Lustre特有的超级块操作定义在结构体变量lustre_super_operations中。初始化正确的超级块操作,是在我们在函数client_common_fill_super()中最初建立内存中超级块数据结构时进行的:

sb->s_op =&lustre_super_operations;

值得指出的是,当创建Lustre文件时,alloc_inode()超级块方法由ll_alloc_inode()函数实现。它创建了一个Lustre特有的索引节点对象ll_inode_info,并且返回嵌入其中的VFS 索引节点。请注意这里一般性的VFS 索引节点和Lustre 索引节点交互方法的特别之处:这种方法创建并且用包含Lustre文件系统所需的额外状态信息来填充VFS 索引节点结构体,但是仅返回嵌入在lli之中的VFS 索引节点结构体&lli->lli_vfs_inode。

static structinode **ll_alloc_inode(struct super_block *sb)

{

structll_inode_info *lli;

...

return&lli->lli_vfs_inode;

}

为了从子结构体中找回父结构体,Lustre定义了一个帮助方法ll_i2info(),它实质上以如下方式调用了内核宏container_of:

/* parameters ofmacro: ptr, type, memeber */

returncontainer_of(inode, struct ll_inode_info, lli_vfs_inode);

3.1.3   Lustre 索引节点

初始化正确的索引节点和文件/文件夹操作是在ll_read_inode2()期间索引节点被填充的时候进行的。为此,定义了四个结构体变量。其中两个是为索引节点操作定义的:ll_file_inode_operations和ll_dir_inode_operations。另外两个是为文件/文件夹操作定义的ll_file_operations和ll_dir_operations。对每组来说,文件和文件夹都有各自的集合,而到底指定哪个是由索引节点或者文件本身决定的。如下所示,给出了一个文件操作的定义实例:

/* in dir.c */

structfile_operations ll_dir_operations = {

       .open = ll_file_open,

       .release = ll_file_release,

       .read = generic_read_dir,

       .readdir = ll_readdir,

       .ioctl = ll_dir_ioctl, ...

};

/* in file.c */

structfile_operations ll_file_operations = {

       .read = ll_file_read,

       .write = ll_file_write,

       .open = ll_file_open, ...

例如,如果将被创建的是一个文件,那么i_op将被指定为ll_file_operations;而如果将被创建的是一个文件夹,那么i_op将被指定为ll_dir_operations:

if(S_ISREG(inode->i_mode)) {

       inode->i_op =&ll_file_inode_operations;

       inode->i_fop = sbi->ll_fop;

       ...

} else if(S_ISDIR(inode->i_mode)) {

       inode->i_op =&ll_dir_inode_operations;

       inode->i_fop = &ll_dir_operations;

       ...

一个一般性的观察是:指向方法表的指针,是由创建它的新实例的当事人或者函数适当地初始化并建立起来的。

3.2    路径查找

路径查找是相对来说较为复杂的任务,而又是最重要和常用的任务之一。Linux VFS承担了大部分的重担;而我们想强调之处是Lustre做特殊操作的联结点。在这一节,我们追寻源码主线,以足够的细节总结了基本步骤,但是也跳过了许多在实际源码中所需要注意的分支,例如:

  • 包含.和..的路径名,
  • 包含软链接的路径名,软链接可能导致循环引用,
  • 访问权限和许可检查,
  • 包含另一个文件系统的挂载点的路径名,
  • 包含不再存在的文件的路径名,
  • LOOKUP_PARENT标识设置,
  • 不包含末尾斜杠(trailing slash)的路径名。

查找可能由sys_open()调用引发,由此引发的惯常的调用路径是执行filp_open()和open_namei()。就是这个函数最后初始化了path_lookup()的调用(?)。特别的,如果一个文件以O_CREAT标志作为访问模式的参数打开,那这个查找操作将设置LOOKUP_PARENT、LOOKUP_OPEN和LOOKUP_CREATE。最后的路径查找结果是如下之一:

  • 如果已存在,返回最后一个路径部件的dentry对象,
  • 如果不存在,按照创建新文件时的情形,返回倒数第二个路径部件的dentry对象。从这里,你可以通过调用父索引节点的创建方法,分配一个新的磁盘索引节点。

现在,我们来聚焦于路径查找的特别之处。如果路径以/开头,那么这是一个绝对路径:搜索以在current->fs->root中的进程的根文件目录作为开始。否则,搜索以current->fs->pwd作为开始。此时,我们也知道开始文件夹的dentry对象及其索引节点(查看Figure3,以弄清为什么)。nameidata使用字段dentry和mnt来追踪解析好的上一个路径部件。初始时,它们被指定为开始文件夹。核心的查找操作由link_path_walk(name, nd)进行,其中name是路径名,nd是nameidata结构体的地址。

1. 对于待解析的下一个部件,从它的名字中计算出32比特的哈希值,以供在dentry cache哈希表中查询使用。

2. 在nd->flags中设置LOOKUP_CONTINUE,标识仍有更多的部件尚待解析。

3. 调用do_lookup(),在dentry对象中搜索路径部件。如果找到了(跳过重新证实),则这个路径部件已被解析,继续。如果没有找到,则调用real_lookup()。在循环的最后,本地dentry变量next的dentry和mnt 字段将分别指向dentry对象和已挂载的文件系统对象,而这里的dentry对象和文件系统对象正是我们所尝试解析的路径部件的。

4. 如果上述,do_lookup()解析到了路径名的最后一个部件,而就像我们开始所假设的,假如这不是一个符号链接,那么我们就达到目的地了。我们所需要做的就是将dentry对象和mnt信息存储在传递过来的nameidata参数nd中,并无错返回。

nd->dentry =next->dentry;

nd->mnt =next->mnt;

Lustre特有的操作由real_lookup()函数处理。一个dentry由VFS创建并且传递到文件系统特有的查找函数中。Lustre查找函数的责任是定位或者创建对应的索引节点,并以正确信息对之赋值。如果不能找到索引节点,那么dentry仍然存在,只是它的索引节点指针指向NULL。这种dentry被称为无效的(negative),意即没有这个名字的文件。这种转换(switching)的代码段如下所示:

struct dentry*result;

struct inode *dir= parent->d_inode;

...

result =d_lookup(parent, name);

if (!result) {

       struct dentry *dentry = d_alloc(parent,name, nd);

       if (dentry) {

       result =dir->i_op->lookup(dir,dentry,nd);

       ...

}

现在,查找开始在Lustre端进行,为得到更多的信息,接下来的操作可能需要涉及到与MDS进行的通信。

同时还存在一个cached_lookup路径,它调用Lustre提供的->revalidate方法。这种方法证实缓存的dentry/索引节点仍然有效,无需再从服务端更新。

3.3    I/O路径

这一节将探讨Lustre所遵循的I/O路径:异步I/O、组I/O、和直接I/O。接着我们将探讨Lustre怎样在将I/O的控制权已交给VFS(大多数情况下)的情况下和VFS进行交互,其中VFS做了更多的准备工作,然后以一页一页的方式(page-by-pagebasis)通过地址空间方法(addressspace methods)读写数据,而其中的地址空间方法则由Lustre提供。所以,试着想像如下过程:VFS挂钩(hooks)到llite,然后llite调用VFS帮助处理,随后VFS将控制权交还llite——一个进进出出、相互纠缠的过程。

3.3.1   异步I/O

这也许是Lustre中最为曲折(traveled)的I/O路径,我们将自顶向下地描述一个写过程。读操作与之非常类似。在VFS中注册写操作,已经在2.1节中谈及。

1. writev()是为旧内核提供的,较新的内核使用aio_write()。我们以ll_file_write()作为分析的切入点。这个函数定义了一个iovec,其中base指向用户空间中提供的缓存,而length是要写入的字符数。

struct ioveclocal_iov = { .iov_base = (void __user *) buf,

.iov_len = count };

这里初始化的另一个结构体是kiocb,它记录了I/O的状态。传入vec和kiocb参数后,它根据内核版本的不同调用ll_file_writev()或者ll_file_aio_write()。在我这里,我们追寻后者。从源码角度看(codewise?),两个函数的实现是相同的,只是原型定义稍微有所不同:

#ifdefHAVE_FILE_WRITEV

static sszie_tll_file_writev(

       struct file *file,

       const struct iovect *iov,

       unsigned long nr_segs,

       loff_t *ppos) {

#else /*AIO stuff*/

static ssize_t ll_file_aio_write(

       struct kiocb *iocb,

       const struct iovec *iov,

       unsigned long nr_segs,

       loff_t pos)

{

       struct file *file = iocb->ki_filp;

       loff_t *ppos = &iocb->ki_pos;

#endif

2. 理解ll_file_aio_write()函数的关键是Lustre根据分条大小,将写分割为多个块,然后在一个循环中请求对每个块上锁。这种方法的目的在于避免需要对大extent上锁时的复杂性。虽然我们之前提到过LOV是处理分条信息的层,但是LustreLite对点认识得也非常清楚,正如这种情况所表现出来的一样——你可以看到它们合作得多么紧密。分条信息通过如下方法得到:

structlov_stripe_md *lsm = ll_l2inof(inode)->lli_smd;

Lustre通过创建一个原始iov控制结构体的复本iov_copy,控制了每次写入的大小,然后回过头来,请求内核中常用的惯例来驱动写入:

retval =generic_file_aio_write(iocb, iov_copy, nrsegs_copy, *pppos);

我们在*iov中记录需要写入的字节数,并用count来跟踪尚待写入的字节数。这个过程一直重复,直至出现错误,或者所有字节都已写入。

在我们进行一般的写入惯例之前,在通过ll_file_get_tree_lock_lov()来获得锁的基础上,还有一些这个函数需要处理的边角:

l  如果用户应用以O_LOV_DELAY_CREATE标志打开文件,但是未调用ioctl来创建文件,就开始写入,那么我们就必须使这个调用失败。

l  如果用户应用以O_APPEND标志打开文件,那么我们需要获取对整个上下文上的锁:lock_end需要设置OBD_OBJECT_EOF标志,或者设置为-1来标识文件的结尾。

3. generic_file_aio_write()只是__generic_file_aio_write_nolock()函数的一个封装;两个函数都在ULK3中有所描述,而我们将在此对之进行详述。由于本节不覆盖直接I/O的内容,写流程进入到generic_file_buffered_write()函数。在此期间,写入被进一步分割成页,并为之分配了页高速缓存。执行上述工作的主要内容,由如下所示的循环完成:

do {

       ...

       status = a_ops->prepare_write(file,page, offset, offset+bytes);

       ...

       copied = filemap_copy_from_user(page,offset, buf, bytes);

       ...

       status = a_ops->commit_write(file,page, offset, offset+bytes);

       ...

} while (count);

首先,我们通过使用Lustre注册的方法来准备页写入。这种准备工作包括检查初始位置是否是在开始处、检查是否需要先从磁盘中读入(当然,在Lustre中没有本地磁盘,但是VFS却是这样认为的)。然后它从用户空间中复制有价值的信息到内核中。最后,我们再次掉用Lustre特有的方法来将页写入。这样,控制就两次流入和流出Lustre代码。

这里存在一些关于页和界限管理的有趣的要点。假设需要写入的的逻辑文件位置是8193,而页大小是4KB。由于这是一个基于页的写入(写入准备和写入执行都是基于页的),它首先计算页索引(2)和页偏移量(1),而bytes是在该页中所能写入的最大字节数。然而,如果剩下的count数比bytes小,我们需要将bytes调整为所需要写入的确切的字节数。

index = pos>> PAGE_CACHE_SHIFT;

offset = (pos& (PAGE_CACHE_SIZE -1));

bytes =PAGE_CACHE_SIZE - offset;

bytes = min(bytes,count);

计算所得的页索引将被用来在页高速缓存中定位或者分配页,然后按如下方式与文件映射相关联:

structaddress_space *mapping = file->f_mapping;

page =__grab_cache_page(mapping, index, &cached_page, &lru_pvec);

稍微离题:__grab_cache_page()是一个在一般的文件写请求时使用的帮助函数。这个函数的基本流程是首先检查这个页是否存在于页高速缓存中,如果存在则返回。否则它将通过调用page_cache_alloc()来分配一个新页,并将之加入到页高速缓存中(add_to_page_cache())。然而,在上一次检查到现在的时间段内,另外一个线程可能分配了一个页,所以加入页高速缓存可能会失败。在这种情况下,我们需要再次检查并从页高速缓存中找到该页:

repeat:

       page = find_lock_page(mapping, index);

       if (!page) {

              ... page_cache_alloc(mapping);

              err = add_to_page_cache(*cachedpage, mapping, index, GFP_KERNEL);

       if (err == -EEXIST)

              goto repeat;

这里以沾污代码的代价进行了一个优化:如果像如上所说的,不能将已分配的页加入到页高速缓存中,则将它保留在cached_page中,而不是将它返回,这样下次再请求一个新页时,我们不需要再次调用分配函数。

4. 现在我们进入到准备写阶段。它按如下描述:

intll_prepare_write(struct file *file, struct page *page,

unsigned from, unsigned to)

以设置为offset的from,设置为offset + bytes的to作为参数,ll_prepare_write()被调用。这就是我们在上面提到的分界问题。总的来说。这个方法是用来保证页已经更新了,如果这不是整页的写,那么首先将读入特定的页。

这里使用了一些新的结构体,它们的含义如下:

结构体obdo是用来以线上(on thewire)的形式表示Lustre对象及其相关信息进行。

结构体brw_page用来描述待发送的页的状态。

结构体obd_info用来在Lustre各层间传输参数。

在这个方法中,我们还需要为部分页写(partial page write)之类的情况检查文件末尾(EOF):如果EOF处于页内(写超出了EOF),我们需要将未写入的部分填充为0;否则,我们需要预读它。

5. 接着,LOV通过lov_prep_async_page()初试化准备页。

各层定义了页写入需要经历的三个结构体。在Lustre Lite层,是ll_async_page(LLAP)。LOV则定义了lov_async_page(LAP)和osc_async_page(OAP)。

6. 如果写操作是异步的,它将由osc_queue_async_io()处理。为了确定我们不会超过所允许的脏页数,阻塞在试图增加额外的脏页的企图上,osc_queue_async_io()内部调用osc_enter_cache()函数进行清算。以类似的方式,它也强制实行grant。

这样,在(至少)有两种方式下,OAP将不能进入高速缓存。

  • 如果高速缓存的大小小于32MB,那么就O.K.了,将OAP放入高速缓存,返回0,否则返回错误码。
  • 如果客户端的grant不够,那么同样返回错误码。在每个OSC初试化时,我们可以假设OST服务器端为客户端预留(grant)一些空间。grant是服务器向客户端保证的能写入的数据量,客户端确信服务器能够处理这么多数据。grant的初始值是2MB,这是相当于两个RPC大小的数据。随后写请求将由客户端发起,如果需要,它能请求更多的grant。同时,在每个数据传输或者写入时,客户端需要跟踪这个grant,确保不会超额。

如果页不能进入缓冲,将会稍后重试组I/O或者异步I/O。

7. 在OAP高速缓存检查完毕之后,将调用loi_list_maint()来将OAP放在合适的链表里,并使之准备好读或写操作。

8. 函数osc_check_rpcs()对每个lov_oinfo中的对象创建RPC。注意,每个RPC只能携带一个数据对象的内容。

3.3.2   组I/O(或者同步I/O)

组I/O是当OAP不能被成功放入高速缓存中时激发的,这种不成功最有可能的情况是grant不够。在这种情形下,Lustre Lite将创建一个obd_to_group结构体来存储OAP。该页将会设置好URGENT标识,加入客户端的obd就绪列表。最后,将调用oig_wait()来等待组I/O的完成。

注意,组I/O会等待操作的完成,所以它又被称为同步I/O。其次,所有的组I/O都是urgent I/O,又同时是读操作。相比之下,在异步I/O中,OAP高速缓存则(当提交时)进入写列表

同样值得指出的是,除了直接I/O和无锁I/O,所有的读都以组I/O的形式执行。直接I/O将在下面简要介绍。无锁I/O是一种类型特别的I/O(现在不可用了,除非在liblustre中),在无锁I/O中,客户端不申请任何的锁,而是通知服务器根据客户端的行为处理锁。

3.3.3   直接I/O

对于直接I/O,VFS传递一个参数iovec用来描述所传输的数据段。这个参数由一个开始地址void * iobase和大小iov_len简单的进行定义。直接I/O要求开始地址必须是page-aligned。

Lustre Lite调用ll_direct_IO_26()和obd_brw_async()函数处理每个页。实质上,这将转化为调用osc_brw_async(),稍后它将被用于创建RPC请求。

3.3.4   与VFS的接口

不论是一般的,在VFS的上层,还是特殊的,在LustreLite里,有几个地方可以注册你自己的读写方法,所有这些都是通过地址空间操作结构体提供的。对于读操作,是readpage()及其向量形式的readpages()。对于写,事情稍微有点复杂。第一个入口点是writepage(),可能由如下事件触发:

l 当内存压力超出阈值,VM将触发脏页的刷新。

l 当用户应用调用fsync()强制执行脏页的刷新。

l 当内核线程周期性地刷新脏页。

l 当Lustre锁管理器撤销页的锁(块请求),脏页应当刷新。

readpage()和writepage()是否实现是可选的,并不是每个文件系统都支持它们。但是利用内核中默认的读写方式(即do_generic_file_read()和do_generic_file_write()的一般化实现)需要者两个方法。这简化了和内核高速缓存和VM的配合。同时,提供mmap的支持同样需要这两个方法。

同样,文件系统也以地址空间对象的形式注册prepare_write()和commit_write()函数。因此,地址空间对象可以描绘为连接文件空间和存储空间的桥梁。

Lustre的地址空间操作定义在lustre/llite/rw26.c中:

structaddress_space_operations ll_aops = {

      .readpage = ll_readpage,

      .direct_IO = ll_direct_IO_26,

      .writepage = ll_writepage_26,

      .set_page_dirty =__set_page_dirty_nobuffers,

      .sync_page = NULL,

      .prepare_write = ll_prepare_write,

      .commit_write = ll_commit_write,

      .invalidatepage = ll_invalidatepage,

      .releasepage = ll_releasepage,

      .bmap = NULL

};

在file对象中有一个*f_mapping指向文件对应的地址空间对象。这种联系是在文件对象创建的时候建立的。


3.4     预读

Lustre客户端的预读发生在页读取的情况下,由结构体ll_readahead_state控制。该结构体定义在lustre/llite/llite_internal.h中。这个结构体每个文件一个,包含了以下信息:

l 读历史。1. 发生过多少次连续读。2. 如果是跳(stride)读,那么发生过多少次连续跳读。3. 跳读间隔和跳读长度。

l 预读窗口(即预读窗口起点和终点)。读操作发生愈连续,预读窗口生长地愈长。

l 帮助监测预读模式的状态信息。

每个客户端最大可能预读40MB,预读算法如下所示:

1. 在页读取中(ll_readpage)文件的预读状态要根据当前状况进行跟新:

a) 如果页偏移量处于一个连续的窗口中(在上一次页的+8、-8窗口中),那么ras_consecutive_requests(由ll_readahead_state定义)将递增1。如果这是读的第一页,那么预读窗口将增加1MB的长度。所以如果读是连续的,那么预读窗口将与读操作的增加一起增长。

b) 如果页不处于连续的窗口中,那么将判断是否处于跳读模式。如果是,将现在的长度/步幅间隔同过去历史中的相对比。如果它们相等,那么ras_consecutive_stride_requests将加1。如果这是读的第一页,那么预读窗口也将增加1MB的长度。

c) 如果页既不处于连续的窗口中,又不处于连续跳跃窗口中,那么所有的预读状态将被重置。例如ras_consecutive_pages和ras_consecutive_requests将重置为0。

       2. 接着,页的读取将根据上一步跟新的状态进行实际的预读。

a) 增加预读的窗口,尝试覆盖此次读中所有的页。

b) 根据实际文件长度调整预读窗口,计算此次预读中将读入多少页。

c) 实际执行预读。

在proc中有一个可以查看预读的状态的预读状态文件(/proc/fs/lustre/llite/XXXXX/read_ahead_stats)。

本文章欢迎转载,请保留原始博客链接http://blog.youkuaiyun.com/fsdev/article
Lustre文件系统曲折而坚韧的发展史1 自2005年6月以来,它持续在超级计算机世界排名前十中占有至少一半的席位,前排名前100的超级计算机中,至少60个超级计算机使用Lustre,其中包括在2014 立即下载

相关推荐

80MHz~100MHz频谱分析仪设计

由理论分析可知,经过乘法器后输出的信号为频率为w1+w2的高频信号和w1-w2的低频信号,除了想要采样的直流信号外,最小的信号频率为100kHz。因此,经过一系列推导可以得到该滤波器的元件C1,C2,C3,R1,R2的近似值分别为:C1=2.36nF,C2=32.1nF,C3=1.07nF,R1=1.39k,R2=2.84k。AK,此时的频率w2即为被测信号的频率,待测信号源电压幅度为,因此为了能达到直流采样的设计,我们将本振源的频率输出范围做成80~110 MHz,提高了题目的频率要求。

qq_48771816的博客 5242

深入理解Lustre文件系统-第5篇 LDLM:锁管理者

Lustre锁管理者的基本思想来自于VAXDLM。在我们深入源码理解它如何工作之前,我们需要解释一些基本的概念。 5.1名字空间 我们首先涉及的概念是名字空间。当你请求锁时,你都在请求某个名字空间中的锁,每个Lustre服务都定义了一个名字空间。为了在一个实际的情景中讨论这个问题,假设你的Lustre文件系统有十个OST,从LDLM的观点看,有十个名字空间。另外,MDS和MGS各自有它们的...

hududumo 722

动态数据脱敏技术分析

在当前国内信息安全热潮中,数据脱敏作为数据安全的重要一环得到了业界的认可与重视。早在2012年,数据脱敏首次作为一个单独的魔力象限由Gartner发布,Gartner在2014年又提出了:按照数据使用场景,将数据脱敏分为静态数据脱敏(Static data masking-SDM )与动态数据脱敏(Dynamic data masking-DDM )。 可能有人望文生义,认为动态数据脱敏一定比静态数据脱敏高级。非也非也,静态or动态,取决于脱敏的使用场景,主要是以使用场景为由来选择合适的数据脱敏的模式。 本

美创科技的博客 4673

【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree日志结构合并树...

LSM 树广泛用于数据存储,例如 RocksDB、Apache AsterixDB、Bigtable、HBase、LevelDB、Apache Accumulo、SQLite4、Tarantool、WiredTiger、Apache Cassandra、InfluxDB和ScyllaDB等。在这篇文章中,我们将深入探讨 Log Structured Merge Tree ,又名 LSM 树:许多高...

AI天才研究院 1万+

音频功放电路大全

作为硬件工程师,特别是做纯粹模拟电路、应用于音频功放的工程师,对于A类,B类,AB类,D类,G类,H类,T类功放应该特别熟悉。大多数工程师或许只知道其中的一部分、或者知道大概,为了让更多的工程师掌握更加详尽的音频功放知识,下文对以上说的音频功放做详细的说明。 功放,顾名思义,就是功率放大的缩写。与电压或者电流放大来说,功放要求获得一定的、不失真的功率,一般在大信号状态下工作,因此,功放电路一般包含电压放大或者电流放大电路没有的特殊问题,具体表现在:①输出功率尽可能大;②通常在大信号状态下工作;③非线性失真突

ltqdxl的博客 6595

基于Multisim的模电调幅发射机仿真设计

振荡器产生的高频信号的功率很小,所以有必要对高频信号进行功率放大,有利于信号的调制发射。缓冲电路将主振荡器和高频功放电路相隔离,以减小功放电路对主振荡器的影响。缓冲电路可以用射极跟随器电路来实现,其作用就是前后级的阻抗相互匹配,减小前后级的影响。总电路的仿真结果见下图:示波器1~4分别是主振荡器、缓冲电路、高频放大电路和调幅电路的输出波形。由于载波频率稳定度≤10-3,所以可以采用并联型改进电容反馈式三端振荡器(Seiler电路)谐振回路,滤除其他频率的成分,得到与载波(假设。(1)缓冲电路的设计与实现。

Eve223322的博客 4583

深入理解Lustre文件系统-第4篇 LOV和OSC

从高层的图景看,LOV的任务是将页指向正确的OSC,而OSC的任务是收集脏页向量、组合(group)它们、将它们从传输线上发送到OST(当然,通过PortalRPC和LNET)。 4.1 OBD设备操作 OBD面向设备操作是一个在LOV、OSC和MDC中使用并能被看到的一般性的源码组织模式和实现方法。OBD设备由obd_ops结构体定义的方法表描述,该方法表有点类似于前面提到的VFS文件、索引...

管子(zero)的杂乱空间 400

SXU-软件项目管理-朱少民-第九章 项目监控与控制 第十章 项目收尾 附相关习题

软件项目管理-朱少民-第九章 项目监控与控制 附相关习题

weixin_63597914的博客 1488

Python学习笔记之:本地文件的读取

读取文件 #桌面上的第一个txt f = open(r"C:\Users\Administrator\Desktop\test.txt") print(f.read()) 以test.txt文件为例,该文件存储路径为桌面 打印读取文件内容f.read() print(f.readline()) 读取文件的第一行内容 print(list(f)) 将内容读取到列表中并输出 小编发现一个问题,如果将read()、readline()、list(f)同时输出,会发现结果不是按程序逐条输出的,程序会默认排除

程序小白博客 1584

从地方政府网站抓取政务公开数据:Python爬虫技术详解

在本文中,我们通过具体实例介绍了如何使用Python编写爬虫,从地方政府门户网站抓取政务公开数据。我们使用了requestsSelenium等最新技术,结合SQLite数据库存储抓取的数据,最后使用matplotlib进行数据可视化分析。通过本项目,您可以掌握如何抓取政府网站的公开数据,并将其进行清洗、存储和分析。这些技术对于进行政府政策分析、公共服务分析等方面具有重要的实际意义。希望通过本文的介绍,您能够更好地理解政务数据抓取的流程和方法,并能在实际工作中运用这些技术进行数据采集和分析。

2201_76125261的博客 1703

QLineEdit控件: 实现IP地址输入框(基于编辑信号)

在开发网络应用程序时,经常需要用户输入IP地址。直接使用普通输入框容易导致格式错误,用户体验也较差。本文将手把手教你用Qt的QLineEdit控件实现一个智能IP地址输入框。即使你是刚接触Qt的新手,也能通过本文理解核心实现原理。

u011186532的专栏 650

Lustre File System (转)

Lustre File System 历史 Lustre是HP,Intel,Cluster File System公司联合美国能源部开发的Linux集群并行文件系统。它来源于卡耐基梅隆大学的NASD项目研究工作。Lustre文件系统2003年推出了1.0版,目前已经推出了1.4.7的发布版本。 Lustre在美国能源部(U.S.Department of Energy:DOE)、Lawren...

cooldatabase 395

6、高通8155座舱平台--摄像头系统

在项目中遇到过,很多从手机转过来的工程师,上来就按手机那套调Camera,结果发现根本跑不通——为什么?我踩过这个坑——一开始想靠软件打时间戳来对齐,结果发现两个sensor的曝光时间不一样,时间戳根本对不上。我记得第一次调AWB时,怎么调颜色都不对,后来发现是BLC的参数没配好——黑电平偏了,后面所有颜色校正都是错的。上层应用说「我要拍张照」,HAL把它翻译成「ISP你给我处理这帧数据,sensor你给我曝光一次」。8155上推荐用camx,它是高通新一代的Camera框架,性能更好,而且支持多路同步。

woailuojp的博客 59

linux buffer cache page cache,buffer cache vs page cache(page cache的演化)

buffer cache vs page cache(page cache的演化)在2.2x时期,page cache和buffer cache是两套cache系统,之间有同步.但是linux不保证每个版本都如此.如果现在/dev/hda1是根,如果hda1上有文件a.txt用dd dump /dev/hda1能够得到和open a.txt一样的结果.到了2.4.x事情已经变得不是这样了,dd i...

weixin_39956451的博客 372

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

深入理解Lustre文件系统-第1篇 跟踪调试系统

一直以来,Linus Torvalds对内核调试器都秉持着抵触态度,并且摆出了我是bastard我怕谁的姿态。他保持了一贯风格,言辞尖锐却直指本质。相信这是经验之谈。在调试内核时,最关键的问题是如何获取出错相关的信息,准确定位出错位置。获取信息有很多方法,其中内核调试器只能提供有限的帮助,而分析日志则是最基本也是最主要的方法。为内核层软件提供一种方便的日志工具,将大大简化其调试工作。在Linu...

hududumo 634

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

深入理解Lustre文件系统-第11篇 Lustre一般化文件系统封装层:fsfilt

Lustre在底层本地文件系统和Lustre之间提供了一个称为fsfilt的封装层。在上层中,obd_filter使用fsfilter层提供的一般化的函数,而fsfilter曾将这些调用传送到一个文件系统特有的实现中。这些特有的实现是特定的底层文件系统接口。fsfilt通过使用针对特定文件系统的小(tiny)封装(即对ext3使用的fsfilt和对Reiserfs3文件系统使用的fsfilt_re...

hududumo 410

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

我的AI存储实践及思考

我从2020年进入人工智能行业,开始为AI训练提供存储解决方案及技术支持,随着这几年行业以及公司的发展,采用的存储方案经历了几次大的演变 ,最开始使用的是分布式并行文件系统Lustre,接着是BeeGFS+Ceph,再到最近的分布式异步对象存储DAOS,本文结合自己的经历,将历次存储架构/方案选型的背景,考量及部署,思考记录如下(保密要求,略过了部分配置及拓扑细节),供参考。

梦幻之巅 2959

Lustre I/O性能特点与最佳实践

Lustre是面向集群的存储架构,它是基于Linux平台的开源集群(并行)文件系统,提供与POSIX兼容的文件系统接口。Lustre两个最大特征是高扩展性和高性能,能够支持数万客户端系统、PB级存储容量、数百GB的聚合I/O吞吐量。Lustre是Scale-Out存储架构,借助强大的横向扩展能力,通过增加服务器即可方便扩展系统总存储容量和性能。Lustre的集群和并行架构,非常适合众多客户端并发进行大文件读写的场合,但目前对于小文件应用非常不适用,尤其是海量小文件应用LOSF(Lots Of Small F

刘爱贵的专栏 3万+

毅硕HPC | Lustre文件系统在HPC集群中的部署实战

Lustre作为一种开源的、分布式并行文件系统,凭借其卓越的可扩展性和极高的I/O吞吐量,成为了全球Top 500超算中心的首选。Lustre不仅仅是一个存储系统,它是连接计算资源与科学数据的纽带。

bio2s 1137

从 HPC 到 AI:探索文件系统的发展及性能评估

随着 AI 技术的迅速发展,模型规模和复杂度以及待处理数据量都在急剧上升,这些趋势使得高性能计算(HPC)变得越来越必要。HPC 通过集成强大的计算资源,比如 GPU 和 CPU 集群,提供了处理和分析大规模数据所需的算力。然而,这也带来了新的挑战,尤其是在存储系统方面,包括如何有效处理大量数据、确保数据访问的高效性以及如何控制成本和运维管理。。它们通过跨多个节点分布存储资源,有效地处理和管理大数据集,满足 HPC 对数据存取速度的高要求。

JuiceFS的博客 2786

【Lustre相关】功能实践-01-Lustre集群部署配置

io500是针对存储性能评测的一个权威国际排行榜,主要包括带宽和元数据性能测试部分,共计十二项严苛的场景测试,该基准使用大文件的读/写带宽和小文件读/写/查询性能来衡量存储性能,这些性能也是影响大数据分析、人工智能、高性能计算的关键因素。 Lustre是一个开源高性能并行文件系统,专注于HPC高性能并行计算场景,几乎占据io500榜单三分之一的位置。 本文主要介绍Lustre集群架构及部署配置说明

Luxf0的博客 6609

深入理解Lustre文件系统-第2篇 Portal RPC

远程进程调用(Remote Procedure Call,RPC)是构建分布式系统时所使用的一种常见组件。它使得客户端可以像进行本地调用一样进行远程的过程调用,即客户端可以忽略消息传递的细节,而专注于过程调用的效果。 Portal RPC是Lustre 的RPC组件,它构建于LNET之上,提供客户端和服务器之间进行消息通信的接口。 2.1消息通信连接的建立 进行Portal RPC通信...

hududumo 584

深入理解Lustre文件系统-第1篇 前言

Lustre文件系统是由U.S.Department of Energy Office of Science and National Nuclear SecurityAdministration laboratorie于近十年之前发起并提供基金支持的的,旨在满足当时和以后的超级计算平台中对于开源、高可扩展的、高性能的并行文件系统的需求。经过十年的发展,它得以在众多中型到大型超级计算平台和集群...

管子(zero)的杂乱空间 353
上一篇: 深入理解Lustre文件系统-第2篇 体系结构的模块视图
下一篇: 深入理解Lustre文件系统-第4篇 LOV和OSC
存储李希
博客等级 码龄15年 418粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值