linux命令源码剖析 linux命令源代码

Linux 之mutex 源码分析 mutex相关的函数并不是linux kernel实现的,而是glibc实现的,源码位于nptl目录下 。
首先说数据结构:
typedef union
{
struct
{
int __lock;
unsigned int __count;
int __owner;
unsigned int __nusers;
/* KIND must stay at this position in the structure to maintain
binary compatibility.*/
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
结构体其他元素暂时不了解,以后更新 。
/*nptl/pthread_mutex_init.c*/
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 ?: default_attr;
/* Clear the whole variable.*/
memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);
/* Copy the values from the attribute.*/
mutex-__data.__kind = imutexattr-mutexkind~0x80000000;
/* Default values: mutex not used yet.*/
// 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属性 。
/*nptl/pthread_mutex_lock.c*/
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))
{

default:
/* Correct code cannot set any other type.*/
case PTHREAD_MUTEX_TIMED_NP:
simple:
/* Normal mutex.*/
LLL_MUTEX_LOCK (mutex-__data.__lock);
break;

}
/* Record the ownership.*/
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); 宏定义:
/* The only basic operation needed is compare and exchange.*/
#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 = opFUTEX_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;
}
/*
* requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.
* number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.
*/
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 = opFUTEX_CMD_MASK;
int fshared = 0;
if (!(opFUTEX_PRIVATE_FLAG))
fshared = 1;
clockrt = opFUTEX_CLOCK_REALTIME;
if (clockrtcmd != FUTEX_WAIT_BITSETcmd != 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;

default:
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:
/* Prepare to wait on uaddr. */
ret = futex_wait_setup(uaddr, val, fshared, q, hb);
if (ret)
goto out;
/* queue_me and wait for wakeup, timeout, or a signal. */
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. 退出循环 , 访问用户控件的临界资源 。
/*nptl/pthread_mutex_unlock.c*/
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))
{
… …
default:
/* Correct code cannot set any other type.*/
case PTHREAD_MUTEX_TIMED_NP:
case PTHREAD_MUTEX_ADAPTIVE_NP:
/* Normal mutex.Nothing special to do.*/
break;
}
/* Always reset the owner field.*/
mutex-__data.__owner = 0;
if (decr)
/* One less user.*/
--mutex-__data.__nusers;
/* Unlock.*/
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 (__val1, 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 (__val1, 0) 是编译器优化语句,告诉编译器期望值,也就是大多数情况下__val1 ?是0 , 其逻辑判断依然为if(__val1)为真的话执行 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;
}
/* Check if one of the bits is set in both bitsets */
if (!(this-bitsetbitset))
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;
/*
* We set q-lock_ptr = NULL _before_ we wake up the task. If
* a non futex wake up happens on another CPU then the task
* might exit and p would dereference a non existing task
* struct. Prevent this by holding a reference on p across the
* wake up.
*/
get_task_struct(p);
plist_del(q-list, q-list.plist);
/*
* The waiting task can free the futex_q as soon as
* q-lock_ptr = NULL is written, without taking any locks. A
* memory barrier is required here to prevent the following
* store to lock_ptr from getting ahead of the plist_del.
*/
smp_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内核源代码怎么看?一般在Linux系统中linux命令源码剖析的/usr/src/linux*.*.*(*.*.*代表的是内核版本linux命令源码剖析,如2.4.23)目录下就是内核源代码(如果没有类似目录,是因为还没安装内核代码) 。另外还可从互连网上免费下载 。注意,不要总到去下载,最好使用它的镜像站点下载 。请在里找一个合适的下载点,再到pub/linux/kernel/v2.6/目录下去下载2.4.23内核 。
代码目录结构
在阅读源码之前 , 还应知道Linux内核源码的整体分布情况 。现代的操作系统一般由进程管理、内存管理、文件系统、驱动程序和网络等组成 。Linux内核源码的各个目录大致与此相对应 , 其组成如下(假设相对于Linux-2.4.23目录):
1.arch目录包括linux命令源码剖析了所有和体系结构相关的核心代码 。它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录 。PC机一般都基于此目录 。
2.include目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在include/linux子目录下 。
3.init目录包含核心的初始化代码(不是系统的引导代码),有main.c和Version.c两个文件 。这是研究核心如何工作的好起点 。
4.mm目录包含linux命令源码剖析了所有的内存管理代码 。与具体硬件体系结构相关的内存管理代码位于arch/*/mm目录下 。
5.drivers目录中是系统中所有的设备驱动程序 。它又进一步划分成几类设备驱动,每一种有对应的子目录,如声卡的驱动对应于drivers/sound 。
6.ipc目录包含了核心进程间的通信代码 。
7.modules目录存放了已建好的、可动态加载的模块 。
8.fs目录存放Linux支持的文件系统代码 。不同的文件系统有不同的子目录对应,如ext3文件系统对应的就是ext3子目录 。
Kernel内核管理的核心代码放在这里 。同时与处理器结构相关代码都放在arch/*/kernel目录下 。
9.net目录里是核心的网络部分代码,其每个子目录对应于网络的一个方面 。
10.lib目录包含了核心的库代码,不过与处理器结构相关的库代码被放在arch/*/lib/目录下 。
11.scripts目录包含用于配置核心的脚本文件 。
12.documentation目录下是一些文档,是对每个目录作用的具体说明 。
一般在每个目录下都有一个.depend文件和一个Makefile文件 。这两个文件都是编译时使用的辅助文件 。仔细阅读这两个文件对弄清各个文件之间的联系和依托关系很有帮助 。另外有的目录下还有Readme文件,它是对该目录下文件的一些说明,同样有利于对内核源码的理解 。
在阅读方法或顺序上,有纵向与横向之分 。所谓纵向就是顺着程序的执行顺序逐步进行linux命令源码剖析;所谓横向,就是按模块进行 。它们经常结合在一起进行 。对于Linux启动的代码可顺着Linux的启动顺序一步步来阅读;对于像内存管理部分,可以单独拿出来进行阅读分析 。实际上这是一个反复的过程,不可能读一遍就理解 。
linux源代码,求详细分析,代码如下,来自 /mm: filemap.h找到了个帖子,你可以参考参考 。原帖地址:
参考:
如果你没有安装Berkeley db(8)库软件包,或不知是否拥有;你就需要在Makefile文件中去除对它的支持 。编辑你的Makefile文件,查找以DBMDEF开始的行:
DBMDEF= -DNDBM –DNEWDB –DINS ?这一行
删除这个部分
删除字符串“-DNEWDB”这个部分后,修改完成后,结果如下所示:
DBMDEF= -DNDBM –DNIS ?删除以后
接着查找以LIBS开始的行:
LIBS= -ldb -ldbm -lresolv?这一行(不完全准确)
删除这个部分
删除这行的字符串“-ldb”这个部分,修改完成后 , 结果如下所示:
LIBS= -ldbm -lresolv ?删除以后
这样就完成了从你的Makefile中删除对Berkeley db的支持 。
如果你的系统支持Berkeley db的话,则你还需要给Makefile一些消息 。必须指定正确的头文件和库函数的路径 , 以便将其包含到sendmail中去 。查找以INCDIRS开始的行:
INCDIRS=-I/usr/sww/include ?这一行
# loader options
LDOPTS= -Bstatic
# Library directories
LIBDIRS=-L/usr/sww/lib?还有这一行
你需要将这两行中的路径名改为Berkeley db的库所在路径 。如果你无法确定,最好去除对Berkeley db的支持 。
INCDIRS=-I/usr/local/include/db?新的
# loader options
LDOPTS= -Bstatic
# Library directories
LIBDIRS=-L/usr/local/lib?新的
上面这个修改的例子 , 是基于Berkeley db的头文件在/usr/local/include/db,库文件在/usr/local/lib目录下所做的,可供参考 。
希望以上内容对你有所帮助!
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节点?。┍槔拿恳桓鼋诘悖∷胣先将下一个要遍历的节点保存起来,防止删除本节点后,无法找到下一个节点,而出现错误!
已知指向某个结构体的指针pos,以及指向它中member成员的指针head,从下一个结构体开始向后遍历这个结构体链
Note: 同理,先保存下一个要遍历的节点!从head下一个节点向后遍历链表 。
list.h使用说明
linux内核list.h分析(一)
linux内核list.h分析(二)
【Linux内核数据结构】最为经典的链表list
谁能给我一个Linux系统中du命令的详细分析?(包括源代码的分析)Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的,这里介绍Linux du命令 。
du:查询档案或目录的磁盘使用空间
a:显示全部目录和其次目录下的每个档案所占的磁盘空间
b:大小用bytes来表示 (默认值为k bytes)
c:最后再加上总计 (默认值)
s:只显示各档案大小的总合
x:只计算同属同一个档案系统的档案
L:计算所有的档案大小
常用命令:du –a
操作详解
引用
指令 du 能以指定的目录下的子目录为单位,显示每个目录内所有档案所占用的磁盘空间大小 。例如:
# du -h /etc
104K /etc/defaults
6.0K /etc/X11
8.0K /etc/bluetooth
4.0K /etc/gnats
52K /etc/isdn
388K /etc/mail
68K /etc/mtree
2.0K /etc/ntp
38K /etc/pam.d
44K /etc/periodic/daily
6.0K /etc/periodic/monthly
42K /etc/periodic/security
16K /etc/periodic/weekly
110K /etc/periodic
6.0K /etc/ppp
318K /etc/rc.d
2.0K /etc/skel
130K /etc/ssh
10K /etc/ssl
1.7M /etc
我们目样使用 -h 参数来显示 human-readable 的格式 。在应用时,我们可以使用 du 这个指令来查看哪个目录占用最多的空间 。不过 , du 的输出结果通常很长 , 我们可以加上 -s 参数来省略指定目录下的子目录,而只显示该目录的总合即可:
# du -sh /etc
1.7M /etc
在查看目录的使用情形时,我们可以将输出结果导到 sort 指令进行排序 , 以了解哪个档案用了最多的空间:
# du /etc | sort -nr | more
1746 /etc
388 /etc/mail
318 /etc/rc.d
130 /etc/ssh
110 /etc/periodic
104 /etc/defaults
68 /etc/mtree
52 /etc/isdn
44 /etc/periodic/daily
42 /etc/periodic/security
38 /etc/pam.d
16 /etc/periodic/weekly
10 /etc/ssl
8 /etc/bluetooth
6 /etc/ppp
6 /etc/periodic/monthly
6 /etc/X11
4 /etc/gnats
2 /etc/skel
2 /etc/ntp
sort 的参数 -nr 表示要以数字排序法进行反向排序 , 因为我们要对目录大小做排序,所以不可以使用 human-readable 的大小输出,不然目录大小中会有 K、M 等字样,会造成排序不正确 。
现在明了了Linux du命令和Linux df命令的不同之处了吗
【linux命令源码剖析 linux命令源代码】linux命令源码剖析的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux命令源代码、linux命令源码剖析的信息别忘了在本站进行查找喔 。

    推荐阅读