假设我们有一种数据结构foo,需要维持一个这种数据结构的双链队列,最简单,最常见的办法就是在这个数据结构的类型定义中加入两个指针;
typedef struct foo
{
struct foo *prev;
struct foo *next;
.........;
} foo;
之后的工作就是为这种数据结构写一套用于各种队列操作的子程序,由于用来维持队列的这两个指针的类型是固定(都指向foo数据结构),这些子程序不能用于其它数据结构的队列操作,换言之,需要维持多少种数据结构的队列,就得有多少套的队列操作子程序,对于使用队列较少的应用程序或许不是个问题,但是对于大量队列的内核就有点力不从心啦!
linux内核针对此问题采用了一套通用的,一般的,可以用到各种不同数据结构的队列操作.为此 linux内核代码的此项作者把指针preev和next从具体的"宿主"数据结构中
抽象出来成为一种新的数据结构list_head;这种数据结构即可以"寄宿"在具体的宿主数据结构内部,成为该数据结构的一个"连接件";也可以独立存在而成为一个队列的头
linux内核中这个数据机构位于/usr/src/kernels/2.6.32-279.el6.x86_64/include/linux/list.h中,仅仅是数据结构类型的申明,;
[list_add()>__list_Add()]
/*
* Insert a new entry between two know consecutive entries.
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add(
struct list_head * new,
struct list_head * prev,
struct list_head * next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
队列拖链的操作list_del():
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev,entry->next);
}
调用另一个inline函数__list_del()来完成操作
static __inline__ void __list_del(struct list_head * prev,struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
page = memlist_entry(curr,struct page,list);
/**
* list_entry - get the struct for this entry
* @ptr:the &struct list_head pointer.
* @type:the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr,type,member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)));
小异有时候也是很重要的,稍不加重视就会造成困扰!汇编Intel,AT&T代码格式 区别;
Intel和AT&T在汇编格式上的小异:
a. Intel格式中字母大写,AT&T中小写;
b. AT&T格式中寄存器名前要加%作为前缀,Intel语句中不用;
c. 在AT&T的386 assembly中,instruct的源操作数与目标操作数的顺序与在Intel的386 assembly中正好相反,在Intel中目标在前,源在后,而在AT&T格式中则是源在前,目标在后.eg:
将寄存器exa的内容送入ebx,在Intel格式为MOVE EBA,EAX
而在AT&T格式中为"move %eax,%ebx";
d. 在AT&T格式中,access instruct Operter data size(width)由操作码名称的最后一个字母(也就是操作码的后缀)来决定,用作操作码后缀的字母有
b(express 8 bits)
w(express 16 bits)
l(express 21 bits)
在Intel格式中,则是表示内存单元的操作数前加上"BYTE PTR","WORD PTR","DWORD PTR"
eg:将FOO所指内存单元中的字节取入8bits的寄存器AL
MOV AL-操作目标, BYTE PTR(表示操作数宽带) FOO--操作源 (Intel格式)
movb FOO,%al
e. 在AT&T格式中,直接操作数要加上"$"作为前缀,而在Intel格式中则不带前缀,所以Intel格式中的"PUSH4",在AT&T格式中就变成push$4
f. 在AT&T格式中,绝对转移或调用指令jump/call的操作数(也即转移或调用的目标地址)要加上"*"作为前缀,Intel汇编指令中不带;
g. 远程的转移指令和子程序调用指令的操作码名称,在AT&T格式中为"ljmp" 和 "lcall",而在Intel格式中,则为"JMP FAR" 和”CALL FAR“;
当转移和调用的目标为直接操作数时
CALL FAR SECTION:OFFSET(Intel format)
JMP FAR SECTION:OFFSET
lcall $section,$offset
ljmp $section,$offset
与此相应的远程返回的指令
RET FAR STACK-ADJUST
lret $stack_adjust
h. 间接 indirect address general format
SECTION:[BASE+INDEX*SCALE+DISP]
section:disp(base,index,scale)
这种寻址方式常常用于在数据结构数组中访问特定元素内的一个自动,base为数组的起始地址,Scale为每个数组元素的大小,index为下标.
当需要在C语言程序中嵌入一段汇编语言程序段时,可以使用gcc提供的”asm“语句功能
#define __SLOW_DOWN_IO __ASM__ __volatile__ ("cutb %al,$0x80")
static __inline__ void atomic_add(int i,atomic_t *v)
{
__asm__ __volatile__(
LOCK "addl %1,%0"
:"=m" (v->counter)
:"ir" (i),"m" (v->counter));
}
指令部:输出部:输入部:损坏
原文:http://www.cnblogs.com/ruiy/p/nel.html