├── README.md ├── t.c ├── do_rsync.sh ├── pic ├── footprint.png ├── footprint2.png ├── kernel_list.png └── kernel_list_2.png ├── none.html ├── kernel_list.html ├── swappiness.html ├── container_of.html └── comment.html /README.md: -------------------------------------------------------------------------------- 1 | # blog_posts 2 | 存放博客文章 3 | -------------------------------------------------------------------------------- /t.c: -------------------------------------------------------------------------------- 1 | for test 2 | for test2 3 | llalalla 4 | -------------------------------------------------------------------------------- /do_rsync.sh: -------------------------------------------------------------------------------- 1 | rsync -aSvH ./ root@qindao:~/sparrow/sparrow/website/blog/posts 2 | -------------------------------------------------------------------------------- /pic/footprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codercheng/blog_posts/master/pic/footprint.png -------------------------------------------------------------------------------- /pic/footprint2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codercheng/blog_posts/master/pic/footprint2.png -------------------------------------------------------------------------------- /pic/kernel_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codercheng/blog_posts/master/pic/kernel_list.png -------------------------------------------------------------------------------- /pic/kernel_list_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codercheng/blog_posts/master/pic/kernel_list_2.png -------------------------------------------------------------------------------- /none.html: -------------------------------------------------------------------------------- 1 |
kernel中链表实现的实在是精妙,赶紧记下来。下面将会介绍下其精妙的实现:
Linux内核中链表的实现不同于我们平常教科书中得实现方法。我们上课时学到的链表很简单,这里也不再介绍,但是其有一个很大的缺点,就是不可复用,不容易扩展(例如:你需要学生信息组成一个链表,还需要书本信息组成另一链表,传统的链表你无法复用,需要定义两个不同的链表Node)。但是,kernel中链表能够做到可复用性,下面具体分析。
kernel中链表定义在include/linux/list.h中, 是一个双循环链表。其不同之处在于,它不是将数据结构塞入链表,而是把链表结点塞入数据结构。直接上图:(上:普通链表;下:内核链表)


上面是普通链表和内核中链表的示意图(上:普通链表;下:内核链表),看出不同了么?最大的不同就是普通链表指向的是整个struct结构体(数据结构塞入链表),而内核的链表指向的可是一个只包含next,prev指针的结构体(即,整个struct结构的一个成员。)(链表结点塞入数据结构)。这就是为什么内核中得链表可以复用了。
那么问题来了,由于内核链表中没有包含其它的数据成员,那么怎么根据next/prev指向的结构得到整个结构体呢?
首先给出这个可以复用的链表Node:
struct list_head {
struct list_head *next, *prev;
};直接在你需要组成链表的struct中添加struct list_head node就可以了。比如:
struct student {
int sid;
char name[256];
/* 链表节点 */
struct list_head node;
};现在来解决上面提到的问题:怎么根据结构题的一个成员来得到整个结构题呢?kernel中实现用到了container_of宏,这个宏可以解决这个问题,那么所用问题就迎刃而解了。关于container_of的详细介绍,请移步 详解内核中的container_of宏
本文只讲解了kernel中如何实现链表的精妙,具体链表的操作方法实现不在这作介绍,请移步 深入分析 Linux 内核链表,这里面对链表的每个方法都讲得比较清楚。
--------------------------------------------------------------------------------
/swappiness.html:
--------------------------------------------------------------------------------
1 | 今天一个同学问我,他的一个程序跑着跑着,中途会出现一个进程kswapd7,导致程序会卡住。 下面就这个问题来分析分析。首先我们看看kswapd这个进程
kswapd(Kernel Swap Daemon)是用来处理页面的交换的内核守护进程,它可以在内存不足时,将一些进程的页面交换到swap空间之中。
问题当然不会仅仅是内存不足这么简单,他的机器64G内存,还剩20G空闲,按理说,不会出现内存不足啊。为什么会导致内存拼命的swap呢?
本人第一感觉是,会不会管理员限制了你用户的内存最大可用值呢?可是人家是root用户啊,并没有这种限制啊。这个原因可以排除掉。然后就想着会不会有什么内核参数,控制着swap的交换,果然,给我发现了新大陆,可以通过调节swappiness内核参数降低系统对swap的使用。原来内核并不是在内存用完后才会开始使用swap把内存换到外存,而是由swappiness这个参数控制着。
Swappiness is a Linux kernel parameter that controls the relative weight given to swapping out runtime memory, as opposed to dropping pages from the system page cache.
root用户打开/etc/sysctl.conf参看你的vm. swappiness的值。
swappiness的值[0,100], 比如在ubuntu中默认vm.swappiness = 60. 表示60%的内存已使用后,开始使用swap。
由上面的分析,前面的案例,那个同学的机器默认的vm.swappiness=60, 已用内存44/64=68.75% > 60% 。那么swap已经开始工作了,所以会看到kswapd7进程出现,大量内存被交换到外存。
我看了下我在阿里云上面的服务器,vm.swappiness默认设置的为0,注意: 0 并不是表示禁止swap,当out of memory的时候,还是会使用swap,也就是不到万不得已,不会使用swap。。当然你可以不设置为0,一般来说,经常设置为1, 10。修改后通过下面命令直接动态加载/etc/sysctl.conf使生效。
sysctl -p当然,动态加载的话,你首先还需要把swap中得内容给交换到内存里面去。用如下命令。
swapoff -a && swapon -a //把swap关闭后再重启。一般服务器上,我们需要调整vm. swappiness的值,比如Ubuntu的默认值60,这样在大内存需求的情况下,会导致swap大量出现,导致性能下降。我们根据业务需求,尽量调小swappiness的值,减少swap。
--------------------------------------------------------------------------------
/container_of.html:
--------------------------------------------------------------------------------
1 | 我们知道linux内核代码中链表的实现用到了一个container_of宏定义,用来根据成员的地址来获取结构体的地址。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
不知所云?请看下面分解:
宏定义能否也有返回值?当然是可以的。那么该如何实现宏定义的返回值呢,很简单:通过使用小括号()把函数体括起来使用宏定义可以得到该函数体最后一条语句运算结果。
/* return (a++)+(b++) */
#define addAB(a, b) ({a++; b++; a+b;})详细或者更多用法 》》》
offsetof宏在 stddef.h 头文件有定义,可以直接使用,但是理解其具体实现,对于理解container_of有很大的帮助。其实现如下:
#define offsetof(type, member) (size_t)&(((type *)0)->member)其作用是返回member在struct或者union结构中得偏移。
(type *)0编译器会认为这是一个type类型的指针,并且起始地址为0。当然如果把0换成1,那么其起始地址就认为是1。这样一来,我们可以轻松的得到表示member成员(((type *)0)->member)。那么我们要得到offset,只需要取member的地址(前面我们说了该结构体起始地址为0)然后转换成size_t类型就是表示其偏移。
下面让我们来一句句分析这个宏定义:
const typeof( ((type *)0)->member ) *__mptr = (ptr);这一句用到了typeof,表示这个member所表示的类型,定义了一个const的该类型指针__mptr,并指向ptr。
(type *)( (char *)__mptr - offsetof(type,member) );这一句,offsetof我们前面已经说过了,表示member在type这个结构中的偏移量;__mptr指针表示ptr指向的地址,这个地址可是真正的地址,不是上面说到得从0开始的地址;那么用这个真正的地址(需要把__mptr转换成char *结构)减去偏移,就得到了该结构体实际的起始地址。那么把这个地址转换成(type *)结构就表示了该结构的的地址,直接用该struct指针指向该地址,那么就根据member获得了整个结构体的地址。
同样,这一句,也是这个宏定义的返回值,就是返回了整个结构体的地址。
疑惑? 其实我觉得上面两句合并成下面一句不就可以了,不知道内核中干嘛分成两部分。知道的请在评论中给我留言,感激不尽。
(type *)( (char *)(ptr) - offsetof(type,member) );#include <stdio.h>
#include <stdlib.h>
#define offsetof(type, member) (size_t)&(((type *)0)->member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct Student {
int id;
char sex;
char tmp;
int age;
};
int main()
{
printf("%d\n", offsetof(struct Student, sex));
printf("%d\n", offsetof(struct Student, age));
printf("%d\n", offsetof(struct Student, tmp));
struct Student *p1 = (struct Student *)malloc(sizeof(struct Student));
p1->age = 10;
struct Student *p2;
/* 由p1的age成员,得到了指向整个p1的指针p2 */
p2 = container_of(&(p1->age), struct Student, age);
printf("this is p2's age:%d\n", p2->age);
printf("%p==%p\n", p1, p2);
return 0;
}
为了更好的理解container_of的使用,请看后续文章, kernel中链表的实现。
2 |
--------------------------------------------------------------------------------
/comment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 
留下你的足迹 



221 |
222 |
--------------------------------------------------------------------------------