簡介和概述
本章最後還涉及到一些相關的代碼,將在這介紹。
《深入Linux內核架構》使用的源代碼是kernel 2.6.24(後續稱參考代碼),這裏以它爲依據作說明。
計時
Linux提供了全局變量jiffies(和jiffies_64)作爲時間座標,它位於include\linux\jiffies.h:
/*
* The 64-bit value is not atomic - you MUST NOT read it
* without sampling the sequence number in xtime_lock.
* get_jiffies_64() will do this for you as appropriate.
*/
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
jiffies會按恆定的時間間隔遞增。
鏈表
鏈表接口位於include\linux\list.h,鏈表數據結構必須包含如下類型的成員:
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
它是一個雙向鏈表組件。
對於真正的雙向鏈表實例,都需要包含它,下面是一個例子:
struct sas_port {
struct device dev;
int port_identifier;
int num_phys;
/* port flags */
unsigned int is_backlink:1;
/* the other end of the link */
struct sas_rphy *rphy;
struct mutex phy_list_mutex;
struct list_head phy_list;
};
有一個宏用來初始化鏈表:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
下面是一個例子:
static struct list_head devreg_anchor = LIST_HEAD_INIT(devreg_anchor);
標準的雙向鏈表如下圖所示:
鏈表的操作函數有:
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is in an undefined state.
*/
static inline void list_del(struct list_head *entry)
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(struct list_head *head)
等等,這裏就不全列舉了。下面是一個函數調用的例子:
BUG_ON(!list_empty(&port->phy_list));
內核對象
內核需要跟蹤C語言中的結構體實例,它通過給這些實例包含內核對象來實現。
內核對象的結構位於include\linux\kobject.h,如下所示:
struct kobject {
const char * k_name;
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct sysfs_dirent * sd;
};
k_name是對象的文本名稱,可利用sysfs(《深入Linux內核架構》第十章會介紹)導出到用戶空間。
kref是引用計數,當它的值是0,表示不需要該對象了。
其它成員暫不介紹。
kobject抽象實際上提供了在內核使用面向對象技術的可能性。
需要跟蹤的結構體實例必須嵌入上述的kobject,下面是一個例子:
/* device block control structure */
struct edac_device_block {
struct edac_device_instance *instance; /* Up Pointer */
char name[EDAC_DEVICE_NAME_LEN + 1];
struct edac_device_counter counters; /* basic UE and CE counters */
int nr_attribs; /* how many attributes */
/* this block's attributes, could be NULL */
struct edac_dev_sysfs_block_attribute *block_attributes;
/* edac sysfs device control */
struct kobject kobj;
};
數據類型定義
在include\linux\types.h中定義了很多的類型,比如u_int8_t等:
typedef __u8 u_int8_t;
typedef __s8 int8_t;
typedef __u16 u_int16_t;
typedef __s16 int16_t;
typedef __u32 u_int32_t;
typedef __s32 int32_t;
需要注意這裏有包含一個頭文件:
#include <asm/types.h>
但實際上並沒有asm這樣的目錄,它實際上算是一個鏈接,真正對應的是帶平臺的:
它會在編譯的時候真正指定。
字節序
計算機有大端和小端的字節序。
內核提供了函數和宏來處理字節序,位於include\linux\byteorder\generic.h中:
/*
* inside the kernel, we can use nicknames;
* outside of it, we must avoid POSIX namespace pollution...
*/
#define cpu_to_le64 __cpu_to_le64
#define le64_to_cpu __le64_to_cpu
#define cpu_to_le32 __cpu_to_le32
#define le32_to_cpu __le32_to_cpu
#define cpu_to_le16 __cpu_to_le16
#define le16_to_cpu __le16_to_cpu
per-cpu
在有若干個CPU的SMP(Symmetrical Multi-Processing)系統中,會爲每個CPU分別創建變量的實例。
#define get_cpu() ({ preempt_disable(); smp_processor_id(); })
可以獲取特定CPU的實例。
void smp_setup_processor_id(void);
可以獲取當前活動的處理器ID。
__user
內核使用__user來標識指向用戶空間中區域的指針,在沒有進一步預防措施的情況下,不能輕易訪問這些指針指向的區域。
比如:
alpha_clone(unsigned long clone_flags, unsigned long usp,
int __user *parent_tid, int __user *child_tid,
unsigned long tls_value, struct pt_regs *regs)