Linux per-CPU實現分析__Linux

來源:互聯網
上載者:User

217 static DEFINE_PER_CPU(struct runqueue, runqueues);
11 #define DEFINE_PER_CPU(type, name)
12 __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name
13



首先,在arch/i386/kernel/vmlinux.lds中有

  /* will be freed after init */  . = ALIGN(4096);/* Init code and data */  __init_begin = .;  /* 此處省略若干行:) */    . = ALIGN(32);  __per_cpu_start = .;  .data.percpu  : { *(.data.percpu) }  __per_cpu_end = .;  . = ALIGN(4096);  __init_end = .;  /* freed after init ends here */

這說明__per_cpu_start和__per_cpu_end標識.data.percpu這個section的開頭和結尾
並且,整個.data.percpu這個section都在__init_begin和__init_end之間,
也就是說,該section所佔記憶體會在系統啟動後釋放(free)掉

因為有
#define DEFINE_PER_CPU(type, name)
__attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name

所以
static DEFINE_PER_CPU(struct runqueue, runqueues);
會擴充成
__attribute__((__section__(".data.percpu"))) __typeof__(struct runqueue)
per_cpu__runqueues;
也就是在.data.percpu這個section中定義了一個變數per_cpu__runqueues,
其類型是struct runqueue。事實上,這裡所謂的變數per_cpu__runqueues,
其實就是一個位移量,標識該變數的地址。

--------------------
其次,系統啟動後,在start_kernel()中會調用如下函數
unsigned long __per_cpu_offset[NR_CPUS];static void __init setup_per_cpu_areas(void){unsigned long size, i;char *ptr;/* Created by linker magic */extern char __per_cpu_start[], __per_cpu_end[];/* Copy section for each CPU (we discard the original) */size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);#ifdef CONFIG_MODULESif (size < PERCPU_ENOUGH_ROOM)size = PERCPU_ENOUGH_ROOM;#endifptr = alloc_bootmem(size * NR_CPUS);for (i = 0; i < NR_CPUS; i++, ptr += size) {__per_cpu_offset[i] = ptr - __per_cpu_start;memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);}}

在該函數中,為每個CPU分配一段專有資料區,並將.data.percpu中的資料拷貝到其中,
每個CPU各有一份。由於資料從__per_cpu_start處轉移到各CPU自己的專有資料區中了,
因此存取其中的變數就不能再用原先的值了,比如存取per_cpu__runqueues
就不能再用per_cpu__runqueues了,需要做一個位移量的調整,
即需要加上各CPU自己的專有資料區首地址相對於__per_cpu_start的位移量。
在這裡也就是__per_cpu_offset[i],其中CPU i的專有資料區相對於
__per_cpu_start的位移量為__per_cpu_offset[i]。
這樣,就可以方便地計算專有資料區中各變數的新地址,比如對於per_cpu_runqueues,
其新地址即變成per_cpu_runqueues+__per_cpu_offset[i]。

經過這樣的處理,.data.percpu這個section在系統初始化後就可以釋放了。

--------------------
再看如何存取per cpu的變數
/* This macro obfuscates arithmetic on a variable address so that gcc   shouldn't recognize the original var, and make assumptions about it */#define RELOC_HIDE(ptr, off)  ({ unsigned long __ptr;    __asm__ ("" : "=g"(__ptr) : "0"(ptr));    (typeof(ptr)) (__ptr + (off)); })/* var is in discarded region: offset to particular copy we want */#define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))#define __get_cpu_var(var) per_cpu(var, smp_processor_id())#define get_cpu_var(var) (*({ preempt_disable(); &__get_cpu_var(var); }))

對於__get_cpu_var(runqueues),將等效地擴充為
__per_cpu_offset[smp_processor_id()] + per_cpu__runqueues
並且是一個lvalue,也就是說可以進行賦值操作。
這正好是上述per_cpu__runqueues變數在對應CPU的專有資料區中的新地址。

由於不同的per cpu變數有不同的位移量,並且不同的CPU其專有資料區首地址不同,
因此,通過__get_cpu_var()便訪問到了不同的變數。

--END


轉至:http://www.unixresources.net/linux/clf/linuxK/archive/00/00/47/91/479165.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.