Linux内核源码解析-list.h
开头就说明了这里的 list.h 文件来自 Linux Kernel ( */include/linux/list.h ),只是去除了列表项的硬件预加载部分。
进行宏替换后就是
Note: 没搞懂这里为什么加个 osn 前缀,原本是 list_add ,现在是 osn_list_add 。
可以看到就是个简单的链表节点删除过程,同时把删除节点的前后指针设为无法访问。
删除节点后初始化,前后指针都指向自己
从A链表删除后头插法插入B链表
从A链表删除后尾插法插入B链表
先对 list 判空,非空就把 list 链表除头节点外裁剪到 head 头节点在的链表中。函数不安全, list 节点可以继续访问其他节点。
多了一步 list 重新初始化的过程。
(unsigned long)(((type *)0)-member))) 将0x0地址强制转换为 type * 类型,然后取 type 中的成员 member 地址,因为起始地址为0,得到的 member 的地址就直接是该成员相对于 type 对象的偏移地址了。
所以该语句的功能是:得到 type 类型对象中 member 成员的地址偏移量。
先将 ptr 强制转换为 char * 类型(因为 char * 类型进行加减的话,加减量为 sizeof(char)*offset , char 占一个字节空间,这样指针加减的步长就是1个字节,实现加一减一。)
整句话的意思就是:得到指向 type 的指针,已知成员的地址,然后减去这个成员相对于整个结构对象的地址偏移量,得到这个数据对象的地址。
就是从前往后,从后往前的区别
Note: 从head节点开始(不包括head节点!)遍历它的每一个节点!它用n先将下一个要遍历的节点保存起来,防止删除本节点后,无法找到下一个节点,而出现错误!
已知指向某个结构体的指针pos,以及指向它中member成员的指针head,从下一个结构体开始向后遍历这个结构体链
Note: 同理,先保存下一个要遍历的节点!从head下一个节点向后遍历链表。
list.h使用说明
linux内核list.h分析(一)
linux内核list.h分析(二)
【Linux内核数据结构】最为经典的链表list
linux内核源码在哪
一般在Linux系统中的/usr/src/linux*.*.*(*.*.*代表的是内核版本,如2.4.23)目录下就是内核源代码(如果没有类似目录,是因为还没安装内核代码)。另外还可从互连网上免费下载。注意,不要总到去下载,***使用它的镜像站点下载。请在里找一个合适的下载点,再到pub/linux/kernel/v2.6/目录下去下载2.4.23内核。
代码目录结构
在阅读源码之前,还应知道Linux内核源码的整体分布情况。现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序和网络等组成。Linux内核源码的各个目录大致与此相对应,其组成如下(假设相对于Linux-2.4.23目录):
1.arch目录包括了所有和体系结构相关的核心代码。它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录。PC机一般都基于此目录。
2.include目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在include/linux子目录下。
3.init目录包含核心的初始化代码(不是系统的引导代码),有main.c和Version.c两个文件。这是研究核心如何工作的好起点。
4.mm目录包含了所有的内存管理代码。与具体硬件体系结构相关的内存管理代码位于archkernel目录下。
9.net目录里是核心的网络部分代码,其每个子目录对应于网络的一个方面。
10.lib目录包含了核心的库代码,不过与处理器结构相关的库代码被放在arch
int __kind;
int __spins;
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;
int __lock; 资源竞争引用计数
int __kind; 锁类型,init 函数中mutexattr 参数传递,该参数可以为NULL,一般为 PTHREAD_MUTEX_NORMAL
结构体其他元素暂时不了解,以后更新。
int
__pthread_mutex_init (mutex, mutexattr)
pthread_mutex_t *mutex;
const pthread_mutexattr_t *mutexattr;
{
const struct pthread_mutexattr *imutexattr;
assert (sizeof (pthread_mutex_t) = __SIZEOF_PTHREAD_MUTEX_T);
imutexattr = (const struct pthread_mutexattr *) mutexattr ?: def***lt_attr;
memset (mutex, ' ', __SIZEOF_PTHREAD_MUTEX_T);
mutex-__data.__kind = imutexattr-mutexkind ~0x80000000;
// mutex-__count = 0; already done by memset
// mutex-__owner = 0; already done by memset
// mutex-__nusers = 0; already done by memset
// mutex-__spins = 0; already done by memset
return 0;
}
init函数就比较简单了,将mutex结构体清零,设置结构体中__kind属性。
int
__pthread_mutex_lock (mutex)
pthread_mutex_t *mutex;
{
assert (sizeof (mutex-__size) = sizeof (mutex-__data));
pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
switch (__builtin_expect (mutex-__data.__kind, PTHREAD_MUTEX_TIMED_NP))
{
…
def***lt:
case PTHREAD_MUTEX_TIMED_NP:
simple:
LLL_MUTEX_LOCK (mutex-__data.__lock);
break;
…
}
assert (mutex-__data.__owner == 0);
mutex-__data.__owner = id;
#ifndef NO_INCR
++mutex-__data.__nusers;
#endif
return 0;
}
该函数主要是调用LLL_MUTEX_LOCK, 省略部分为根据mutex结构体__kind属性不同值做些处理。
宏定义函数LLL_MUTEX_LOCK最终调用,将结构体mutex的__lock属性作为参数传递进来
#define __lll_mutex_lock(futex)
((void) ({
int *__futex = (futex);
if (atomic_compare_and_exchange_bool_acq (__futex, 1, 0) != 0)
__lll_lock_wait (__futex);
}))
atomic_compare_and_exchange_bool_acq (__futex, 1, 0)宏定义为:
#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval)
({ __typeof (mem) __gmemp = (mem);
__typeof (*mem) __gnewval = (newval);
*__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })
这个宏实现的功能是:
如果mem的值等于oldval,则把newval赋值给mem,放回0,否则不做任何处理,返回1.
由此可以看出,当mutex锁限制的资源没有竞争时,__lock 属性被置为1,并返回0,不会调用__lll_lock_wait (__futex); 当存在竞争时,再次调用lock函数,该宏不做任何处理,返回1,调用__lll_lock_wait (__futex);
void
__lll_lock_wait (int *futex)
{
do
{
int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
if (oldval != 0)
lll_futex_wait (futex, 2);
}
while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
}
atomic_compare_and_exchange_val_acq (futex, 2, 1); 宏定义:
#define atomic_compare_and_exchange_val_acq(mem, newval, oldval)
({ __typeof (mem) __gmemp = (mem);
__typeof (*mem) __gret = *__gmemp;
__typeof (*mem) __gnewval = (newval);
if (__gret == (oldval))
*__gmemp = __gnewval;
__gret; })
这个宏实现的功能是,当mem等于oldval时,将mem置为newval,始终返回mem原始值。
此时,futex等于1,futex将被置为2,并且返回1. 进而调用
lll_futex_wait (futex, 2);
#define lll_futex_timed_wait(ftx, val, timespec)
({
DO_INLINE_SYSCALL(futex, 4, (long) (ftx), FUTEX_WAIT, (int) (val),
(long) (timespec));
_r10 == -1 ? -_retval : _retval;
})
该宏对于不同的平台架构会用不同的实现,采用汇编语言实现系统调用。不过确定的是调用了Linux kernel的futex系统调用。
futex在linux kernel的实现位于:kernel/futex.c
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
struct timespec __user *, utime, u32 __user *, uaddr2,
u32, val3)
{
struct timespec ts;
ktime_t t, *tp = NULL;
u32 val2 = 0;
int cmd = op FUTEX_CMD_MASK;
if (utime (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
cmd == FUTEX_WAIT_BITSET ||
cmd == FUTEX_WAIT_REQUEUE_PI)) {
if (copy_from_user(ts, utime, sizeof(ts)) != 0)
return -EFAULT;
if (!timespec_valid(ts))
return -EINVAL;
t = timespec_to_ktime(ts);
if (cmd == FUTEX_WAIT)
t = ktime_add_safe(ktime_get(), t);
tp = t;
}
if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
val2 = (u32) (unsigned long) utime;
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
}
futex具有六个形参,pthread_mutex_lock最终只关注了前四个。futex函数对参数进行判断和转化之后,直接调用do_futex。
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
u32 __user *uaddr2, u32 val2, u32 val3)
{
int clockrt, ret = -ENOSYS;
int cmd = op FUTEX_CMD_MASK;
int fshared = 0;
if (!(op FUTEX_PRIVATE_FLAG))
fshared = 1;
clockrt = op FUTEX_CLOCK_REALTIME;
if (clockrt cmd != FUTEX_WAIT_BITSET cmd != FUTEX_WAIT_REQUEUE_PI)
return -ENOSYS;
switch (cmd) {
case FUTEX_WAIT:
val3 = FUTEX_BITSET_MATCH_ANY;
case FUTEX_WAIT_BITSET:
ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);
break;
…
def***lt:
ret = -ENOSYS;
}
return ret;
}
省略部分为对其他cmd的处理,pthread_mutex_lock函数最终传入的cmd参数为FUTEX_WAIT,所以在此只关注此分之,分析futex_wait函数的实现。
static int futex_wait(u32 __user *uaddr, int fshared,
u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
{
struct hrtimer_sleeper timeout, *to = NULL;
struct restart_block *restart;
struct futex_hash_bucket *hb;
struct futex_q q;
int ret;
… … //delete parameters check and convertion
retry:
ret = futex_wait_setup(uaddr, val, fshared, q, hb);
if (ret)
goto out;
futex_wait_queue_me(hb, q, to);
… … //other handlers
return ret;
}
futex_wait_setup 将线程放进休眠队列中,
futex_wait_queue_me(hb, q, to);将本线程休眠,等待唤醒。
唤醒后,__lll_lock_wait函数中的while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0); 语句将被执行,由于此时futex在pthread_mutex_unlock中置为0,所以atomic_compare_and_exchange_bool_acq (futex, 2, 0)语句将futex置为2,返回0. 退出循环,访问用户控件的临界资源。
int
internal_function attribute_hidden
__pthread_mutex_unlock_usercnt (mutex, decr)
pthread_mutex_t *mutex;
int decr;
{
switch (__builtin_expect (mutex-__data.__kind, PTHREAD_MUTEX_TIMED_NP))
{
… …
def***lt:
case PTHREAD_MUTEX_TIMED_NP:
case PTHREAD_MUTEX_ADAPTIVE_NP:
break;
}
mutex-__data.__owner = 0;
if (decr)
--mutex-__data.__nusers;
lll_mutex_unlock (mutex-__data.__lock);
return 0;
}
省略部分是针对不同的__kind属性值做的一些处理,最终调用 lll_mutex_unlock。
该宏函数最终的定义为:
#define __lll_mutex_unlock(futex)
((void) ({
int *__futex = (futex);
int __val = atomic_exchange_rel (__futex, 0);
if (__builtin_expect (__val 1, 0))
lll_futex_wake (__futex, 1);
}))
atomic_exchange_rel (__futex, 0);宏为:
#define atomic_exchange_rel(mem, value)
(__sync_synchronize (), __sync_lock_test_and_set (mem, value))
实现功能为:将mem设置为value,返回原始mem值。
__builtin_expect (__val 1, 0) 是编译器优化语句,告诉编译器期望值,也就是大多数情况下__val 1 ?是0,其逻辑判断依然为if(__val 1)为真的话执行 lll_futex_wake。
现在分析,在资源没有被竞争的情况下,__futex 为1,那么返回值__val则为1,那么 lll_futex_wake (__futex, 1); 不会被执行,不产生系统调用。 当资源产生竞争的情况时,根据对pthread_mutex_lock 函数的分析,__futex为2, __val则为2,执行 lll_futex_wake (__futex, 1); 从而唤醒等在临界资源的线程。
lll_futex_wake (__futex, 1); 最终会调动同一个系统调用,即futex, 只是传递的cmd参数为FUTEX_WAKE。
在linux kernel的futex实现中,调用
static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
{
struct futex_hash_bucket *hb;
struct futex_q *this, *next;
struct plist_head *head;
union futex_key key = FUTEX_KEY_INIT;
int ret;
if (!bitset)
return -EINVAL;
ret = get_futex_key(uaddr, fshared, key);
if (unlikely(ret != 0))
goto out;
hb = hash_futex(key);
spin_lock(hb-lock);
head = hb-chain;
plist_for_each_entry_safe(this, next, head, list) {
if (match_futex (this-key, key)) {
if (this-pi_state || this-rt_waiter) {
ret = -EINVAL;
break;
}
if (!(this-bitset bitset))
continue;
wake_futex(this);
if (++ret = nr_wake)
break;
}
}
spin_unlock(hb-lock);
put_futex_key(fshared, key);
out:
return ret;
}
该函数遍历在该mutex上休眠的所有线程,调用wake_futex进行唤醒,
static void wake_futex(struct futex_q *q)
{
struct task_struct *p = q-task;
get_task_struct(p);
plist_del(q-list, q-list.plist);
***p_wmb();
q-lock_ptr = NULL;
wake_up_state(p, TASK_NORMAL);
put_task_struct(p);
}
wake_up_state(p, TASK_NORMAL); 的实现位于kernel/sched.c中,属于linux进程调度的技术。
linux内核源码安装
起因:想查看链表list.h源码,但系统中没有找到
经过一番搜索,总结下安装步骤:
此时需要安装一个mock,并新建一个mockbuild用户
4、命令行执行
5、回到根目录下 /root/rpmbuild ,可以看到SPECS文件夹cd到这个文件夹
执行 rpmbuild -bp --target = $ (uname -m) kernel.spec
如果提示rpmbuild not find,需要先安装rpmbuild, yum install rpmbuild
6、运行后会出现错误,提示
执行
7、依赖包安装完成后,再次执行
至此,安装完成。
如何编译linux源代码
首先uname -r看一下你当前的linux内核版本
1、linux的源码是在/usr/src这个目录下,此目录有你电脑上各个版本的linux内核源代码,用uname -r命令可以查看你当前使用的是哪套内核,你把你下载的内核源码也保存到这个目录之下。
2、配置内核 make menuconfig,根据你的需要来进行选择,设置完保存之后会在当前目录下生成.config配置文件,以后的编译会根据这个来有选择的编译。
3、编译,依次执行make、make bzImage、make modules、make modules
4、安装,make install
5、.创建系统启动映像,到 /boot 目录下,执行 mkinitramfs -o initrd.img-2.6.36 2.6.36
6、修改启动项,因为你在启动的时候会出现多个内核供你选择,此事要选择你刚编译的那个版本,如果你的电脑没有等待时间,就会进入默认的,默认的那个取决于 /boot/grub/grub.cfg 文件的设置,找到if [ "${linux_gfx_mode}" != "text" ]这行,他的***个就是你默认启动的那个内核,如果你刚编译的内核是在下面,就把代表这个内核的几行代码移到***位如:
menuentry 'Ubuntu, with Linux 3.2.0-35-generic' --class ubuntu --class gnu-linux --class gnu --class os {
recordfail
gfxmode $linux_gfx_mode
in***od gzio
in***od part_msdos
in***od ext2
set root='(hd0,msdos1)'
search --no-floppy --fs-uuid --set=root 9961c170-2566-41ac-8155-18f231c1bea5
linux/boot/vmlinuz-3.2.0-35-generic root=UUID=9961c170-2566-41ac-8155-18f231c1bea5 ro quiet splash $vt_handoff
initrd/boot/initrd.img-3.2.0-35-generic
}
当然你也可以修改 set def***lt="0"来决定用哪个,看看你的内核在第几位,def***lt就填几,不过我用过这种方法,貌似不好用。
重启过后你编译的内核源码就成功地运行了,如果出现问题,比如鼠标不能用,u***不识别等问题就好好查查你的make menuconfig这一步,改好后就万事ok了。
最后再用uname -r看看你的linux内核版本。是不是你刚下的那个呢!有没有成就感?
关于linux源码和linux源码目录的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。