宋寶華: kvmalloc ——倚天劍屠龍刀兩大神器合體?

你應該曾經糾結過是用kmalloc(),還是vmalloc()?現在你不用那麼糾結了,因爲內核裏面現在有個API叫kvmalloc(),可以認爲是kmalloc()和vmalloc()的雙劍合一。屠龍刀和倚天劍的合體

內核裏面有大量的代碼現在都使用了kvmalloc(),譬如:

source/ipc/msg.c

static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
  struct msg_queue *msq;
  int retval;
  key_t key = params->key;
  int msgflg = params->flg;


  msq = kvmalloc(sizeof(*msq), GFP_KERNEL);
  if (unlikely(!msq))
    return -ENOMEM;


  ...
}


這個代碼在早期的內核裏面是(比如v4.0-rc7/source/ipc/msg.c):

static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
  struct msg_queue *msq;
  int id, retval;
  key_t key = params->key;
  int msgflg = params->flg;


  msq = ipc_rcu_alloc(sizeof(*msq));
  if (!msq)
    return -ENOMEM;


 ...


}

看起來是用的這個函數申請內存:

ipc_rcu_alloc(sizeof(*msq))

那麼這個ipc_rc_alloc()是怎麼回事呢?

void *ipc_alloc(int size)
{
  void *out;
  if (size > PAGE_SIZE)
    out = vmalloc(size);
  else
    out = kmalloc(size, GFP_KERNEL);
  return out;
}

邏輯上是,大於一頁的時候用vmalloc(),小於等於1頁用kmalloc()。

而kvmalloc()的實現代碼裏面則對類似邏輯進行了非常智能地處理:

void *kvmalloc_node(size_t size, gfp_t flags, int node)
{
  gfp_t kmalloc_flags = flags;
  void *ret;


  /*
   * vmalloc uses GFP_KERNEL for some internal allocations (e.g page tables)
   * so the given set of flags has to be compatible.
   */
  if ((flags & GFP_KERNEL) != GFP_KERNEL)
    return kmalloc_node(size, flags, node);


  /*
   * We want to attempt a large physically contiguous block first because
   * it is less likely to fragment multiple larger blocks and therefore
   * contribute to a long term fragmentation less than vmalloc fallback.
   * However make sure that larger requests are not too disruptive - no
   * OOM killer and no allocation failure warnings as we have a fallback.
   */
  if (size > PAGE_SIZE) {
    kmalloc_flags |= __GFP_NOWARN;


    if (!(kmalloc_flags & __GFP_RETRY_MAYFAIL))
      kmalloc_flags |= __GFP_NORETRY;
  }


  ret = kmalloc_node(size, kmalloc_flags, node);


  /*
   * It doesn't really make sense to fallback to vmalloc for sub page
   * requests
   */
  if (ret || size <= PAGE_SIZE)
    return ret;


  return __vmalloc_node_flags_caller(size, node, flags,
      __builtin_return_address(0));
}
EXPORT_SYMBOL(kvmalloc_node);


static inline void *kvmalloc(size_t size, gfp_t flags)
{
  return kvmalloc_node(size, flags, NUMA_NO_NODE);
}

大於一個page的時候,會先用kmalloc()進行__GFP_NORETRY的嘗試,如果嘗試失敗就fallback到vmalloc(NORETRY標記避免了kmalloc在申請內存失敗地情況下,反覆嘗試甚至做OOM來獲得內存)。

當然,kvmalloc()的size如果小於1個page,則沿用老的kmalloc()邏輯,而且也不會設置__GFP_NORETRY,如果反覆嘗試失敗的話,也不會fallback到vmalloc(),因爲vmalloc()申請小於1個page的內存是不合適的。

可觀看我今天的技術分享小視頻:

凡事都沒有絕對的,當咱們還在糾結是kmalloc()還是vmalloc()的時候,人家已經造出了kvmalloc()。咱的糾結,相對於人家的創造,是不是有一種要鑽進去地洞的感覺?思考是最重要的,腦洞要開地大一點,被動地學習永遠只是追着別人的腦子跑。 

(END)

更多精彩,盡在"Linux閱碼場",掃描下方二維碼關注

您的鼓勵是我們前行的動力

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章