linux核心:CPU私人變數(per-CPU變數)__linux

來源:互聯網
上載者:User


http://blog.chinaunix.net/uid-24148050-id-300576.html

分類: LINUX

一、簡介

2.6核心上一個新的特性就是per-CPU變數。顧名思義,就是每個處理器上有此變數的一個副本。
per-CPU的最大優點就是,對它的訪問幾乎不需要鎖,因為每個CPU都在自己的副本上工作。
tasklet、timer_list等機制都使用了per-CPU技術。

二、API使用

注意,2.6核心是搶佔式的。
所以在訪問per-CPU變數時,應使用特定的API來避免搶佔,即避免它被切換到另一個CPU上被處理。

per-CPU變數可以在編譯時間聲明,也可以在系統運行時動態產生

執行個體一:
編譯期間建立一個per-CPU變數:
    DEFINE_PER_CPU(int,my_percpu); //聲明一個變數
    DEFINE_PER_CPU(int[3],my_percpu_array); //聲明一個數組

使用編譯時間產生的per-CPU變數:
    ptr = get_cpu_var(my_percpu); //     使用ptr
    put_cpu_var(my_percpu); //

當然,也可以使用下列宏來訪問特定CPU上的per-CPU變數
    per_cpu(my_percpu, cpu_id); //
 
per-CPU變數匯出,供模組使用:
    EXPORT_PER_CPU_SYMBOL(per_cpu_var);
    EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);

執行個體二:
動態分配per-CPU變數:
    void *alloc_percpu(type);
    void *__alloc_percpu(size_t size, size_t align);

使用動態產生的per-CPU變數:
    int cpu;
    cpu = get_cpu();
    ptr = per_cpu_ptr(my_percpu);
    //使用ptr
    put_cpu();
三、實現

使用上面的API為什麼就能避免搶佔問題呢,看看代碼實現就知道了

#define get_cpu_var(var) (*({ \
    extern int simple_identifier_##var(void); \
    preempt_disable(); \
    &__get_cpu_var(var); }))
#define put_cpu_var(var) preempt_enable()
#define get_cpu() ({ preempt_disable(); smp_processor_id(); }) #define put_cpu() preempt_enable()
關鍵就在於 preempt_disable 和 preempt_enable 兩個調用,分別是禁止搶佔和開啟搶佔
搶佔相關的東東以後再看

per-cpu 變數的引入有效解決了SMP系統中處理器對鎖得競爭,每個cpu只需訪問自己的本地變數。本文闡述了per-cpu變數在2.6核心上的實現和相關操作。 在系統編譯階段我們就手工的定義了一份所有的per-cpu變數,這些變數的定義是通過宏DEFINE_PER_CPU實現的: 11 #define DEFINE_PER_CPU(type, name) \
  12 __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name

從上面的代碼我們可以看出,手工定義的所有per-cpu變數都是放在.data.percpu段的。注意上面的宏只是在SMP體繫結構下才如此定義。如果不是SMP結構的電腦那麼只是簡單的把所有的per-cpu變數放到全域變數應該放到的地方。

單CPU的per-cpu變數定義: 27 #else /* ! SMP */
  28 
  29 #define DEFINE_PER_CPU(type, name) \
  30 __typeof__(type) per_cpu__##name

在瞭解了上述代碼後,我們還必須弄清楚一點:單CPU的電腦中使用的per-cpu變數就是通過上述宏定義的放在全域資料區的per-cpu變 量。而在SMP體繫結構中,我們使用卻不是放在.data.percpu段的變數,設想一下如果使用這個變數,那麼應該哪個CPU使用呢。事實上,SMP 下,每個cpu使用的都是在.data.percpu段中的這些per-cpu變數的副本,有幾個cpu就建立幾個這樣的副本。


在系統初始化期 間,start_kernel()函數中調用setup_per_cpu_areas()函數,用於為每個cpu的per-cpu變數副本分配空間,注意 這時alloc記憶體 Clerk還沒建立起來,該函數調用alloc_bootmem函數為初始化期間的這些變數副本分配物理空間。 332 static void __init setup_per_cpu_areas(void)
     /* */
 333 {
 334 unsigned long size, i;
 335 char *ptr;
 336 
 337 /* Copy section for each CPU (we discard the original) */
 338 size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);
 339 #ifdef CONFIG_MODULES
 340 if (size < PERCPU_ENOUGH_ROOM)
 341 size = PERCPU_ENOUGH_ROOM;
 342 #endif
 343 
 344 ptr = alloc_bootmem(size * NR_CPUS);
 345 
 346 for (i = 0; i < NR_CPUS; i++, ptr += size) {
 347 __per_cpu_offset[i] = ptr - __per_cpu_start;
 348 memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
 349 }
 350 }
 351 #endif /* !__GENERIC_PER_CPU */

上述函數,在分配好每個cpu的per-cpu變數副本所佔用的物理空間的同時,也對__per_cpu_offset[NR_CPUS]數組進行了初始化用於以後找到指定CPU的這些per-cpu變數副本。

  15 #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))
  16 #define __get_cpu_var(var) per_cpu(var, smp_processor_id()) 這兩個宏一個用於獲得指定cpu的per-cpu變數,另一個用於獲的本地cpu的per-cpu變數,可以自己分析一下。 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.