深入理解Linux虚拟内存管理(九)(中)

简介: 深入理解Linux虚拟内存管理(九)

深入理解Linux虚拟内存管理(九)(上):https://developer.aliyun.com/article/1597903

(3)写入文件

① shmem_file_write
// mm/shmem.c
// 这块是函数导引。
static ssize_t
shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
  // 获取描述哪些文件被写入的索引节点。
  struct inode  *inode = file->f_dentry->d_inode;
  loff_t    pos;
  unsigned long written;
  int   err;
  // 如果用户尝试写负的字节数,则返回-EINVAL。
  if ((ssize_t) count < 0)
    return -EINVAL;
  // 如果用户空间缓冲区不可访问,则返回-EINVAL。
  if (!access_ok(VERIFY_READ, buf, count))
    return -EFAULT;
  // 获取保护索引节点的semaphore信号量。
  down(&inode->i_sem);
  // 记录写入开始的位置。
  pos = *ppos;
  // 初始化写入字节为0。
  written = 0;
  // precheck_file_write()执行一系列的检查以确保写操作的正确进行。检查包括以
  // append 模式打开文件时更新pos为文件尾部,并保证系统不会超过进程的限定。
  // 文件实现在 mm/filemap.c 中
  err = precheck_file_write(file, inode, &count, &pos);
  // 如果不能执行写操作,则跳转到out。
  if (err || !count)
    goto out;
  // 如果设置了 SUID 位则清除它。
  remove_suid(inode);
  // 更新索引节点的ctime和mtime。
  inode->i_ctime = inode->i_mtime = CURRENT_TIME;
  
// 循环直至所有的请求都进行了写操作。
  do {
    struct page *page = NULL;
    unsigned long bytes, index, offset;
    char *kaddr;
    int left;
  // 设置offset为当前被写页面内的偏移量。
    offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
  // index是当前被写文件中页面的索引。
    index = pos >> PAGE_CACHE_SHIFT;
  // bytes是当前页面中还需要写入的字节数。
    bytes = PAGE_CACHE_SIZE - offset;
  // 如果bytes表明需要写入比请求(count)更多的字节数,则设置bytes为count。
    if (bytes > count)
      bytes = count;

    /*
     * We don't hold page lock across copy from user -
     * what would it guard against? - so no deadlock here.
     */
  // 定位要写入的页面。SGP_WRITE 标志位表明了如果某个页面还不存在则需
  // 要分配它。如果无法找到某页面或分配页面,则跳出循环。
    err = shmem_getpage(inode, index, &page, SGP_WRITE);
    if (err)
      break;
  // 在再次解除页面映射以前,映射被写入且从用户空间缓冲区复制数据的页面。
    kaddr = kmap(page);
  // 从用户空间缓冲区复制数据到内核页面。定义在文件 include/asm-i386/uaccess.h 中
    left = __copy_from_user(kaddr + offset, buf, bytes);
    kunmap(page);
  // 更新写入的字节数。
    written += bytes;
  // 更新还需要写入的字节数。
    count -= bytes;
  // 更新在文件中的位置。
    pos += bytes;
  // 更新用户空间缓冲区中的指针。
    buf += bytes;
  // 如果文件变大,则更新inode->i_size。
    if (pos > inode->i_size)
      inode->i_size = pos;
  // 刷新dcache以避免别名混淆隐患。
    flush_dcache_page(page);
  // 设置该页面为脏且被引用过。
    SetPageDirty(page);
    SetPageReferenced(page);
  // 释放shmem_getpage()对页面使用的引用。
    page_cache_release(page);
  // 如果所有请求的字节都没有从用户空间缓存区读出,则更新写操作统计数据和
  // 文件中的位置及缓冲区中的位置。
    if (left) {
      pos -= left;
      written -= left;
      err = -EFAULT;
      break;
    }
  } while (count);
  // 更新文件指针。
  *ppos = pos;
  // 如果所有的字节都没有写入,则设置错误返回变量。
  if (written)
    err = written;
out:
  // 释放索引节点semaphore信号量。
  up(&inode->i_sem);
  // 返回成功或者返回还需要写入的字节数。
  return err;
}
⑴ ⇒ shmem_getpage

    shmem_getpage 函数

⑵ ⇒ kmap

    kmap 函数

⑶ ⇒ kunmap

    kunmap 函数

(4)符号链接

① shmem_symlink

    这个函数用于创建符号链接 symname 并决定在什么地方存储该信息。如果链接的名字足够小则存储在索引节点中,否则存储在页面帧当中。

// mm/shmem.c
static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
// 这块执行基本的全部检查,并为符号链接创建一个新的索引节点。
// 参数symname是所创建链接的名字。
  int error;
  int len;
  struct inode *inode;
  struct page *page = NULL;
  char *kaddr;
  struct shmem_inode_info *info;
  // 计算链接的长度(len)。
  len = strlen(symname) + 1;
  // 如果名字比一个页面长,则返回-ENAMETOOLONG。
  if (len > PAGE_CACHE_SIZE)
    return -ENAMETOOLONG;
  // 分配一个新的inodeo分配失败则返回-ENOSPC
  inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
  if (!inode)
    return -ENOSPC;
  // 获取私有信息结构。
  info = SHMEM_I(inode);
  // 索引节点的大小即链接的长度。
  inode->i_size = len-1;
// 这块用于存储链接信息。
  // 如果名字的长度小于shmem_inode_info所使用的空间,则复制名字到保留
  // 给私有结构的空间中。
  if (len <= sizeof(struct shmem_inode_info)) {
    /* do it inline */
    memcpy(info, symname, len);
  // 设置inode->i_op为shmem_symlink_inline_operations操作,这样可以知道
  // 链接名字在索引节点中。
    inode->i_op = &shmem_symlink_inline_operations;
  } else {
  // 用 shmem_getpage_locked 分配页面。
    error = shmem_getpage(inode, 0, &page, SGP_WRITE);
  // 如果发生错误,则解除索引节点的引用并返回错误。
    if (error) {
      iput(inode);
      return error;
    }
  // 使用shmem_symlink_inode_operations, 这样可以知道链接信息被包括在页面中。
    inode->i_op = &shmem_symlink_inode_operations;
  // shmem_ilock是一个全局锁,用于保护索引节点的全局链表,这个链表是由私有的
  // 信息结构info->list字段链接起来的。
    spin_lock(&shmem_ilock);
  // 添加新的索引节点到全局链表。
    list_add_tail(&info->list, &shmem_inodes);
  // 释放 shmem_ilock 。
    spin_unlock(&shmem_ilock);
  // 映射页面。
    kaddr = kmap(page);
  // 复制链接信息。
    memcpy(kaddr, symname, len);
  // 解除页面映射。
    kunmap(page);
  // 设置页面为脏。
    SetPageDirty(page);
  // 释放对页面的引用。
    page_cache_release(page);
  }
  // 增加目录的大小,因为添加了新的索引节点。BOGO_DIRENT_SIZE 是索引节点的
  // 伪大小,这样 ls 操作看起来更好一些。
  dir->i_size += BOGO_DIRENT_SIZE;
  // 更新 i_ctime 和 i_mtime。
  dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  // 实例化索引节点。
  d_instantiate(dentry, inode);
  dget(dentry);
  // 返回成功。
  return 0;
}
⑴ ⇒ shmem_get_inode

    shmem_get_inode 函数

⑵ ⇒ shmem_getpage

    shmem_getpage 函数

⑶ ⇒ d_instantiate

    d_instantiate 函数

② shmem_readlink_inline
// mm/shmem.c
static int shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen)
{
  // 链接名字被包括在索引节点中,这样可以将它作为vfs_readlink()的参数传递给 VFS 层。
  // 其实现在文件 fs/namei.c 中
  return vfs_readlink(dentry, buffer, buflen, (const char *)SHMEM_I(dentry->d_inode));
}
③ shmem_follow_link_inline
// mm/shmem.c
static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
{
  // 链接名字被包括在索引节点中,这样可以将它作为vfs_followlink()的参数传递给 VFS 层。
  // 其实现在文件 fs/namei.c 中
  return vfs_follow_link(nd, (const char *)SHMEM_I(dentry->d_inode));
}
④ shmem_follow_link
// mm/shmem.c
static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
{
  struct page *page = NULL;
  // 由于链接名字在页面中,因此可以调用shmem_getpage()获取页面。
  int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ);
  // 如果发生错误,则返回错误。
  if (res)
    return res;
  // 映射页面并作为指针传递给vfs_follow_link()。
  // 其实现在文件 fs/namei.c 中
  res = vfs_follow_link(nd, kmap(page));
  // 解除页面映射。
  kunmap(page);
  mark_page_accessed(page);
  // 解除对页面的引用。  
  page_cache_release(page);
  // 返回成功。
  return res;
}
⑴ ⇒ shmem_getpage

    shmem_getpage 函数

⑤ shmem_readlink
// mm/shmem.c
static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
{
  struct page *page = NULL;
  // 链接名字被包括在和symlink关联的页面中,它可以调用shmem_getpage()获取指针。
  int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ);
  // 如果发生错误,则返回NULL。
  if (res)
    return res;
  // 调用kmap() 映射页面,并将它作为参数传递给vfs_readlink()。该
  // 链接位于页面首部。
  // 其实现在文件 fs/namei.c 中
  res = vfs_readlink(dentry, buffer, buflen, kmap(page));
  // 解除页面映射。
  kunmap(page);
  // 标记页面被访问过。
  mark_page_accessed(page);
  // 解除调用shmem_getpage()对页面的引用。
  page_cache_release(page);
  // 返回该链接。
  return res;
}
⑴ ⇒ shmem_getpage

    shmem_getpage 函数

(5)同步文件

① shmem_sync_file

    这个函数简单返回 0,因为文件只存在于内存而不需要同步文件到磁盘。

// mm/shmem.c
static int shmem_sync_file(struct file *file, struct dentry *dentry, int datasync)
{
  return 0;
}

4、tmpfs 中的索引节点操作

(1)截取

① shmem_truncate

    当调用这个函数的时候,inode->i_sizevmtruncate() 设置为新大小。这个函数用于创建或移除页面,以此设置文件的大小。

// mm/shmem.c
static void shmem_truncate(struct inode *inode)
{
  // 用SHMEM_I()获取索引节点的私有文件系统信息。
  struct shmem_inode_info *info = SHMEM_I(inode);
  // 获取超级块私有信息。
  struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
  unsigned long freed = 0;
  unsigned long index;
  // 更新索引节点的ctime和mtime。
  inode->i_ctime = inode->i_mtime = CURRENT_TIME;
  // 获取文件新的尾部页面的索引。原来的大小存储在info->next_index中。
  index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
  // 如果文件扩大了,只需要返回,因为全局。页面将用于表示扩大的区域。
  if (index >= info->next_index)
    return;
  // 获取私有info自旋锁。
  spin_lock(&info->lock);
  // 继续调用shmem_truncate_indirect()直至文件被截取为需要的大小。
  while (index < info->next_index)
    freed += shmem_truncate_indirect(info, index);
  // 如果shmem_info_info结构表示的换出页面多于文件中的页面,这是个bug。 
  BUG_ON(info->swapped > info->next_index);
  // 释放私有info自旋锁。
  spin_unlock(&info->lock);
  // 获取超级块私有info自旋锁。
  spin_lock(&sbinfo->stat_lock);
  // 更新可用空闲块的数量。
  sbinfo->free_blocks += freed;
  // 更新索引节点所使用的块数量。
  inode->i_blocks -= freed*BLOCKS_PER_PAGE;
  // 释放超级块私有info自旋锁。
  spin_unlock(&sbinfo->stat_lock);
}
② shmem_truncate_indirect

    这个函数定位索引节点中最后两次间接访问的块,并调用 shmem_truncate_direct() 截取它。

// mm/shmem.c
/*
 * shmem_truncate_indirect - truncate an inode
 *
 * @info:  the info structure of the inode
 * @index: the index to truncate
 *
 * This function locates the last doubly indirect block and calls
 * then shmem_truncate_direct to do the real work
 */
static inline unsigned long
shmem_truncate_indirect(struct shmem_inode_info *info, unsigned long index)
{
  swp_entry_t ***base;
  unsigned long baseidx, start;
  // len是当前文件中被第2次使用的最后页面。
  unsigned long len = info->next_index;
  unsigned long freed;
// 如果文件很小且所有的项都存储在直接块信息中,则调用shmem_free_swp() 并传递
// info->i_direct 中第一个交换项和项数目给这个函数,供截取之用。
  if (len <= SHMEM_NR_DIRECT) {
    info->next_index = index;
    if (!info->swapped)
      return 0;
    freed = shmem_free_swp(info->i_direct + index,
          info->i_direct + len);
    info->swapped -= freed;
    return freed;
  }
// 被截取的页面位于间接块的某处。这个代码部分用于计算3个变量:base,
// baseidx和len。base是页面的首部,该页面包括将被截取交换项的指针。baseidx是被使用间
// 接块中第一个项的页索引而len是这里将被截取的项数目。
//
// 计算两次间接块的变量。接着设置base到info->i_indirect首部的交换项。而
// info->i_indirect 首部的页面索引baseidx为SHMEM_NR_DIRECT。在这里 len 为文件中页
// 面的数量,因此减去直接块的数量就是剩下页面的数量。  
  if (len <= ENTRIES_PER_PAGEPAGE/2 + SHMEM_NR_DIRECT) {
    len -= SHMEM_NR_DIRECT;
    base = (swp_entry_t ***) &info->i_indirect;
    baseidx = SHMEM_NR_DIRECT;
  } else {
// 如果不是这样,那么这是一个3级索引块,因此必须在计算base,baseidx和len
// 以前遍历下一级。
    len -= ENTRIES_PER_PAGEPAGE/2 + SHMEM_NR_DIRECT;
    BUG_ON(len > ENTRIES_PER_PAGEPAGE*ENTRIES_PER_PAGE/2);
    baseidx = len - 1;
    baseidx -= baseidx % ENTRIES_PER_PAGEPAGE;
    base = (swp_entry_t ***) info->i_indirect +
      ENTRIES_PER_PAGE/2 + baseidx/ENTRIES_PER_PAGEPAGE;
    len -= baseidx;
    baseidx += ENTRIES_PER_PAGEPAGE/2 + SHMEM_NR_DIRECT;
  }
// 如果在截取后文件变大,则更新 next_index 为文件新尾部,并设置start为间接
// 块的首部。
  if (index > baseidx) {
    info->next_index = index;
    start = index - baseidx;
  } else {
// 如果在截取后文件变小,则移动当前文件尾部至将被截取的间接块的首部。
    info->next_index = baseidx;
    start = 0;
  }
  // 如果在base处有块,则调用shmem_truncate_direct()截取其中的页面。
  return *base? shmem_truncate_direct(info, base, start, len): 0;
}
③ shmem_truncate_direct

    这个函数用于循环遍历间接块,并对包含被截取交换向量的每个页面调用 shmem_free_swp

// mm/shmem.c
/*
 * shmem_truncate_direct - free the swap entries of a whole doubly
 *                         indirect block
 *
 * @info: the info structure of the inode
 * @dir:  pointer to the pointer to the block
 * @start:  offset to start from (in pages)
 * @len:  how many pages are stored in this block
 */
static inline unsigned long
shmem_truncate_direct(struct shmem_inode_info *info, swp_entry_t ***dir, unsigned long start, unsigned long len)
{
  swp_entry_t **last, **ptr;
  unsigned long off, freed_swp, freed = 0;
  // last是被截取间接块中最后一个页面。
  last = *dir + (len + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
  // 如果截取是部分截取而非全页截取,则off是页面中的截取偏移量。
  off = start % ENTRIES_PER_PAGE;
  
// 从dir的startth块开始截取,直至最后一块。
  for (ptr = *dir + start/ENTRIES_PER_PAGE; ptr < last; ptr++, off = 0) {
  // 如果这里没有页面,则继续下一个。
    if (!*ptr)
      continue;
  // 如果info结构表明交换出属于这个索引节点的页面,则调用shmem_free_swp()
  // 释放与该页面相关联的交换槽。如果释放了一个,那么更新infoswapped并增加空闲页面的
  // 计数。
    if (info->swapped) {
      freed_swp = shmem_free_swp(*ptr + off,
            *ptr + ENTRIES_PER_PAGE);
      info->swapped -= freed_swp;
      freed += freed_swp;
    }
  // 如果这不是部分截取,则释放该页面。
    if (!off) {
      freed++;
      free_page((unsigned long) *ptr);
      *ptr = 0;
    }
  }
  // 如果整个间接块现在已释放,则回收该页面。
  if (!start) {
    freed++;
    free_page((unsigned long) *dir);
    *dir = 0;
  }
  // 返回被释放页面的数量。
  return freed;
}
④ shmem_free_swp

    这个函数释放起始项位于 dir 的交换项的 count

// mm/shmem.c
/*
 * shmem_free_swp - free some swap entries in a directory
 *
 * @dir:   pointer to the directory
 * @edir:  pointer after last entry of the directory
 */
static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir)
{
  swp_entry_t *ptr;
  int freed = 0;
  // 循环释放每个交换项。
  for (ptr = dir; ptr < edir; ptr++) {
  // 如果存在交换项,则用free_swap_and_cache()释放它并设置交换项为0。它增
  // 加被释放页面的数量。
    if (ptr->val) {
      free_swap_and_cache(*ptr);
      *ptr = (swp_entry_t){0};
      freed++;
    }
  }
  // 返回被释放页面的总数量。
  return freed;
}

(2)链接

① shmem_link

    这个函数创建一个从 dentryold_dentry 的硬链接。

// mm/shmem.c
/*
 * Link a file..
 */
static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
  // 获取与old_dentry对应的索引节点。
  struct inode *inode = old_dentry->d_inode;
  // 如果它链接到一个目录,则返回-EPERM。严格意义上,根目录应当允许硬
  // 链接目录,虽然并不推荐这么处理,但是在文件系统中创建循环时利用诸如find的操作时可
  // 能会找不到路径。所以tmpfs简单地不允许目录硬链接。
  if (S_ISDIR(inode->i_mode))
    return -EPERM;
  // 为新链接增加目录的大小。
  dir->i_size += BOGO_DIRENT_SIZE;
  // 更新目录的mtime和ctime,并更新索引节点的ctime。
  inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  // 增加指向索引节点的链接数量。
  inode->i_nlink++;
  atomic_inc(&inode->i_count);  /* New dentry reference */
  // 调用dget()获取新dentry的额外引用。
  dget(dentry);   /* Extra pinning count for the created dentry */
  // 实例化新目录项。
  d_instantiate(dentry, inode);
  // 返回成功。
  return 0;
}

(3)解除链接

① shmem_unlink


// mm/shmem.c
static int shmem_unlink(struct inode *dir, struct dentry *dentry)
{
  // 获取解除链接的dentry的索引节点。
  struct inode *inode = dentry->d_inode;
  // 更新目录索引节点的大小。
  dir->i_size -= BOGO_DIRENT_SIZE;
  // 更新ctime和mtime变量。
  inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  // 减小索引节点的链接数量。
  inode->i_nlink--;
  // 调用dput()减小dentry的引用计数。在引用计数为0时,这个函数也调用iput()清
  // 除索引节点。
  dput(dentry); /* Undo the count from "create" - this does all the work */
  return 0;
}

(4)创建目录

① shmem_mkdir
// mm/shmem.c
static int shmem_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
  int error;
  // 调用shmem_mknod() (见L. 2. 2小节)创建一个特定文件。通过指定S_IFDIR标
  // 志位来创建一个目录。
  if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
    return error;
  // 增加父目录的i_nlink字段。
  dir->i_nlink++;
  return 0;
}

(5)移除目录

① shmem_rmdir
// mm/shmem.c
static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
{
  // 用shmem_empty()检查目录是否为空。如果不是,则返回-ENOTEMPTY。
  if (!shmem_empty(dentry))
    return -ENOTEMPTY;
  // 减小父目录的i_nlink字段。
  dir->i_nlink--;
  // 返回 shmem_unlink()(见L. 4. 3. 1)的结果,结果一般是删除该目录。
  return shmem_unlink(dir, dentry);
}


② shmem_empty

    这个函数检查目录是否为空。

// mm/shmem.c
/*
 * Check that a directory is empty (this works
 * for regular files too, they'll just always be
 * considered empty..).
 *
 * Note that an empty directory can still have
 * children, they just all have to be negative..
 */
static int shmem_empty(struct dentry *dentry)
{
  struct list_head *list;
  // dcache_lock虽然保护多个事物,但主要保护dcache的查找,因为这是该函数必须做的工作。
  spin_lock(&dcache_lock);
  list = dentry->d_subdirs.next;
// 循环遍历子目录链表并查找活动目录项,而子目录链表包含了所有的子目录项。如
// 果找到,则表明目录不为空。  
  while (list != &dentry->d_subdirs) {
  // 获取子目录项。
    struct dentry *de = list_entry(list, struct dentry, d_child);
  // 如果目录项有一个相关联的合法索引节点且目前通过hash查找到,则由
  // shmem_positive() (见 L. 4. 5. 3)返回。
  // 因为如果hash查找到,则意味着目录项是活动的,且目录不为空。
    if (shmem_positive(de)) {
  // 如果目录不为空,则释放自旋锁并返回。
      spin_unlock(&dcache_lock);
      return 0;
    }
  // 转到下一个子目录项。
    list = list->next;
  }
  // 目录为空。则释放自旋锁并返回。
  spin_unlock(&dcache_lock);
  return 1;
}
③ shmem_positive
// mm/shmem.c
static inline int shmem_positive(struct dentry *dentry)
{
  // 如果目录项有一个相关联的合法索引节点且目前通过hash查找到,则返回真。
  return dentry->d_inode && !d_unhashed(dentry);
}

深入理解Linux虚拟内存管理(九)(下):https://developer.aliyun.com/article/1597920

目录
相关文章
|
2月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
179 6
|
11天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
17天前
|
存储 缓存 监控
|
1月前
|
存储 缓存 监控
Linux中内存和性能问题
【10月更文挑战第5天】
38 4
|
1月前
|
算法 Linux
Linux中内存问题
【10月更文挑战第6天】
41 2
|
14天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
24 0
|
1月前
|
存储 缓存 固态存储
|
1月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
46 0
|
3月前
|
Linux 调度
深入理解Linux虚拟内存管理(七)(下)
深入理解Linux虚拟内存管理(七)
68 4
|
3月前
|
Linux 索引
深入理解Linux虚拟内存管理(九)(上)
深入理解Linux虚拟内存管理(九)
48 2