核心中的C語言
GNU C 的編譯器gcc
新特性
從C++引入了 inline, const
為了支援64位,增加資料類型: long long int
屬性描述符 attribute
asm 和 asm
由于gcc增加了一些保留字如inline,在ANSI C并非是保留字,是以在老的C代碼中可能有變量名是inline,産生了沖突。
gcc允許在保留字前後加上"__"。
"__inline__" 等價 "inline"
"__asm__" 等價 "asm"
同時"attribute"也是保留字
struct fool {
char a;
int x[z] attribute__ ((packed));
}
表示 x數組和成員a之間,不要進行對齊。讓x緊挨着a。
宏的使用
宏的正确寫法
正确寫法: #define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
錯誤1: #define DUMP_WRITE(addr,nr) memcpy(bufp,addr,nr); bufp += nr; 這樣是不對的
if (addr)
DUMP_WRITE(addr, nr);
else
do_something_else();
經過預處理後編譯失敗。如果把DUMP_WRITE放在else裡面,則直接引起bug!!!
錯誤2: #define DUMP_WRITE(addr,nr) { memcpy(bufp,addr,nr); bufp += nr; }
同樣編譯錯誤
空的宏
#define prepare_to_switch() do { } while(0)
核心中的隊列
list_head像一個扣子一樣存在每個想要被串起來的結構中
struct list_head {
struct list_head *next, *prev;
};
struct page {
list_head list;
list_head lru;
}
初始化
#define INIT_LIST_HEAD(ptr) do { \
ptr->next = ptr; \
ptr->prev = ptr; \
} while(0);
将一個新結構體挂到鍊上
static __inline__ void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static __inlie__ void __list_add(struct list_head * new,
struct list_head * prev,
struct list_head * next)
{
new->next = next;
new->prev = prev;
next->prev = new;
prev->ext = new;
}
通過list_head找到宿主結構
page = memlist_entry(curr, struct page, list);
#define list_entry(ptr, type, member) \
((type *)((char*)(ptr) - (unsigned long)(&((type*)0)->member)))
核心中的彙編
彙編
純彙編.s
.S -> 預處理 -> .s
嵌入在C語言的彙編
GNU的i386彙編 AT&T
特點
1) 小寫
2) 寄存器前%
3) 順序
4) 直接數前面要加上$
5) 操作的大小在指令的最後一個字母 b, w, l
6) jump/call 要加上*(函數指針)
7) ljmp lcall
8) 間接尋址
Section:disp(base, index, scale)
尋址數組:base + index*scale
數組的元素是一個結構體,disp是字段在結構上的偏移
-4(%ebp) = -4(%ebp, 0, 0) 0和逗号可以省略
foo(, %eax, 4) = 0 + %eax*4 - foo
嵌入在C的彙編
關鍵字
要嵌入彙編需要使用asm關鍵字
#define __SLOW_DOWN_IO __asm__ __volatitle__("outb %al, $0x80")
#define __SLOW_DOWN_IO __asm__ __volatitle__("jmp 1f \n1:\tjmp 1f\n1:")
jmp 1f //1f表示向前找1
1: jmp 1f
1:
寄存器的配置設定 和 c語言變量的綁定
static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
LOCK "addl %1,%0"
:"=m" (v->counter)
:"ir" (i), "m" (v->counter));
}
格式 -> 指令部: 輸出部: 輸入部: 損壞部
難點:如何把C中的變量 和 彙編中的操作數結合起來。因為,這段彙編的上下文寄存器配置設定情況隻有gcc知道。
做法:隻提供具體的指令部分,而對寄存器的使用則提供一個樣闆和一些限制條件。到底如何結合由gcc, gas處理。
指令部分中的%0, %1就是需要使用的寄存器的樣闆操作數,多少取決于CPU的通用寄存器個數。
由于%給了寄存器樣闆操作數了,是以嵌入到c中的彙編,寄存器前要加兩個%。
c的變量和寄存器的限制條件 constraint
輸出部中的限制
"=m" (v->counter)
1) 多個限制用逗号分開。
2) 限制以=開始
3) "=m" 表示相應的操作數(%0) 是一個記憶體單元,而且執行這段彙編後不保留原值(因為它在輸出部麻)。
輸入部中的限制
:"ir" (i), "m" (v->counter));
1) 不帶"="
2) 有兩個限制
"ir" (i), 表示彙編指令中的%1可以是在寄存器中的直接數("ir": i表示直接數(immediate), r表示寄存器),并且這個操作數來自于c語言的變量i.
"m" (v->counter), 表示指令中的%2是一個記憶體上的單元,值來自于c語言的v-counter
根據輸入和輸出部的對寄存器的限制,gcc會自動配置設定寄存器,并且保證執行前後不會影響現場。
操作數的編号
從輸出部的第一個限制還是 %0 %1 %2
限制描述
"m", "v", "o" --> 記憶體單元
"r" --> 任意的寄存器
"q" --> eax ebx ecx edx
"a", "b", "c", "d" --> 分别表示eax ebx ecx edx
"S", "D" --> ESI EDI
"I" --> 常數[0~32)
"g" --> 任意
"i", "h" --> 直接操作數
字元串拷貝
static inline void * __memcpy(void * to, const void * from, size_t n)
{
int d0, d1, d2;
__asm__ __volatile__(
"rep ; movsl\n\t"
"testb $2,%b4\n\t"
"je 1f\n\t"
"movsw\n"
"1:\ttestb $1,%b4\n\t"
"je 2f\n\t"
"movsb\n"
"2:"
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
: "memory");
return (to);
}