鄰居子系統(Neighbour Subsystem)維護三層網絡地址到鏈路層地址的映射(ARP、NDP 等)。為防止緩存項無限增長耗盡內存,內核實現了基於定時器的垃圾回收(GC)機制,定期清理失效、長期未用條目。本文基於 Linux 2.6.21 內核源碼(net/core/neighbour.c)剖析 GC 定時器初始化、掃描邏輯、閾值控制、條目釋放全流程。
// neigh_table_init() 中
init_timer(&tbl->gc_timer);
tbl->gc_timer.data = (unsigned long)tbl;
tbl->gc_timer.function = neigh_periodic_timer;
tbl->gc_timer.expires = jiffies + 1; // 1 個 tick 後首次運行
add_timer(&tbl->gc_timer);
每個協議族(IPv4 arp_tbl、IPv6 nd_tbl 等)擁有獨立 neigh_table 及 GC 定時器。
neigh_periodic_timer
static void neigh_periodic_timer(unsigned long arg) {
struct neigh_table *tbl = (struct neigh_table *)arg;
struct neighbour *n, **np;
unsigned long expire, now = jiffies;
NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
write_lock(&tbl->lock);
// 1. 每 300 HZ 重新計算 reachable_time(隨機化防止同步風暴)
if (time_after(now, tbl->last_rand + 300 * HZ)) {
tbl->last_rand = now;
struct neigh_parms *p;
for (p = &tbl->parms; p; p = p->next)
p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
}
// 2. 分桶掃描:每次處理一個 hash 桶
np = &tbl->hash_buckets[tbl->hash_chain_gc];
tbl->hash_chain_gc = (tbl->hash_chain_gc + 1) & tbl->hash_mask; // 下次掃描下一桶
while ((n = *np) != NULL) {
write_lock(&n->lock);
unsigned int state = n->nud_state;
// 跳過永久條目、定時器處理中條目
if (state & (NUD_PERMANENT | NUD_IN_TIMER)) {
write_unlock(&n->lock);
goto next_elt;
}
// 修正 used 時間不早於 confirmed
if (time_before(n->used, n->confirmed))
n->used = n->confirmed;
// 3. 回收條件:引用計數=1 且 (FAILED 狀態 OR 超過 gc_staletime 未用)
if (atomic_read(&n->refcnt) == 1 &&
(state == NUD_FAILED ||
time_after(now, n->used + n->parms->gc_staletime))) {
*np = n->next; // 從 hash 鏈表摘除
n->dead = 1;
write_unlock(&n->lock);
neigh_release(n); // 釋放資源
continue;
}
write_unlock(&n->lock);
next_elt:
np = &n->next;
}
// 4. 重新裝填定時器:base_reachable_time / 2 / hash_bucket_num
expire = tbl->parms.base_reachable_time >> 1;
expire /= (tbl->hash_mask + 1);
if (!expire) expire = 1;
mod_timer(&tbl->gc_timer, now + expire);
write_unlock(&tbl->lock);
}
| 參數 | 含義 | 默認值 (IPv4) |
|---|---|---|
| gc_interval | 常規 GC 間隔(由動態計算覆蓋) | 30 HZ |
| gc_thresh1 | 條目數 < thresh1:不強制 GC | 128 |
| gc_thresh2 | 條目數 > thresh2:5 秒內未刷新則強制 GC | 512 |
| gc_thresh3 | 條目數 > thresh3:拒絕新建,強制 GC | 1024 |
| base_reachable_time | 基礎可達時間(隨機化後為 reachable_time) | 30 HZ |
| gc_staletime | 失效條目最大存活時間 | 60 HZ |