LinuxKernel(一)

首先,回顧一下基礎的宏操做:linux

C語言宏

###

  1. #的做用是字符串化:在一個宏中的參數前面使用一個#,預處理器會把這個參數轉換爲一個字符數組數組

    #define ERROR_LOG(info) fprintf(stderr,"error:"#info"\n");

    則有:函數

    ERROR_LOG("add"); ---> fprintf(stderr,"error: "add"\n");
    ERROR_LOG(devied =0); ---> fprintf(stderr,"error: devied=0\n");
  2. #是一種分隔鏈接方式,它的做用是先分隔,而後進行強制鏈接。工具

    例如:優化

    #define XNAME(n) x##n

    那麼XNAME(4)就會展開爲x4.3d

do{/*codes*/}while(0)

採用這種方式是爲了防範在使用宏過程當中出現錯誤,主要有以下幾點:指針

  (1)空的宏定義避免warning:code

#define foo() do{}while(0)

  (2)存在一個獨立的block,能夠用來進行變量定義,進行比較複雜的實現。
  (3)若是出如今判斷語句事後的宏,這樣能夠保證做爲一個總體來是實現:blog

#define foo() \
	action1(); \
	action2();

  在遇到分支語句時:字符串

if(NULL == pPtr)
  foo();

foo()中的兩個語句就不會都被執行。

(4)爲什麼不用單獨的{}

#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)
 switch(x,y);
else
 op();

在把宏引入代碼中,會多出一個分號,從而會報錯。

變參宏: ···__VA_ARGS__

某些函數接受可變的參數例如printf(),在頭文件stdvar.h中有工具能夠自定義變參宏。

把宏參數列表中最後的參數用···省略,而__VA_ARGS__可用在替換部分,表面省略號表明的東西。

#define PR(···)  printf(__VA_ARGS__)

例如:

PR("THIS IS __VA_ARGS__");

會被展開爲:

printf("THIS IS __VA_ARGS__");

預約義符號

符號 樣例值 含義
__FILE__ "test.c" 進行編譯的文件名
__LINE__ 25 當前行的行號
__DATE__ "Jan 31 2001" 被編譯的日期
__TIME__ "23:17:24" 被編譯的時間
__STDC__ 1 是否遵循ANSI C
__FUNCTION__ main 所在函數名稱

這些宏與編譯器有關,有些支持有些不支持.

以下程序:

h1.png

運行結果爲:

h2.png

注意到: 若是用函數或內聯函數,每次的行號便都會相同。

下面是內核中,常見的兩個宏:

Linux經常使用的兩個宏

offsetof

該宏的定義以下:

#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )

做用是獲取結構體某成員變量的偏移量。

分析以下:

  1. (type *)0 將0轉化爲該類型的指針,即地址爲0x00000000
  2. ((type *)0)->member 訪問成員member
  3. &(((type *)0)->member) 獲取該成員地址(也就是其偏移量)
  4. (size_t)&(((type *)0)->member) 將地址轉化爲size_t類型 即偏移量

這裏訪問0指針爲什麼不會報錯,取決於gcc對於該過程的優化,不會直接訪問空間而是直接得到地址.

container_of

該宏的定義以下:

#define __container_of__(ptr, type, member) ({\
                const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
                ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
                })

要理解這段宏,須要知道幾個GCC C EXTENSIONS,查閱GCC MANUAL:

1.Statements and Declarations in Expressions
m1.png
3.JPG

2.Referring to a Type with typeof

m2.png
手冊中也給出了一個典型的用法示例:

m3.png

這段宏的分析以下:

  1. typeof()GNU C ,得到變量類型
  2. typeof (((type *)0 )->member) 起始地址爲0 再獲取member 最後返回member類型
  3. const typeof (((type *)0 )->member) * __mptr=(ptr) 定義 __mptr 指針,指向ptr指向的地址,併成爲常量指針
  4. (char *)__mptr __mptr轉化爲字符型指針(運算以1個字節爲單位)
  5. - __offsetof__(type,member)) 減去該成員的偏移量
  6. (type*)( ( char * )__mptr - __offsetof__(type,member)) 最後轉化爲指向該類型的指針(指向該類型的首地址)

關於上述兩個宏的一段程序以下:

#include <stdio.h>
#include <string.h>

/**
 * 獲取結構體變量成員的偏移量
 * @param type 類型(struct)
 * @param member 成員
 */
#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )

/**
 * 獲取指向整個結構體的指針
 * @param ptr 指向成員(member)變量的指針
 * @param type 類型(struct)
 * @param member 成員變量
 */
#define __container_of__(ptr, type, member) ({\
                const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
                ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
                })

typedef struct Student {
    char gender;
    int id;
    int age;
    char name[20];
    double score;
} Stu;

int main() {
    int gender_offset,id_offset,age_offset,name_offset,score_offset;
    gender_offset = __offsetof__(struct Student, gender);
    id_offset     = __offsetof__(struct Student, id);
    age_offset    = __offsetof__(struct Student, age);
    name_offset   = __offsetof__(struct Student, name);
    score_offset  = __offsetof__(struct Student, score);
    printf("%d\t%d\t%d\t%d\t%d\n", gender_offset, id_offset, age_offset, name_offset, score_offset);

    Stu stu;
    Stu *pstu;
    stu.gender = '1';
    stu.id = 9527;
    stu.age = 18;
    stu.score = 98.2;
    strcpy(stu.name, "elioyang");

    pstu = __container_of__(&stu.id, Stu, id);

    printf("gender=%c\n", pstu->gender);
    printf("age=%d\n", pstu->age);
    printf("name=%s\n", pstu->name);
    printf("score=%lf", pstu->score);

    return 0;
}

運行結果以下:

0       4       8       12      32
gender=1
age=18
name=elioyang
score=98.200000
相關文章
相關標籤/搜索