简介和概述
本章最后还涉及到一些相关的代码,将在这介绍。
《深入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)