当前位置:首页>Linux>Linux 内存管理:LRU 链表 (1)

Linux 内存管理:LRU 链表 (1)

  • 2026-04-21 10:58:09
Linux 内存管理:LRU 链表 (1)

1. 前言

限于作者能力水平,本文可能存在疏漏之处,敬请大家批评指正。

2. LRU 链表的作用

什么是 的 LRU(Least Recently Used)?学习过操作系统理论的读者,对此应该不会感到陌生,在 Linux 中,LRU 指最近最少使用的内存,通过选择性的将一些可能参与内存回收的页面加入不同类型的 LRU 链表,然后在内存回收过程中,按不同优先级排列的各类型 LRU 链表中挑选要回收的页面。想要理解 Linux 内存回收过程,就跳不过 Linux 页面的 LRU 链表管理。

3. LRU 链表的实现概要

本文基于 Linux 4.14.111 + ARM 架构 分析 LRU 页面链表的实现概要。

3.1 LRU 链表主要数据结构

LRU 列表是基于每 NUMA 节点的:

// include/linux/mmzone.h/* * We do arithmetic on the LRU lists in various places in the code, * so it is important to keep the active lists LRU_ACTIVE higher in * the array than the corresponding inactive lists, and to keep * the *_FILE lists LRU_FILE higher than the corresponding _ANON lists. * * This has to be kept in sync with the statistics in zone_stat_item * above and the descriptions in vmstat_text in mm/vmstat.c */#define LRU_BASE 0#define LRU_ACTIVE 1#define LRU_FILE 2enum lru_list {  LRU_INACTIVE_ANON = LRU_BASE, /* 未激活的匿名页面链表 */  LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE, /* 活动的匿名页面链表 */  LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE, /* 未激活的文件 page cache 页面链表 */  LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE, /* 活动的文件 page cache 页面链表 */  LRU_UNEVICTABLE, /* 不可回收的页面链表, 包括 文件 page cache 和 匿名 页面 */  NR_LRU_LISTS};struct lruvec {  struct list_headlists[NR_LRU_LISTS];  struct zone_reclaim_statreclaim_stat;  /* Evictions & activations on the inactive file list */  atomic_long_t      inactive_age;  /* Refaults at the time of last reclaim cycle */  unsigned long      refaults;#ifdef CONFIG_MEMCG  struct pglist_data *pgdat;#endif};typedef struct pglist_data {  ...  spinlock_t    lru_lock; /* 每 NUMA 节点所有类型 LRU 链表公用的锁  */  ...  /* Fields commonly accessed by the page reclaim scanner */  struct lruveclruvec/* 每 NUMA 节点各类型的 LRU 链表 */  ...};

将上面每 NUMA 节点 LRU page 链表结构如下图:

另外,从上面数据结构了解到,一个 NUMA 节点各类型 LRU 链表公用了一把锁(pglist_data::lru_lock),这将导致 NUMA 节点的 LRU 链表操作的激烈锁竞争,为缓解这个问题,引入 per-cpu 的 page 向量:先将 page 添加到 per-cpu page 向量,等 per-cpu page 向量填满了,再一次性的将这些 page 移动到 NUMA 节点对应类型的 LRU 链表,如此可以避免大量的 LRU 锁竞争。

per-cpu page 向量用数据结构 struct pagevec 描述:

// include/linux/pagevec.h/* 14 pointers + two long's align the pagevec structure to a power of two */#define PAGEVEC_SIZE  14struct pagevec {unsigned long nr;unsigned long cold;struct page *pages[PAGEVEC_SIZE];};

再看一下定义的 5 个 per-cpu page 向量

// mm/swap.cstatic DEFINE_PER_CPU(struct pagevec, lru_add_pvec);static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs);static DEFINE_PER_CPU(struct pagevec, lru_deactivate_file_pvecs);static DEFINE_PER_CPU(struct pagevec, lru_lazyfree_pvecs);#ifdef CONFIG_SMPstatic DEFINE_PER_CPU(struct pagevec, activate_page_pvecs);#endif

3.2 LRU 链表的初始化

  • 每 NUMA LRU 链表初始化

BOOT 期间,内存管理系统初始化 NUMA LRU page 链表为空:

bootmem_init()  zone_sizes_init()    free_area_init_node()      free_area_init_core()static void __paginginit free_area_init_core(struct pglist_data *pgdat){  .../* 初始化 NUMA 节点的所有 5 种类型(LRU_INACTIVE_ANON,...) 的 LRU 列表为空 */  lruvec_init(node_lruvec(pgdat));  ...}
  • per-cpu page 向量初始化

per-cpu page 向量初始化为空:

// mm/swap.cstatic DEFINE_PER_CPU(struct pagevec, lru_add_pvec);static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs);static DEFINE_PER_CPU(struct pagevec, lru_deactivate_file_pvecs);static DEFINE_PER_CPU(struct pagevec, lru_lazyfree_pvecs);#ifdef CONFIG_SMPstatic DEFINE_PER_CPU(struct pagevec, activate_page_pvecs);#endif

3.3 添加 page 到 LRU 链表

3.3.1 添加 page 到 per-cpu 的 lru_add_pvec 向量

通过 __lru_cache_add() 将 page 添加到 per-cpu 的 lru_add_pvec 向量,如果以下两种情形之一:

  • lru_add_pvec 向量满
  • page 为复合页(compound)

则将 page 迁移到 NUMA 对应类型的 LRU 列表:

static void __lru_cache_add(struct page *page){  struct pagevec *pvec = &get_cpu_var(lru_add_pvec); /* per-cpu 的 LRU 向量 */  get_page(page);  /* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */  if (!pagevec_add(pvec, page) || PageCompound(page))    __pagevec_lru_add(pvec);  put_cpu_var(lru_add_pvec);}/* * Add the passed pages to the LRU, then drop the caller's refcount * on them.  Reinitialises the caller's pagevec. */void __pagevec_lru_add(struct pagevec *pvec){  pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, NULL);}

接下来看看有哪些类型的 page 会添加到 per-cpu 的 lru_add_pvec 向量。

  • lru_cache_add_anon() 添加 {inactive,anon} page 到 lru_add_pvec
void lru_cache_add_anon(struct page *page){if (PageActive(page))    ClearPageActive(page); /* 清除 PG_active 标记 */  __lru_cache_add(page);}

如共享内存 page。

  • lru_cache_add_file() 添加 {inactive, file} page 到 lru_add_pvec
void lru_cache_add_file(struct page *page){if (PageActive(page))    ClearPageActive(page); /* 清除 PG_active 标记 */  __lru_cache_add(page);}

如文件 read ahead page cache。

  • lru_cache_add() 添加 {[in]active, [file|anon]} page 到 lru_add_pvec

lru_cache_add() 添加到 lru_add_pvec 的 page 可能是 {[in]active, [file|anon]} 组合的任一类型,page 添加到 lru_add_pvec 后,可能在某处通过 mark_page_accessed() 添加 page 到 per-cpu 的 activate_page_pvecs,并最终添加到 NUMA 的 active 类型 LRU 链表。

lru_cache_add() 添加到 lru_add_pvec 的 page 最终的类型 ({[in]active, [file|anon]}),将推迟到从 per-cpu page 向量移动到 NUMA 的 LRU 链表时确定,因为在此之前,都可能改变 page 的类型。

3.3.2 添加 page 到 per-cpu 的 lru_rotate_pvecs 向量

  • rotate_reclaimable_page() 添加 {inactive, file} page 到 lru_rotate_pvecs

同步或异步内存回收时,将 page cache 的 dirty page 写往磁盘时,意味着该 page 即将可被回收,调用 rotate_reclaimable_page() 将 {inactive, file} page 添加到 lru_rotate_pvecs,如果添加 page 导致 lru_rotate_pvecs 或 page 是复合页,将 page 从 lru_rotate_pvecs 迁移到 NUMA 对应类型的 LRU 链表。

end_page_writeback()  rotate_reclaimable_page()/* * Writeback is about to end against a page which has been marked for immediate * reclaim.  If it still appears to be reclaimable, move it to the tail of the * inactive list. */void rotate_reclaimable_page(struct page *page){  if (!PageLocked(page) && !PageDirty(page) &&      !PageUnevictable(page) && PageLRU(page)) {struct pagevec *pvec;unsigned long flags;    get_page(page);    local_irq_save(flags);    pvec = this_cpu_ptr(&lru_rotate_pvecs);/* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */if (!pagevec_add(pvec, page) || PageCompound(page))      pagevec_move_tail(pvec);    local_irq_restore(flags);  }}

3.3.3 添加 page 到 per-cpu 的 lru_deactivate_file_pvecs 向量

通过 deactivate_file_page() 调用,将可被回收的、处于 active 的 file page cache,转为 inactive,添加到 per-cpu 的 lru_deactivate_file_pvecs 向量:

/** * deactivate_file_page - forcefully deactivate a file page * @page: page to deactivate * * This function hints the VM that @page is a good reclaim candidate, * for example if its invalidation fails due to the page being dirty * or under writeback. */void deactivate_file_page(struct page *page){  /*   * In a workload with many unevictable page such as mprotect,   * unevictable page deactivation for accelerating reclaim is pointless.   */  if (PageUnevictable(page))return;  if (likely(get_page_unless_zero(page))) {struct pagevec *pvec = &get_cpu_var(lru_deactivate_file_pvecs);/* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */if (!pagevec_add(pvec, page) || PageCompound(page))      pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL);    put_cpu_var(lru_deactivate_file_pvecs);  }}

所以,per-cpu 的 lru_deactivate_file_pvecs 向量中存放的是作为回收候选的、 {active,file} page,如遇 lru_deactivate_file_pvecs 向量存满 或 page 为 复合页,则将 page 转为 {inactive,file} page 并移动到 NUMA 对应类型的 LRU 链表。

3.3.4 添加 page 到 per-cpu 的 lru_lazyfree_pvecs 向量

madvise(MADV_DONTNEED) 或 madvise(MADV_FREE) 建议释放的物理 page,这些 page 可能即将可用,mark_page_lazyfree() 将这些 {invative, anno} page 添加到 per-cpu 的 lru_lazyfree_pvecs 向量,,如果添加 page 导致 lru_lazyfree_pvecs 或 page 是复合页,将 page 从 lru_lazyfree_pvecs 迁移到 NUMA 对应类型的 LRU 链表。

/** * mark_page_lazyfree - make an anon page lazyfree * @page: page to deactivate * * mark_page_lazyfree() moves @page to the inactive file list. * This is done to accelerate the reclaim of @page. */void mark_page_lazyfree(struct page *page){if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&      !PageSwapCache(page) && !PageUnevictable(page)) {struct pagevec *pvec = &get_cpu_var(lru_lazyfree_pvecs);    get_page(page);/* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */if (!pagevec_add(pvec, page) || PageCompound(page))      pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL);    put_cpu_var(lru_lazyfree_pvecs);  }}

3.3.5 添加 page 到 per-cpu 的 activate_page_pvecs 向量

void activate_page(struct page *page){  page = compound_head(page);if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {struct pagevec *pvec = &get_cpu_var(activate_page_pvecs);    get_page(page);/* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */if (!pagevec_add(pvec, page) || PageCompound(page))      pagevec_lru_move_fn(pvec, __activate_page, NULL);    put_cpu_var(activate_page_pvecs);  }}
  • page swap in
static int handle_pte_fault(struct vm_fault *vmf){  ...  if (!pte_present(vmf->orig_pte))return do_swap_page(vmf); /* swap 页面 换入 */  ...}int do_swap_page(struct vm_fault *vmf){  ...  if (page == swapcache) {    do_page_add_anon_rmap(page, vma, vmf->address, exclusive);    ...    activate_page(page);  } else { /* ksm created a completely new copy */    ...  }  ...}
  • mark_page_accessed() 调用场景

如 file read ahead page。

/* * Mark a page as having seen activity. * * inactive,unreferenced  ->  inactive,referenced * inactive,referenced    ->  active,unreferenced * active,unreferenced    ->  active,referenced * * When a newly allocated page is not yet visible, so safe for non-atomic ops, * __SetPageReferenced(page) may be substituted for mark_page_accessed(page). */void mark_page_accessed(struct page *page){  page = compound_head(page);  if (!PageActive(page) && !PageUnevictable(page) &&      PageReferenced(page)) {/*     * If the page is on the LRU, queue it for activation via     * activate_page_pvecs. Otherwise, assume the page is on a     * pagevec, mark it active and it'll be moved to the active     * LRU on the next drain.     */if (PageLRU(page)) /* 语义上相当于 if (page->flags & PG_lru) */      activate_page(page);else      ...    ClearPageReferenced(page);    ...  } else if (!PageReferenced(page)) {    ...  }  ...}

3.4 page 在 LRU 链表间的迁移

有了 3.3 小节的基础,我们就可以分析 page 在 LRU 列表间的迁移了。可以分为新增迁移两大类:

  • 新增 page 当前不在 NUMA LRU,然后从 per-cpu lru_add_pvec 向量添加到 NUMA 对应类型的 LRU 列表,即:page => lru_add_pvec => 某类型 NUMA LRU

  • 迁移 page 当前位于 NUMA LRU,然后从当前类型的 NUMA LRU 列表,以某个 per-cpu page 向量为跳板,迁移到另一种类型的 NUMA LRU,即:类型 A NUMA LRU => per-cpu page 向量 => 类型 A NUMA LRU

3.4.1 新增

从前面 3.3.1 分析了解到,lru_cache_add() 将 page 添加到 per-cpu lru_add_pvec 向量,lru_add_pvec 向量可能包含以下所有 5 种类型的 page:

enum lru_list {  LRU_INACTIVE_ANON = LRU_BASE,  LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,  LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,  LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,  LRU_UNEVICTABLE,  NR_LRU_LISTS};

然后在 lru_add_pvec 向量满 或 添加复合页 时,通过 pagevec_lru_move_fn() 回调 __pagevec_lru_add_fn() 将 lru_add_pvec 向量中的 page 移入 NUMA 对应类型的 LRU 链表:

/* 将 page 从 per-cpu 的 pagevec @pvec 移动到 page 所属 NUMA 节点的 LRU 列表 */static void pagevec_lru_move_fn(struct pagevec *pvec,void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),void *arg){  int i;  struct pglist_data *pgdat = NULL;  struct lruvec *lruvec;  unsigned long flags = 0;  /*   * 逐个 page 移动:    * per-cpu pagevec => page 对应的 NUMA 节点的 LRU 列表.   *   * 为什么要逐个 page 移动? 因为 @pvec 上每个 page 可能   * 分别属于不同的 NUMA 节点.   */  for (i = 0; i < pagevec_count(pvec); i++) {struct page *page = pvec->pages[i];struct pglist_data *pagepgdat = page_pgdat(page); /* page 关联 (NUMA 节点) 的 pg_data_t */if (pagepgdat != pgdat) {if (pgdat)        spin_unlock_irqrestore(&pgdat->lru_lock, flags);      pgdat = pagepgdat;      spin_lock_irqsave(&pgdat->lru_lock, flags);    }    lruvec = mem_cgroup_page_lruvec(page, pgdat); /* NUMA 节点的 LRU 向量 *//* 将 @page 从 per-cpu 的 pagevec 移动到 NUMA 节点的 LRU 列表 *//*     * lru_add_pvec => NUMA LRU: __pagevec_lru_add_fn()     * lru_rotate_pvecs => NUMA LRU: pagevec_move_tail_fn()     * lru_deactivate_file_pvecs => NUMA LRU: lru_deactivate_file_fn()     * lru_lazyfree_pvecs => NUMA LRU: lru_lazyfree_fn()     * activate_page_pvecs => NUMA LRU: __activate_page()     */    (*move_fn)(page, lruvec, arg);  }  if (pgdat)    spin_unlock_irqrestore(&pgdat->lru_lock, flags);  /* 将 @pages 中的 page 引用计数减 1, 对引用计数归 0 的 page, 归还给 buddy */  release_pages(pvec->pages, pvec->nr, pvec->cold);  /* @pvec 的 page 已经悉数移动到 NUMA 节点的 LRU 列表, 因此清零其 page 计数 */  pagevec_reinit(pvec);}static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec,void *arg){  int file = page_is_file_cache(page);  int active = PageActive(page);  /*   * 确定 page 应该放入的 LRU 列表类型:   * . PageUnevictable(page) => LRU_UNEVICTABLE   * . file cache   *   . PageActive(page) => LRU_ACTIVE_FILE   *   . !PageActive(page) => LRU_INACTIVE_FILE   * . anon   *   . PageActive(page) => LRU_ACTIVE_ANON   *   . !PageActive(page) => LRU_INACTIVE_ANON   */  enum lru_list lru = page_lru(page);  VM_BUG_ON_PAGE(PageLRU(page), page); /* (1) page 不应该已经进入 LRU, 这里应该是新添加的 */  SetPageLRU(page); /* 标记 page 位于 LRU 列表: page->flags |= PG_lru */  add_page_to_lru_list(page, lruvec, lru); /* (2) 将 page 添加到 @lru 类型 LRU 列表 (lruvec->lists[lru]) 头部 */  ...}

上面代码注释 (1) 告诉我们,当前 page 不在 NUMA LRU 链表内;注释 (2) 的 add_page_to_lru_list() 新增 page 到对应的 NUMA LRU 链表:

/* 将 page 添加到 @lru 类型 LRU 列表 (lruvec->lists[lru]) 头部 */static __always_inline void add_page_to_lru_list(struct page *page,struct lruvec *lruvec, enum lru_list lru){  update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page));  list_add(&page->lru, &lruvec->lists[lru]);}

因为是新增操作,意味 page 当前不在任何 NUMA LRU 链表内,所以没有从 __pagevec_lru_add_fn() 没有从旧 LRU 链表移除的操作,这是和后面的 page 在 LRU 链表间迁移过程最大的区别。

3.4.2 迁移

page 在不同类型 LRU 链表间迁移分为 4 种场景,我们一一讨论。

  • rotate:可回收 page 从同一 LRU 链表的当前位置移动到尾部

从 3.3.2 中了解到,rotate_reclaimable_page() 将位于某类型 LRU 链表的 page,添加到 per-cpu 的 lru_rotate_pvecs 向量中,这时候 page 同时位于 NUMA LRU 链表和 lru_rotate_pvecs 向量,直到 lru_rotate_pvecs 向量满或添加了复合页,此时触发 page 移动:

void rotate_reclaimable_page(struct page *page){  if (!PageLocked(page) && !PageDirty(page) &&      !PageUnevictable(page) && PageLRU(page)) {struct pagevec *pvec;unsigned long flags;    ...    pvec = this_cpu_ptr(&lru_rotate_pvecs);/* 添加 page 导致 per-cpu LRU 向量满了 或 page 为 复合页, 都要将 page 迁移到 NUMA 的 LRU 列表 */if (!pagevec_add(pvec, page) || PageCompound(page))      pagevec_move_tail(pvec);    ...  }}static void pagevec_move_tail(struct pagevec *pvec){  int pgmoved = 0;  pagevec_lru_move_fn(pvec, pagevec_move_tail_fn, &pgmoved);  ...}static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec,void *arg){  int *pgmoved = arg;  if (PageLRU(page) && !PageUnevictable(page)) {    del_page_from_lru_list(page, lruvec, page_lru(page)); /* page 从当前位置移除 */    ClearPageActive(page); /* 清除 PG_active */    add_page_to_lru_list_tail(page, lruvec, page_lru(page)); /* page 添加到同一 LRU 尾部 */    (*pgmoved)++;  }}

从 pagevec_move_tail_fn() 可见,page 从当前位置移动到同一 LRU 链表尾部,这主要是在那些文件脏页回写时,放在 {inactive, file} LRU 链表尾部,可以更快的被回收(页面回收从 inactive LRU 链表尾部开始)。注意,pagevec_move_tail_fn() 是在 pagevec_lru_move_fn() 的 pagevec 循环中被回调,所以 rotate 的是整个 lru_rotate_pvecs 向量中 page,即将 lru_rotate_pvecs 向量中的 page 倒序放置了。

  • file page: active => inactive

从 3.3.3中了解到,deactivate_file_page(){active, file}的 page,转换为{inactive, file},以lru_deactivate_file_pvecs向量为跳板,通过pagevec_lru_move_fn()回调lru_deactivate_file_fn(),将lru_deactivate_file_pvecs向量中的所有 page,从 NUMA 节点的LRU_ACTIVE_FILE类型 LRU 链表,迁移到LRU_INACTIVE_FILE 类型 LRU 链表:

static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec,void *arg){  int lru, file;  bool active;  if (!PageLRU(page)) /* page 当前不位于 LRU, 则什么都不做 */return;  if (PageUnevictable(page)) /* page 不能被回收 */return;  /* Some processes are using the page */  if (page_mapped(page)) /* page 还在某些进程使用, 不能作为回收候选页 */return;  active = PageActive(page);  file = page_is_file_cache(page);  lru = page_lru_base_type(page);  del_page_from_lru_list(page, lruvec, lru + active); /* 从 旧的 {lru + active} 类型 LRU 链表移除 */  ClearPageActive(page); /* 清除 PG_active */  ClearPageReferenced(page); /* 清除 PG_referenced */  add_page_to_lru_list(page, lruvec, lru); /* 添加到 新的 {lru} 类型 LRU 链表 */  if (PageWriteback(page) || PageDirty(page)) { /* 如果是 回写页 或 脏页 *//*     * PG_reclaim could be raced with end_page_writeback     * It can make readahead confusing.  But race window     * is _really_ small and  it's non-critical problem.     */    SetPageReclaim(page); /* 标记为可回收 */  } else {/*     * The page's writeback ends up during pagevec     * We moves tha page into tail of inactive.     */    list_move_tail(&page->lru, &lruvec->lists[lru]);    ...  }  ...}
  • file page: {anno} => {inactive, file}

从 3.3.4 中了解到,mark_page_lazyfree() 将 madvise() 标记的可释放 page,以 lru_lazyfree_pvecs 为跳板,通过 pagevec_lru_move_fn() 回调 lru_lazyfree_fn(),将 lru_lazyfree_pvecs 中的所有 page,从 anno 类型 LRU 链表,迁移到 {inactive, file} LRU,作为可回收内存 page:

static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec,void *arg){  if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&      !PageSwapCache(page) && !PageUnevictable(page)) {bool active = PageActive(page);    del_page_from_lru_list(page, lruvec,               LRU_INACTIVE_ANON + active); /* 从旧的 匿名页类型 LRU 链表移除 */    ClearPageActive(page); /* 清除 PG_active */    ClearPageReferenced(page); /* 清除 PG_referenced *//*     * lazyfree pages are clean anonymous pages. They have     * SwapBacked flag cleared to distinguish normal anonymous     * pages     */    ClearPageSwapBacked(page); /* 清除 PG_swapbacked */    add_page_to_lru_list(page, lruvec, LRU_INACTIVE_FILE); /* 添加到新 {inactive, file} 类型 LRU 链表 */    ...  }}
  • inactive => active

从 3.3.5中了解到,activate_page(){inactive}page 转为{active}page,也即以activate_page_pvecs向量为跳板,通过pagevec_lru_move_fn()回调__activate_page(),将activate_page_pvecs向量中的所有 page ,从{inactive}类型 LRU 链表迁移到{active} LRU 链表:

static void __activate_page(struct page *page, struct lruvec *lruvec,void *arg){  if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {int file = page_is_file_cache(page);int lru = page_lru_base_type(page);    del_page_from_lru_list(page, lruvec, lru); /* 从 inactive(LRU_INACTIVE_ANON,LRU_INACTIVE_FILE) 类型 LRU 链表移除 */    SetPageActive(page); /* 标记为活动 page, 即设置 PG_active 标志 *//*     * 添加到 active 类型 LRU:      * - LRU_INACTIVE_ANON => LRU_ACTIVE_ANON     * - LRU_INACTIVE_FILE => LRU_ACTIVE_FILE     */    lru += LRU_ACTIVE;    add_page_to_lru_list(page, lruvec, lru); /* 添加到 active(LRU_ACTIVE_ANON,LRU_ACTIVE_FILE) 类型 LRU 链表 */    ...  }}

典型的如 swap in 场景。

3.4.3 手动触发 page 新增 和 迁移 情形

前面两小节讨论的 page 新增迁移,都是在 per-cpu 向量满或操作复合页时自动触发,有时候可能需要手动触发,内核提供了下列主要接口来支持:

  • lru_add_drain_cpu()
void lru_add_drain_cpu(int cpu){  struct pagevec *pvec = &per_cpu(lru_add_pvec, cpu);  if (pagevec_count(pvec))    __pagevec_lru_add(pvec); /* (1) lru_add_pvec => NUMA LRU */  pvec = &per_cpu(lru_rotate_pvecs, cpu);  if (pagevec_count(pvec)) {unsigned long flags;/* No harm done if a racing interrupt already did this */    local_irq_save(flags);    pagevec_move_tail(pvec); /* (2) rotated lru_rotate_pvecs => NUMA LRU */    local_irq_restore(flags);  }  pvec = &per_cpu(lru_deactivate_file_pvecs, cpu);  if (pagevec_count(pvec))    pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL); /* (3) */  pvec = &per_cpu(lru_lazyfree_pvecs, cpu);  if (pagevec_count(pvec))    pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL); /* (4) */  activate_page_drain(cpu); /* (5) */}

函数的逻辑一目了然,上面的注释 (1),(2),(3),(4),(5) 处,将 5 类 per-cpu 向量缓存的 page,移动到 NUMA LRU 链表中。

  • lru_add_drain_all_cpuslocked()
void lru_add_drain_all_cpuslocked(void){  static DEFINE_MUTEX(lock);  static struct cpumask has_work;  int cpu;  /*   * Make sure nobody triggers this path before mm_percpu_wq is fully   * initialized.   */  if (WARN_ON(!mm_percpu_wq))return;  mutex_lock(&lock);  cpumask_clear(&has_work);  for_each_online_cpu(cpu) {struct work_struct *work = &per_cpu(lru_add_drain_work, cpu); /* per-cpu 的 drain work */if (pagevec_count(&per_cpu(lru_add_pvec, cpu)) ||        pagevec_count(&per_cpu(lru_rotate_pvecs, cpu)) ||        pagevec_count(&per_cpu(lru_deactivate_file_pvecs, cpu)) ||        pagevec_count(&per_cpu(lru_lazyfree_pvecs, cpu)) ||        need_activate_page_drain(cpu)) {      INIT_WORK(work, lru_add_drain_per_cpu);      queue_work_on(cpu, mm_percpu_wq, work); /* per-cpu 的 drain work 入队 */      cpumask_set_cpu(cpu, &has_work);    }  }  for_each_cpu(cpu, &has_work)    flush_work(&per_cpu(lru_add_drain_work, cpu)); /* 执行 per-cpu 的 drain work */  mutex_unlock(&lock);}

执行 work 回调 lru_add_drain_per_cpu()

lru_add_drain_per_cpu()  lru_add_drain()    lru_add_drain_cpu(get_cpu());    put_cpu();

lru_add_drain_cpu() 前面已经分析过了,这里不再赘述。

  • activate_page_drain()
static void activate_page_drain(int cpu){struct pagevec *pvec = &per_cpu(activate_page_pvecs, cpu);if (pagevec_count(pvec))    pagevec_lru_move_fn(pvec, __activate_page, NULL);}

__activate_page() 前面已经分析过了,这里不再赘述。

其它还有几个变体接口,最终都会落到这 3 个接口中来,这里就不一一展开了。

4. 小结

最后,用下图来小结本文的内容:

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-21 13:36:24 HTTP/2.0 GET : https://f.mffb.com.cn/a/484330.html
  2. 运行时间 : 0.156281s [ 吞吐率:6.40req/s ] 内存消耗:5,013.09kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=4695a24952348cbb365de76d6660d78c
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000665s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000631s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000287s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000249s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000507s ]
  6. SELECT * FROM `set` [ RunTime:0.000198s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000624s ]
  8. SELECT * FROM `article` WHERE `id` = 484330 LIMIT 1 [ RunTime:0.000492s ]
  9. UPDATE `article` SET `lasttime` = 1776749784 WHERE `id` = 484330 [ RunTime:0.000766s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000229s ]
  11. SELECT * FROM `article` WHERE `id` < 484330 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000413s ]
  12. SELECT * FROM `article` WHERE `id` > 484330 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000375s ]
  13. SELECT * FROM `article` WHERE `id` < 484330 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000934s ]
  14. SELECT * FROM `article` WHERE `id` < 484330 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000672s ]
  15. SELECT * FROM `article` WHERE `id` < 484330 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000960s ]
0.157940s