diff --git a/study/debug/README.md b/study/debug/README.md
index 77d7155..4c263af 100644
--- a/study/debug/README.md
+++ b/study/debug/README.md
@@ -32,13 +32,16 @@ Linux内核调试的方式以及工具集锦
|:---:|:----:|
| debugfs等文件系统 | 提供了 `procfs`, `sysfs`, `debugfs`以及 `relayfs` 来与用户空间进行数据交互, 尤其是 **`debugfs`**, 这是内核开发者们实现的专门用来调试的文件系统接口. 其他的工具或者接口, 多数都依赖于 `debugfs`. |
| printk | 强大的输出系统, 没有什么逻辑上的`bug`是用`PRINT`解决不了的 |
-| ftrace以及其前端工具trace-cmd等 | **内核**提供了 **`ftrace`** 工具来实现检查点, 事件等的检测, 这一框架依赖于 `debugfs`, 他在 `debugfs` 中的 `tracing` 子系统中为用户提供了丰富的操作接口, 我们可以通过该系统对内核实现检测和分析. 功能虽然强大, 但是其操作并不是很简单, 因此**使用者们**为实现了 **`trace-cmd`** 等前端工具, 简化了 `ftrace` 的使用. |
+| ftrace以及其前端工具trace-cmd等 | **内核**提供了 **`ftrace`** 工具来实现检查点, 事件等的检测, 这一框架依赖于 `debugfs`, 他在 `debugfs` 中的 `tracing` 子系统中为用户提供了丰富的操作接口, 我们可以通过该系统对内核实现检测和分析. 功能虽然强大, 但是其操作并不是很简单, 因此使用者们为其实现了 **`trace-cmd`** 等前端工具, 简化了 `ftrace` 的使用. |
| `kprobe`以及更强大的`systemtap` | 内核中实现的 `krpobe` 通过类似与代码劫持一样的技巧, 在内核的代码或者函数执行前后, 强制加上某些调试信息, 可以很巧妙的完成调试工作, 这是一项先进的调试技术, 但是仍然有觉得它不够好, 劫持代码需要用驱动的方式编译并加载, 能不能通过脚本的方式自动生成劫持代码并自动加载和收集数据, 于是`systemtap` 出现了. 通过 `systemtap` 用户只需要编写脚本, 就可以完成调试并动态分析内核 |
| kgdb && kgtp | `KGDB` 是大名鼎鼎的内核调试工具, `KGTP`则通过驱动的方式强化了 `gdb`的功能, 诸如tracepoint, 打印内核变量等. |
| perf | `erf Event`是一款随 `inux`内核代码一同发布和维护的性能诊断工具, 核社区维护和发展. `Perf` 不仅可以用于应用程序的性能统计分析, 也可以应用于内核代码的性能统计和分析. 得益于其优秀的体系结构设计, 越来越多的新功能被加入 `Perf`, 使其已经成为一个多功能的性能统计工具集 |
| LTTng | `LTTng` 是一个 `Linux` 平台开源的跟踪工具, 是一套软件组件, 可允许跟踪 `Linux` 内核和用户程序, 并控制跟踪会话(开始/停止跟踪、启动/停止事件 等等). |
-
-
+| eBPF | eBPF(extended Berkeley Packet Filter) |
+| ktap | |
+| dtrace4linux | `Sun DTracer` 的 `Linux` 移植版 |
+| OL DTrace | `Oracle Linux DTracer` |
+| sysdig |
#2 用户空间与内核空间数据交换的文件系统
-------
@@ -479,8 +482,13 @@ KGTP在Linux内核 2.6.18到upstream 上都被测试过。
[LTTng and LTTng project](http://blog.csdn.net/ganggexiongqi/article/details/6664331)
+#9 参考资料
+-------
+
[Linux内核调试方法](http://www.cnblogs.com/shineshqw/articles/2359114.html)
+[choose-a-linux-traccer](http://www.brendangregg.com/blog/2015-07-08/choosing-a-linux-traccer.html), [中英文对照](http://www.oschina.net/translate/choossing-a-linux-tracer?cmp)
+
http://blog.csdn.net/bob_fly1984/article/details/51405856
http://www.verydemo.com/demo_c167_i62250.html
http://www.oschina.net/translate/dynamic-debug-howto?print
@@ -493,6 +501,7 @@ http://www.fx114.net/qa-120-128312.aspx
http://www.fx114.net/qa-259-116990.aspx
+
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作
diff --git a/study/debug/modules/trace/00-code/trace-events-sample/Makefile.bak b/study/debug/modules/trace/00-code/trace-events-sample/Makefile.bak
deleted file mode 100644
index 372101a..0000000
--- a/study/debug/modules/trace/00-code/trace-events-sample/Makefile.bak
+++ /dev/null
@@ -1,81 +0,0 @@
-# ------------------------------------------------------------------------------
-#
-# Makefile for the LDD-LinuxDeviceDrivers.
-#
-# Author: gatieme
-# Create: 2016-07-29 15:50:46
-# Last modified: 2016-07-29 16:10:29
-# Description:
-# This program is loaded as a kernel(v2.6.18 or later) module.
-# Use "make install" to load it into kernel.
-# Use "make remove" to remove the module out of kernel.
-#
-# ------------------------------------------------------------------------------
-
-
-# my driver description
-DRIVER_VERSION := "1.0.0"
-DRIVER_AUTHOR := "Gatieme @ AderStep Inc..."
-DRIVER_DESC := "Linux input module for Elo MultiTouch(MT) devices"
-DRIVER_LICENSE := "Dual BSD/GPL"
-
-
-MODULE_NAME := trace-events-sample
-#MODCFLAGS:=-O2 -Wall -DMODULE -D__KERNEL__ -DLINUX -std=c99
-SRC := /home/gatieme/Work/GitHub/LDD-LinuxDeviceDrivers/study/debug/trace/trace-events-sample
-EXTRA_CFLAGS += -I$(SRC)
-
-
-
-
-
-ifneq ($(KERNELRELEASE),)
-
-
-
-
-obj-m := $(MODULE_NAME).o #print_vmarea.o
-
-else
-
-KERNELDIR ?= /lib/modules/$(shell uname -r)/build
-
-PWD := $(shell pwd)
-
-modules:
- make -C $(KERNELDIR) M=$(PWD) modules
-
-modules_install:
- make -C $(KERNELDIR) M=$(PWD) modules_install
-
-
-
-insmod:
- sudo insmod $(MODULE_NAME).ko
-
-reinsmod:
- sudo rmmod $(MODULE_NAME)
- sudo insmod $(MODULE_NAME).ko
-
-github:
- cd $(ROOT) && make github
-
-rmmod:
- sudo rmmod $(MODULE_NAME)
-
-test :
- cat /sys/kernel/debug/mydebug/a
- cat /sys/kernel/debug/mydebug/b
- cat /sys/kernel/debug/mydebug/subdir/c
-
-clean:
- make -C $(KERNELDIR) M=$(PWD) clean
- rm -f modules.order Module.symvers Module.markers
-
-.PHNOY:
- modules modules_install clean
-
-
-
-endif
-
diff --git a/study/debug/tools/systemtap/01-install/README.md b/study/debug/tools/systemtap/01-install/README.md
index d6b4a14..8574980 100644
--- a/study/debug/tools/systemtap/01-install/README.md
+++ b/study/debug/tools/systemtap/01-install/README.md
@@ -445,6 +445,7 @@ stap -ve 'probe begin { log("hello world") exit() }'
[SystemTap 学习笔记 - 安装篇](https://segmentfault.com/a/1190000000671438)
+[SystemTap使用技巧【一】](http://blog.csdn.net/wangzuxi/article/details/42849053)
@@ -452,4 +453,3 @@ stap -ve 'probe begin { log("hello world") exit() }'
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作
因本人技术水平和知识面有限, 内容如有纰漏或者需要修正的地方, 欢迎大家指正, 也欢迎大家提供一些其他好的调试工具以供收录, 鄙人在此谢谢啦
-
diff --git a/study/debug/tools/systemtap/02-cross_compile/systemtap_bin.png b/study/debug/tools/systemtap/02-cross_compile/systemtap_bin.png
new file mode 100644
index 0000000..6c53c5b
Binary files /dev/null and b/study/debug/tools/systemtap/02-cross_compile/systemtap_bin.png differ
diff --git a/study/driver/force_rmmod/src/02-stap/force_rmmod.stp b/study/driver/force_rmmod/src/02-stap/force_rmmod.stp
new file mode 100644
index 0000000..d22d399
--- /dev/null
+++ b/study/driver/force_rmmod/src/02-stap/force_rmmod.stp
@@ -0,0 +1,145 @@
+#!/usr/bin/stap
+%{
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 重新注册驱动的exit函数 */
+//#define CONFIG_REPLACE_EXIT_FUNCTION
+%}
+
+global del_mod_name, mod, depond
+
+probe begin
+{
+ log("begin to probe")
+
+ printf("start to force rmmod module:%s\n", @1)
+
+ del_mod_name = @1
+
+ // ==1== 查找到待删除驱动的结构信息
+ mod = find_module_addr(del_mod_name)
+
+ if (mod == -1) {
+ printf("==1==can't find module %s\n", del_mod_name)
+ exit()
+ } else {
+ printf("==1==find module %s success...\n", del_mod_name)
+ }
+
+ // ==2== 检查是否有其他模块依赖于待删除驱动
+ depond = check_depond_modules(mod)
+ if (depond == -1) {
+ printf("==2==there are some modules depond on %s"
+ ", we can't remove it...\n",
+ del_mod_name)
+ exit()
+ } else {
+ printf("==2==no modules depond on %s...\n", del_mod_name)
+ }
+
+ // ==3== 强制cleanup待删除驱动
+ printf("==3==cleanup module %s...\n", del_mod_name)
+ printf("start to cleanup module %s...\n", del_mod_name)
+ force_remove_module(mod)
+ printf("cleanup module %s success...\n", del_mod_name)
+
+ exit()
+}
+
+/////////////////////
+// 找到待删除模块的内核module信息
+/////////////////////
+function find_module_addr:long (del_mod_name:string)
+%{
+ struct module *mod = NULL;
+#if 0
+ // 方法一, 遍历内核模块树 list_mod 查询
+ struct module *list_mod = NULL;
+ /* 遍历模块列表, 查找 del_mod_name 模块 */
+ list_for_each_entry(list_mod, THIS_MODULE->list.prev, list) {
+ if (strcmp(list_mod->name, STAP_ARG_del_mod_name) == 0) {
+ mod = list_mod;
+ }
+ }
+#else
+ // 方法二, 通过 find_module 函数查找模块信息
+ mod = find_module(STAP_ARG_del_mod_name);
+#endif
+ if (mod == NULL) {
+ //printk("module %s not found\n", STAP_ARG_del_mod_name);
+ STAP_RETVALUE = (long)-1;
+ } else {
+ //printk("[before] name:%s, state:%d, refcnt:%u\n",
+ // mod->name ,mod->state, module_refcount(mod));
+ STAP_RETVALUE = (long)mod;
+ }
+ //STAP_RETURN((long)mod);
+%}
+
+/////////////////////
+// 如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出
+/////////////////////
+function check_depond_modules:long (del_mod:long)
+%{
+ struct module *mod = (struct module *)STAP_ARG_del_mod;
+
+ /////////////////////
+ // 如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出
+ /////////////////////
+ /* 如果有其他模块依赖于 del_mod */
+ if (!list_empty(&mod->source_list)) {
+ STAP_RETVALUE = (long)-1;
+ } else {
+ STAP_RETVALUE = (long)0;
+ }
+%}
+
+/////////////////////
+// 清除驱动的状态和引用计数
+/////////////////////
+function force_remove_module:long (del_mod:long)
+%{
+ struct module *mod = (struct module *)STAP_ARG_del_mod;
+ int cpu;
+
+ mod->state = MODULE_STATE_LIVE;
+
+ /* 清除驱动的引用计数 */
+ for_each_possible_cpu(cpu) {
+ local_set((local_t *) per_cpu_ptr(&(mod->refcnt), cpu), 0);
+ }
+ atomic_set(&mod->refcnt, 1);
+
+ /* 重新注册驱动的exit函数 */
+#ifdef CONFIG_REPLACE_EXIT_FUNCTION
+ mod->exit = force_replace_exit_module_function;
+#endif
+%}
+
+%{
+#ifdef CONFIG_REPLACE_EXIT_FUNCTION
+// 此处为外部注册的待卸载模块的exit函数
+// 用于替代模块原来的exit函数
+// 注意--此函数由于需要被待删除模块引用, 因此不能声明为static
+/* static */ void force_replace_exit_module_function(void)
+{
+ /////////////////////
+ // 此处完善待卸载驱动的 exit/cleanup 函数
+ /////////////////////
+ printk("module exit SUCCESS...\n");
+}
+#endif // CONFIG_REPLACE_EXIT_FUNCTION
+%}
+
+probe end
+{
+ log("end to probe")
+}
diff --git a/study/driver/force_rmmod/src/02-stap/test.stp b/study/driver/force_rmmod/src/02-stap/test.stp
deleted file mode 100644
index 7617f84..0000000
--- a/study/driver/force_rmmod/src/02-stap/test.stp
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/stap
-%{
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* 重新注册驱动的exit函数 */
-//#define CONFIG_REPLACE_EXIT_FUNCTION
-%}
-
-global del_mod_name, mod, depond
-
-probe begin
-{
- log("begin to probe")
-
- printf("start to force rmmod module:%s\n", @1)
-
- del_mod_name = @1
-
- // ==1== 查找到待删除驱动的结构信息
- mod = find_module_addr(del_mod_name)
- if(mod == -1)
- {
- printf("==1==can't find module %s\n", del_mod_name)
- exit( )
- }
- else
- {
- printf("==1==find module %s success...\n", del_mod_name)
- }
-
- // ==2== 检查是否有其他模块依赖于待删除驱动
- depond = check_depond_modules(mod)
- if(depond == -1)
- {
- printf("==2==there are some modules depond on %s, we can't remove it...\n", del_mod_name)
- exit( )
- }
- else
- {
- printf("==2==no modules depond on %s...\n", del_mod_name)
- }
-
- // ==3== 强制cleanup待删除驱动
- printf("==3==cleanup module %s...\n", del_mod_name)
- printf("start to cleanup module %s...\n", del_mod_name)
- force_remove_module(mod)
- printf("cleanup module %s success...\n", del_mod_name)
-
- exit( )
-}
-
-/////////////////////
-// 找到待删除模块的内核module信息
-/////////////////////
-function find_module_addr:long(del_mod_name:string)
-%{
- struct module *mod = NULL;
-
- // 方法一, 遍历内核模块树list_mod查询
- struct module *list_mod = NULL;
- /* 遍历模块列表, 查找 del_mod_name 模块 */
- list_for_each_entry(list_mod, THIS_MODULE->list.prev, list)
- {
- if (strcmp(list_mod->name, STAP_ARG_del_mod_name) == 0)
- {
- mod = list_mod;
- }
- }
-
- //if((mod = find_module(STAP_ARG_del_mod_name)) == NULL)
- if(mod == NULL)
- {
- //printk("module %s not found\n", STAP_ARG_del_mod_name);
- STAP_RETVALUE = (long)-1;
- }
- else
- {
- //printk("[before] name:%s, state:%d, refcnt:%u\n",
- // mod->name ,mod->state, module_refcount(mod));
- STAP_RETVALUE = (long)mod;
- }
- //STAP_RETURN((long)mod);
-%}
-
-/////////////////////
-// 如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出
-/////////////////////
-function check_depond_modules:long(del_mod:long)
-%{
- struct module *mod = (struct module *)STAP_ARG_del_mod;
-
- /////////////////////
- // 如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出
- /////////////////////
- /* 如果有其他模块依赖于 del_mod */
- if (!list_empty(&mod->source_list))
- {
- STAP_RETVALUE = (long)-1;
- }
- else
- {
- STAP_RETVALUE = (long)0;
- }
-%}
-
-/////////////////////
-// 清除驱动的状态和引用计数
-/////////////////////
-function force_remove_module:long(del_mod:long)
-%{
- struct module *mod = (struct module *)STAP_ARG_del_mod;
- int cpu;
-
- mod->state = MODULE_STATE_LIVE;
-
- /* 清除驱动的引用计数 */
- for_each_possible_cpu(cpu)
- {
- local_set((local_t*)per_cpu_ptr(&(mod->refcnt), cpu), 0);
- }
- atomic_set(&mod->refcnt, 1);
-
-
- /* 重新注册驱动的exit函数 */
-#ifdef CONFIG_REPLACE_EXIT_FUNCTION
- mod->exit = force_replace_exit_module_function;
-#endif
-%}
-
-%{
-#ifdef CONFIG_REPLACE_EXIT_FUNCTION
-// 此处为外部注册的待卸载模块的exit函数
-// 用于替代模块原来的exit函数
-// 注意--此函数由于需要被待删除模块引用, 因此不能声明为static
-/* static */ void force_replace_exit_module_function(void)
-{
- /////////////////////
- // 此处完善待卸载驱动的 exit/cleanup 函数
- /////////////////////
- printk("module exit SUCCESS...\n");
-}
-#endif // CONFIG_REPLACE_EXIT_FUNCTION
-%}
-
-
-probe end
-{
- log("end to probe")
-}
diff --git a/study/patch/submit_patch/README.md b/study/patch/submit_patch/README.md
new file mode 100644
index 0000000..73bd989
--- /dev/null
+++ b/study/patch/submit_patch/README.md
@@ -0,0 +1,689 @@
+AderXCoding
+=======
+
+| CSDN | GitHub |
+|:----:|:------:|
+| [Aderstep--紫夜阑珊-青伶巷草](http://blog.csdn.net/gatieme) | [`AderXCoding/system/tools`](https://github.com/gatieme/AderXCoding/tree/master/system/tools) |
+
+
+
+
+
+本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作
+
+因本人技术水平和知识面有限, 内容如有纰漏或者需要修正的地方, 欢迎大家指正, 也欢迎大家提供一些其他好的调试工具以供收录, 鄙人在此谢谢啦
+
+
+
+
+#1 应有的心态
+-------
+
+`Paul` 在《深入理解并行编程》一书第 `11.1.2` 节中说, 验证和测试工作都需要良好的心态. 应当以一种破坏性的、甚至带一点仇恨的心理来验证代码,有时也应当考虑到:不少人的生命依赖于我们代码正确性的几率。总之,心态对事情的成败有重要的影响。
+
+同样的,在向社区提交补丁之前,我们也应当有正确的心态:
+
+1. 我们的代码,可能会影响不少人的生命,所以一定要细心。
+
+2. 悲观的说,如果补丁做得不好,会被社区大牛的口水淹死。当然,更可能的情况是,大牛懒得向你吐口水:)所以在提交补丁前,请为自己的声誉负责。
+
+3. 乐观的说,社区牛人一般都比较Nice,如果你的水平真的比较牛的话。
+
+4. 更进一步乐观的说,你提交的高质量的补丁,可能会为你带来良好的声誉、满意的工作。
+
+5. 需要强调一点的是,在社区,千万不要和Maintainer作对。要象对待学校老师那样,谦恭的对待Maintainer。不管你信不信这一点,反正我信了:)
+ 如果你和我一样,有着近乎自大的自信,请接着读完本文^-^
+
+
+#2 准备工作
+-------
+
+在开始工作之前,请准备如下工作:
+
+#2.1 安装一份Linux
+-------
+
+不论是 `ubuntu`、`centos` 还是其他 `Linux` 发行版本, 都是可以的.
+
+我个人习惯使用 `ubuntu 16.04` 版本. 虽然我曾经尝试在 `windows` 下面安装 `git`, 但是最终你会发现, 在 `windows` 下面使用 `git` 提交补丁几乎可以用痛苦来描述.
+
+
+##2.2 安装git
+-------
+
+默认的 `Linux` 发行版, 一般都已经安装好 `git`. 如果没有, 随便找一本 `git` 的书都可以. 这里不详述. 比较好的git资料有 :
+
+http://git.oschina.net/progit/
+
+https://item.jd.com/11615420.html
+
+##2.3 配置git
+-------
+
+###2.3.1 配置用户名和邮箱
+-------
+
+在配置用户名的时候, 请注意社区朋友习惯用英语沟通, 也就是名在前, 姓在后, 这一点会影响社区邮件讨论, 因此需要留意.(但是随着中文内核开发者的增多, 这点其实已经不那么重要了, 大家还是习惯 `姓 名` 的方式).
+
+在配置邮箱时, 也要注意. 社区会将国内某些著名的邮件服务器屏蔽. 因此建议你申请一个 `gmail` 邮箱.
+
+以下是我的配置:
+
+xiebaoyou@ThinkPad-T440$git config -l | grep "user"
+ user.email=baoyou.xie@linaro.org
+ user.name=Baoyou Xie
+
+###2.3.2 配置 `sendemail`
+-------
+
+你可以手工修改 `~/.gitconfig`, 或者 `git` 仓库下的 `.git/config`文件, 添加 `[sendemail]`节.
+
+该配置用于指定发送补丁时用到的邮件服务器参数。
+
+以下是我的配置, 供参考 :
+
+```cpp
+ [sendemail]
+ smtp encryption= tls
+ smtp server= smtp.gmail.com
+ smtp user= baoyou.xie@linaro.org
+ smtp serverport= 587
+```
+
+`gmail` 邮箱的配置比较麻烦, 需要按照 `google` 的说明, 制作证书. 配置完成后, 请用如下命令, 向自己发送一个测试补丁:
+
+```cpp
+git send-email your.patch --to your.mail --cc your.mail
+```
+
+##2.3 下载源码
+-------
+
+首先,请用如下命令, 拉取 `linus` 维护的 `Linux` 主分支代码到本地 :
+
+```cpp
+git clone ssh://git@dev-private.git.linaro.org/zte/kernel.git
+```
+
+
+
+
+
+这个过程比较长,请耐心等待。
+
+
+##2.4 linux-next 分支
+-------
+
+一般情况下, `Linux` 主分支代码不够新, 如果你基于这个代码制作补丁, 很有可能不会顺利的合入到 `Maintainer` 那里, 换句话说, `Maintainer` 会将补丁发回给你, 要求你重新制作.
+
+所以, 一般情况下, 你需要再用以下命令, 添加其他分支, 特别是 `linux-next` 分支. 强调一下, 你需要习惯基于 `linux-next` 分支进行工作.
+
+
+
+
+详细的信息, 请参见 [`Working with linux-next`](https://www.kernel.org/doc/man-pages/linux-next.html)
+
+```cpp
+# linux-next
+git remote add linux-next https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
+
+# linux-staging
+git remote add staging https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+
+# net tree
+git remote add net git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
+```
+
+然后用如下命令拉取这三个分支的代码到本地:
+
+```cpp
+git fetch --tags linux-next
+
+git fetch --tags staging
+
+git fetch --tags net
+```
+
+有些 `Maintainer` 维护了自己的代码分支, 那么, 你可以在内核源码目录 `\MAINTAINERS` 文件中, 找一下相应文件的维护者, 及其 `git` 地址.
+
+
+例如, `watchdog` 模块的信息如下 :
+
+```cpp
+ WATCHDOGDEVICE DRIVERS
+ M: Wim Van Sebroeck
+ R: Guenter Roeck
+ L: linux-watchdog@vger.kernel.org
+ W: http://www.linux-watchdog.org/
+ T: gitgit://www.linux-watchdog.org/linux-watchdog.git
+ S: Maintained
+ F: Documentation/devicetree/bindings/watchdog/
+ F: Documentation/watchdog/
+ F: drivers/watchdog/
+ F: include/linux/watchdog.h
+ F: include/uapi/linux/watchdog.h
+```
+
+其中, `git://www.linux-watchdog.org/linux-watchdog.git` 是其git地址。
+
+你可以用如下命令拉取 `watchdog` 代码到本地 :
+
+```cpp
+git remote add watchdog git://www.linux-watchdog.org/linux-watchdog.git
+
+gitfetch --tags watchdog
+```
+
+当然, 这里友情提醒一下, `MAINTAINERS` 里面的信息可能不一定准确, 这时候你可能需要借助 `google`, 或者问一下社区的朋友, 或者直接问一下作者本人. 不过, 一般情况, 基于 `linux-next` 分支工作不会有太大的问题. 实在有问题再去打扰作者本人.
+
+4、阅读Documentation/SubmittingPatches,这很重要。
+
+5、检出源码
+
+查看 `linux-next` 的最新 `tag`
+
+```cpp
+git tag -l "next-*" | tail -n1
+```
+
+
+
+可以看到 `linux-next` 最新的代码是 `next-20170901`
+
+```cpp
+git branch my_local_branch next-20170901
+```
+
+这个命令表示将 `linux-next` 分支的 `20170816` 这个 `tag` 作为本地 `my_local_branch` 的基础.
+
+```cpp
+git checkout my_local_branch
+```
+
+这个命令检索出 `linux-next` 的代码到新创建的 `my_local_branch`.
+
+上面两个命令可以直接使用如下命令代替
+
+```cpp
+git checkout -b my_local_branch next-20170901
+```
+
+这条命令将在本地创建分支 `my_local_branch` 与远程分支 `next-20170901` 相关联, 并直接检出代码.
+
+
+
+#3 寻找软柿子
+-------
+
+
+如果没有奇遇, 大厨一般都是从小工做起的. 我们不可能一开始就维护一个重要的模块, 或者修复一些非常重要的故障. 那么我们应当怎么样入手参与社区?
+
+这当然要寻找软柿子了. 拿着软柿子做出来的补丁, 可以让 `Maintainer` 无法拒绝合入你的补丁. 当然, 这么做主要还是为了在 `Maintainer` 那里混个脸熟. 否则, 以后你发的重要补丁, 人家可能不会理你.
+
+什么样的柿子最软?下面是答案:
+
+1. 消除编译警告
+
+2. 编码格式, 例如注释里面的单词拼写错误、对齐不规范、代码格式不符合社区要求.
+
+建议是从"消除编译警告"入手. 社区很多大牛, 都是这样成长起来的.
+
+我们平时编译内核, 基本上遇不到编译警告. 是不是内核非常完美, 没有编译警告, 非矣! 你用下面这个步骤试一下 :
+
+首先, 配置内核, 选择所有模块 :
+
+```cpp
+make ARCH=arm64 allmodconfig
+```
+
+请注意其中 "allmodconfig", 很有用的配置, 我们暂且可以理解为, 将所有模块都编译. 这样我们就可以查找所有模块中的编译警告了.
+
+
+下面这个命令开始编译所有模块 :
+
+```cpp
+make ARCH=arm64 EXTRA_CFLAGS="-Wmissing-declarations -Wmissing-prototypes" CROSS_COMPILE=/toolchains/aarch64-linux-gnu/bin/aarch64-linux-gnu-
+```
+
+其中 `EXTRA_CFLAGS="-Wmissing-declarations-Wmissing-prototypes"` 参数表示追踪所有 `missing-declarations`、 `missing-prototypes`类型的警告.
+
+
+`CROSS_COMPILE=/toolchains/aarch64-linux-gnu/bin/aarch64-linux-gnu-` 是指定交叉编译工具链路径, 需要根据你的实际情况修改. 当然, 如果是 `x86` 架构, 则不需要指定此参数.
+
+在编译的过程中, 我们发现如下错误 :
+
+
+```cpp
+scripts/Makefile.build:311:recipe for target 'drivers/staging/fsl-mc/bus/dpio/qbman-portal.o' failed
+```
+
+我们可以简单的忽略 `drivers/staging/fsl-mc/bus/dpio/qbman-portal.c` 这个文件.
+
+在 `drivers/staging/fsl-mc/bus/dpio/Makefile` 文件中, 发现这个文件的编译依赖于宏 `CONFIG_FSL_MC_DPIO`.
+
+于是,我们修改编译命令,以如下命令继续编译:
+
+```cpp
+make CONFIG_ACPI_SPCR_TABLE=n ARCH=arm64 EXTRA_CFLAGS="-Wmissing-declarations -Wmissing-prototypes" CROSS_COMPILE=/toolchains/aarch64-linux-gnu/bin/aarch64-linux-gnu-
+```
+
+请注意该命令中的 "CONFIG_ACPI_SPCR_TABLE=n", 它强制关闭了 `CONFIG_ACPI_SPCR_TABLE` 配置.
+
+当编译完成以后, 我们是不是发现有很多警告? 特别是在 `drivers` 目录下.
+
+下面是我在 `next-20170807` 版本中发现的警告 :
+
+```cpp
+/dimsum/git/kernel.next/drivers/clk/samsung/clk-s3c2410.c:363:13:warning: no previous prototype for 's3c2410_common_clk_init'[-Wmissing-prototypes]
+ void__init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ ^
+ CC drivers/clk/samsung/clk-s3c2412.o
+/dimsum/git/kernel.next/drivers/clk/samsung/clk-s3c2412.c:254:13:warning: no previous prototype for 's3c2412_common_clk_init'[-Wmissing-prototypes]
+ void__init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
+ ^
+ CC drivers/clk/samsung/clk-s3c2443.o
+/dimsum/git/kernel.next/drivers/clk/samsung/clk-s3c2443.c:388:13:warning: no previous prototype for 's3c2443_common_clk_init' [-Wmissing-prototypes]
+ void__init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
+```
+
+
+下一节,我们就基于这几个警告来制作补丁。
+
+#4 制作补丁
+-------
+
+
+##4.1 修改内核代码消除警告
+-------
+
+要消除这几个警告, 当然很简单了. 将这几个函数声明为 `static` 即可. 下面是我的修改 :
+
+```cpp
+git diff
+diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
+index e0650c3..8f4fc5a 100644
+--- a/drivers/clk/samsung/clk-s3c2410.c
++++ b/drivers/clk/samsung/clk-s3c2410.c
+@@ -360,7 +360,7 @@ static void __inits3c2410_common_clk_register_fixed_ext(
+ samsung_clk_register_alias(ctx, &xti_alias, 1);
+ }
+
+-void __init s3c2410_common_clk_init(structdevice_node *np, unsigned long xti_f,
++static void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ intcurrent_soc,
+ void__iomem *base)
+ {
+diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
+index b8340a4..2a2ce06 100644
+--- a/drivers/clk/samsung/clk-s3c2412.c
++++ b/drivers/clk/samsung/clk-s3c2412.c
+@@ -251,7 +251,7 @@ static void __init s3c2412_common_clk_register_fixed_ext(
+ samsung_clk_register_alias(ctx, &xti_alias, 1);
+ }
+
+-void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
++static void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
+ unsigned long ext_f, void __iomem *base)
+ {
+ struct samsung_clk_provider *ctx;
+diff --gita/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
+index abb935c..f0b88bf 100644
+--- a/drivers/clk/samsung/clk-s3c2443.c
++++ b/drivers/clk/samsung/clk-s3c2443.c
+@@ -385,7 +385,7 @@ static void __inits3c2443_common_clk_register_fixed_ext(
+ ARRAY_SIZE(s3c2443_common_frate_clks));
+ }
+
+-void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
++static void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
+ int current_soc,
+ void __iomem *base)
+```
+
+再编译一次, 警告果然被消除了. 原来, 社区工作如此简单.
+
+
+但是请允许我浇一盆冷水!
+
+##4.2 提交代码
+-------
+
+你先试着用下面的命令做一个补丁出来看看!
+
+
+```cpp
+git add drivers/clk/samsung/clk-s3c2410.c
+git add drivers/clk/samsung/clk-s3c2412.c
+git add drivers/clk/samsung/clk-s3c2443.c
+git commit drivers/clk/samsung/
+
+[zxic/67184930591] this is my test
+3 files changed, 3 insertions(+), 3deletions(-)
+
+gitformat-patch -s -v 1 -1
+```
+
+生成的补丁内容如下:
+
+```cpp
+cat v1-0001-this-is-my-test.patch
+From 493059190e9ca691cf08063ebaf945627a5568c7 Mon Sep 17 00:00:00 2001
+From: Baoyou Xie
+Date: Thu, 17 Aug 2017 19:23:13 +0800
+Subject: [PATCH v1] this is my test
+
+Signed-off-by: Baoyou Xie
+---
+ drivers/clk/samsung/clk-s3c2410.c | 2 +-
+ drivers/clk/samsung/clk-s3c2412.c | 2 +-
+ drivers/clk/samsung/clk-s3c2443.c | 2 +-
+ 3files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
+index e0650c3..8f4fc5a 100644
+--- a/drivers/clk/samsung/clk-s3c2410.c
++++ b/drivers/clk/samsung/clk-s3c2410.c
+@@ -360,7 +360,7 @@ static void __init s3c2410_common_clk_register_fixed_ext(
+ samsung_clk_register_alias(ctx,&xti_alias, 1);
+ }
+
+-void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
++static void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ int current_soc,
+ void __iomem *base)
+ {
+diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
+index b8340a4..2a2ce06 100644
+--- a/drivers/clk/samsung/clk-s3c2412.c
++++ b/drivers/clk/samsung/clk-s3c2412.c
+@@ -251,7 +251,7 @@ static void __inits3c2412_common_clk_register_fixed_ext(
+ samsung_clk_register_alias(ctx,&xti_alias, 1);
+ }
+
+-void __init s3c2412_common_clk_init(structdevice_node *np, unsigned long xti_f,
++static void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
+ unsigned long ext_f, void __iomem *base)
+ {
+ struct samsung_clk_provider *ctx;
+diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
+index abb935c..f0b88bf 100644
+--- a/drivers/clk/samsung/clk-s3c2443.c
++++ b/drivers/clk/samsung/clk-s3c2443.c
+@@ -385,7 +385,7 @@ static void __inits3c2443_common_clk_register_fixed_ext(
+ ARRAY_SIZE(s3c2443_common_frate_clks));
+ }
+
+-void __init s3c2443_common_clk_init(structdevice_node *np, unsigned long xti_f,
++static void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
+ int current_soc,
+ void __iomem *base)
+ {
+--
+2.7.4
+```
+
+你可以试着用
+
+```cpp
+git send-email v1-0001-this-is-my-test.patch --to baoyou.xie@linaro.org
+```
+
+将补丁发给 `Maintainer`. 记得准备好一个盆子, 接大家的口水:)
+
+在制作正确的补丁之前,我们需要这个错误的补丁错在何处:
+
+1. 应该将它拆分成三个补丁。
+
+也许这一点值得商酌, 因为这三个文件都是同一个驱动 : `clk: samsung`. 也许 `Maintainer` 认为它是同一个驱动, 做成一个补丁也是可以的.
+
+我觉得应该拆分成三个.
+
+当然了, 应当以 `Maintainer` 的意见为准. 不同的 `Maintainer` 也许会有不同的意见.
+
+2. 补丁描述实在太 `LOW`
+
+3. 补丁格式不正确
+
+4. 补丁内容不正确
+
+
+下一节我们逐个解决这几个问题.
+
+但是首先我们应当将补丁回退. 使用如下命令 :
+
+```cpp
+git reset HEAD~1
+```
+
+#5 制作正确的补丁
+-------
+
+##5.1 补丁描述
+-------
+
+
+补丁第一行是标题, 比较重要. 它首先应当是模块名称.
+
+但是我们怎么找到 `drivers/clk/samsung/clk-s3c2412.c` 文件属于哪个模块?
+
+可以试试下面这个命令, 看看 `drivers/clk/samsung/clk-s3c2412.c` 文件的历史补丁 :
+
+```cpp
+#git log drivers/clk/samsung/clk-s3c2412.c
+
+commit 02c952c8f95fd0adf1835704db95215f57cfc8e6
+Author:Martin Kaiser
+Date: Wed Jan 25 22:42:25 2017 +0100
+
+
+clk: samsung:mark s3c...._clk_sleep_init() as __init
+```
+
+`ok`, 模块名称是 "clk:samsung"
+
+
+下面是我为这个补丁添加的描述, 其中第一行是标题 :
+
+```cpp
+clk: samsung: mark symbols static where possible for s3c2410
+
+We get 1 warnings when building kernel withW=1:
+/dimsum/git/kernel.next/drivers/clk/samsung/clk-s3c2410.c:363:13:warning: no previous prototype for 's3c2410_common_clk_init'[-Wmissing-prototypes]
+ void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+
+In fact, this function is only used in thefile in which they are
+declared and don't need a declaration, butcan be made static.
+So this patch marks these functions with 'static'.
+```
+
+这段描述是我从其他补丁中拷贝出来的,有几下几点需要注意:
+
+1. 标题中故意添加了 "for s3c2410", 以区别于另外两个补丁
+
+2. "1 warnings" 这个单词中, 错误的使用了复数, 这是因为复制的原因
+
+3. "/dimsum/git/kernel.next/" 这个路径名与我的本地路径相关, 不应当出现在补丁中.
+
+4. 警告描述超过了 `80` 个字符, 但是这是一个特例, 这里允许超过 `80` 字符.
+
+5. 这些问题, 如果不处理的话, `Maintainer` 会不高兴的! 如果 `Maintainer` 表示了不满, 而你不修正的话, 这个补丁就会被忽略.
+
+修正后的补丁描述如下:
+
+```cpp
+clk: samsung: mark symbols static wherepossible for s3c2410
+
+We get 1 warning when building kernel withW=1:
+drivers/clk/samsung/clk-s3c2410.c:363:13:warning: no previous prototype for 's3c2410_common_clk_init'[-Wmissing-prototypes]
+ void__init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+
+In fact, this function is only used in thefile in which they are
+declared and don't need a declaration, but can be made static.
+So this patch marks these functions with 'static'.
+```
+
+
+我们的补丁描述一定要注意用词, 不要出现将 "unused" 写为 "no used" 这样的错误.
+
+反复使用 `git add,git commit` 将补丁提交到 `git` 仓库.
+
+
+终于快成功, 是不是想庆祝一下. 用 `git` 命令看看我们刚才提交的三个补丁 :
+
+```cpp
+root@ThinkPad-T440:/dimsum/git/kernel.next#git log drivers/clk/samsung/
+commit0539c5bc17247010d17394b0dc9f788959381c8f
+Author: Baoyou Xie
+Date: Thu Aug 17 20:43:09 2017 +0800
+
+ clk: samsung: mark symbols static where possible for s3c2443
+
+ We get 1 warning when building kernel with W=1:
+ drivers/clk/samsung/clk-s3c2443.c:388:13: warning: no previous prototypefor 's3c2443_common_clk_init' [-Wmissing-prototypes]
+ void __init s3c2443_common_clk_init(struct device_node *np, unsignedlong xti_f,
+
+ In fact, this function is only used in the file in which they are
+ declared and don't need a declaration, but can be made static.
+ So this patch marks these functions with 'static'.
+
+commitc231d40296b4ee4667e3559e34b00f738cae1e58
+Author: Baoyou Xie
+Date: Thu Aug 17 20:41:38 2017 +0800
+
+ clk: samsung: mark symbols static where possible for s3c2412
+
+ We get 1 warning when building kernel with W=1:
+ drivers/clk/samsung/clk-s3c2412.c:254:13: warning: no previous prototypefor 's3c2412_common_clk_init' [-Wmissing-prototypes]
+ void __init s3c2412_common_clk_init(struct device_node *np, unsignedlong xti_f,
+
+ In fact, this function is only used in the file in which they are
+ declared and don't need a declaration, but can be made static.
+ So this patch marks these functions with 'static'.
+
+commit ff8ea5ed4947d9a643a216d51f14f6cb87abcb97
+Author: Baoyou Xie
+Date: Thu Aug 17 20:40:50 2017 +0800
+
+ clk: samsung: mark symbols static where possible for s3c2410
+```
+
+
+但是, 你发现补丁描述里面还有什么不正确的吗?
+
+不过 `Maintainer` 也许发现不了这个问题, 然后 `~~~~` 然后这个补丁也可能被接收入内核.
+
+
+##5.2 生成补丁
+-------
+
+下面我们生成补丁 :
+
+```cpp
+#git format-patch -s -3
+0001-clk-samsung-mark-symbols-static-where-possible-for-s.patch
+0002-clk-samsung-mark-symbols-static-where-possible-for-s.patch
+0003-clk-samsung-mark-symbols-static-where-possible-for-s.patch
+```
+
+实际上, 我们的补丁仍然是错误的.
+
+##5.3 检查补丁
+-------
+
+在发送补丁前, 我们需要用脚本检查一下补丁 :
+
+```cpp
+./scripts/checkpatch.pl 000*
+---------------------------------------------------------------
+0001-clk-samsung-mark-symbols-static-where-possible-for-s.patch
+---------------------------------------------------------------
+WARNING: Possible unwrapped commit description (prefer a maximum 75 chars per line)
+#9:
+ void__init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+
+WARNING: line over 80 characters
+#29: FILE:drivers/clk/samsung/clk-s3c2410.c:363:
++static void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+
+total: 0 errors, 2 warnings, 8 lineschecked
+```
+
+请留意输出警告, 其中第一个警告是说我们的描述中, 有过长的语句. 前面已经提到, 这个警告可以忽略.
+
+但是第二个警告告诉我们代码行超过 `80` 个字符了. 这是不能忽略的警告, 必须处理.
+
+
+##5.4 修改补丁
+-------
+
+使用 "git resetHEAD~3" 命令将三个补丁回退. 重新修改代码 :
+
+```cpp
+static void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ 修改为
+ static void __init
+ s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ 也可以修改为
+ static void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+```
+
+我个人习惯用第一种方法, 因为看其他代码也是第一种修改方法.
+
+重新提交补丁, 并用 `git format-patch` 命令生成补丁.
+
+#6 发送补丁
+-------
+
+##6.1 获取 `Maintainer`
+-------
+
+
+生成正确的补丁后, 请再次用 `checkpatch.pl` 检查补丁正确性.
+
+确保无误后, 可以准备将它发送给 `Maintainer` 了.
+
+但是应该将补丁发给谁? 这可以用 `get_maintainer.pl` 来查看 :
+
+
+```cpp
+./scripts/get_maintainer.pl 000*
+Kukjin Kim (maintainer:ARM/SAMSUNG EXYNOS ARM ARCHITECTURES)
+Krzysztof Kozlowski (maintainer:ARM/SAMSUNG EXYNOS ARM ARCHITECTURES)
+Sylwester Nawrocki (supporter:SAMSUNG SOC CLOCK DRIVERS)
+Tomasz Figa (supporter:SAMSUNG SOC CLOCK DRIVERS)
+Chanwoo Choi (supporter:SAMSUNG SOC CLOCK DRIVERS)
+Michael Turquette (maintainer:COMMON CLK FRAMEWORK)
+Stephen Boyd (maintainer:COMMON CLK FRAMEWORK)
+linux-arm-kernel@lists.infradead.org(moderated list:ARM/SAMSUNG EXYNOS ARM ARCHITECTURES)
+linux-samsung-soc@vger.kernel.org (moderatedlist:ARM/SAMSUNG EXYNOS ARM ARCHITECTURES)
+linux-clk@vger.kernel.org (open list:COMMONCLK FRAMEWORK)
+linux-kernel@vger.kernel.org (open list)
+```
+
+##6.2 发送补丁
+-------
+
+接下来, 可以用 `git send-email` 命令发送补丁了 :
+
+```cpp
+git send-email 000* --tokgene@kernel.org,krzk@kernel.org,s.nawrocki@samsung.com,tomasz.figa@gmail.com,cw00.choi@samsung.com,mturquette@baylibre.com,sboyd@codeaurora.org--cc linux-arm-kernel@lists.infradead.org,linux-samsung-soc@vger.kernel.org,linux-clk@vger.kernel.org,linux-kernel@vger.kernel.org
+```
+
+
+注意, 哪些人应当作为邮件接收者, 哪些人应当作为抄送者.
+
+在本例中, 补丁是属于实验性质的, 可以不抄送给邮件列表帐户.
+
+提醒 : 你应当将补丁先发给自己, 检查无误后再发出去. 如果你有朋友在社区有较高的威望, 也可以抄送给他, 必要的时候, 也许他能给你一些帮助. 这有助于将补丁顺利的合入社区.
+
+
+>重要提醒 : 本文讲述的, 主要是实验性质的补丁, 用于打开社区大门. 真正重要的补丁, 可能需要经过反复修改, 才能合入社区. 我知道有一些补丁, 超过两年时间都没能合入社区, 因为总是有需要完善的地方, 也许还涉及一些社区政治:)
+
+
+
+* 本作品/博文 ( [AderStep-紫夜阑珊-青伶巷草 Copyright ©2013-2017](http://blog.csdn.net/gatieme) ), 由 [成坚(gatieme)](http://blog.csdn.net/gatieme) 创作;
+
+* 采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可. 欢迎转载、使用、重新发布, 但务必保留文章署名[成坚gatieme](http://blog.csdn.net/gatieme) ( 包含链接: http://blog.csdn.net/gatieme ), 不得用于商业目的.
+
+* 基于本文修改后的作品务必以相同的许可发布. 如有任何疑问,请与我联系.
\ No newline at end of file
diff --git a/study/patch/submit_patch/git_tag_next.png b/study/patch/submit_patch/git_tag_next.png
new file mode 100644
index 0000000..0cc45f5
Binary files /dev/null and b/study/patch/submit_patch/git_tag_next.png differ
diff --git a/study/patch/submit_patch/kernel_org_linus_git.png b/study/patch/submit_patch/kernel_org_linus_git.png
new file mode 100644
index 0000000..6c24965
Binary files /dev/null and b/study/patch/submit_patch/kernel_org_linus_git.png differ
diff --git a/study/patch/submit_patch/linux-next-git.png b/study/patch/submit_patch/linux-next-git.png
new file mode 100644
index 0000000..f5a6f8a
Binary files /dev/null and b/study/patch/submit_patch/linux-next-git.png differ
diff --git a/study/pattern/container_of/Makefile b/study/pattern/container_of/Makefile
new file mode 100644
index 0000000..177056b
--- /dev/null
+++ b/study/pattern/container_of/Makefile
@@ -0,0 +1,29 @@
+target=null_operation test1 test2 test_container_of
+
+all:$(target)
+
+null_operation : null_operation.o
+ $(CC) $^ -o $@ $(LDFLAGS) -lpthread
+ @echo "NULL指针对象的合法和非法操作..."
+
+test1 : test1.o
+ $(CC) $^ -o $@ $(LDFLAGS) -lpthread
+ @echo "NULL指针结构体对象的合法和非法操作1..."
+
+test2 : test2.o
+ $(CC) $^ -o $@ $(LDFLAGS) -lpthread
+ @echo "NULL指针结构体对象的合法和非法操作2..."
+
+test_container_of : test_container_of.o
+ $(CC) $^ -o $@ $(LDFLAGS) -lpthread
+ @echo "container_of宏函数测试用例..."
+
+%.o : %.c
+ $(CC) -c $^ -o $@ $(CFLAGS) $(DEFINES)
+
+%.o : %.cpp
+ $(CXX) -c $^ -o $@ $(CFLAGS) $(DEFINES)
+
+clean :
+ rm -rf *.o
+ rm -rf $(target)
diff --git a/study/pattern/container_of/READDME.md b/study/pattern/container_of/READDME.md
new file mode 100644
index 0000000..aefa911
--- /dev/null
+++ b/study/pattern/container_of/READDME.md
@@ -0,0 +1,192 @@
+Kernel Container_of详解--通过结构体内部成员地址获取到结构体地址
+=======
+
+| CSDN | GitHub |
+|:----:|:------:|
+| [Kernel Container_of详解--通过结构体内部成员地址获取到结构体地址
+](http://blog.csdn.net/gatieme) | [`LDD-LinuxDeviceDrivers/study/pattern/container_of`](https://github.com/gatieme/LinuxDeviceDrivers/tree/master/study/pattern/container_of) |
+
+
+
+
+本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处
+
+
+
+
+在 `Linux` 内核 `Kernel` 中大量使用指针和结构体数据, 这时候经常需要这样的操作, 我们知道了结构体某个成员的地址信息, 怎么拿到这个结构体的地址呢 ?
+
+
+
+
+
+最直观的, 内核中在设计链表, 树等数据结构的时候为了代码的复用和效率, 设计了一套通用的模板. 将链表和书结构分为设计为 `struct list_head` 和 `struct rb_node`, 然后在数据结构体中包含此通用结构, 比如
+
+```cpp
+typedef struct DataNode
+{
+ Data d;
+ struct list_head ptr;
+}DataNode;
+```
+
+那么这个时候, 遍历或者查询链表或者树, 访问的都是通用数据结构( `struct list_head` 和 `struct rb_node` ), 这时候如果想要得到私有数据(比如 `DataNode` 中的 `Data d`), 就需要先获取到整体结构体的地址.
+
+#1 container_of宏函数
+-------
+
+这是一个频繁的操作, 因此内核中提供了 `container_of` 宏函数来完成这个操作, 其声明形式如下所示 :
+
+```cpp
+/////////////////////
+// container_of
+/////////////////////
+结构体类型 container_type 中包含了一个名为 container_field 的成员字段
+通过该成员的地址获取到整体结构类型 container_type 的地址
+point -=> 已知的成员数据的地址
+container_type -=> 待获取地址的结构体的类型(point为指向其成员member的指针)
+container_field -=> 已知的成员数据的字段名称
+container_of(point, container_type, container_field)
+```
+
+`container_of` 宏的实现参见[未填写链接](), 其主要实现如下所示
+
+```cpp
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+#define container_of(ptr, type, member) \
+({ \
+ const typeof( ((type *)0)->member) *m_ptr = (ptr); \
+ (type *)( (char *)m_ptr - offsetof(type, member) ); \
+})
+```
+
+其中 `offsetof` 宏用于获取结构体类型 `TYPE` 中 `member` 成员(相对于结构体首地址)的地址偏移. 由于没有实际的 `TYPE` 结构体对象, 因为使用 `NULL` 指针计算结构体成员的地址偏移, 由于 `NULL` 结构体的起始地址为 0, 因此直接索引即可得到结构体成员的地址.
+
+#2 关于NULL指针异常的疑惑
+-------
+
+> **注意**, `NULL` 指针虽然被认为是非法访问地址, 但是对其取地址, 进行 `sizeof` 和 `typeof`运算, 计算偏移信息等等都是合法的, 只是不可以被访问(读写).
+
+参见如下代码:
+
+```cpp
+#include
+#include
+
+
+int main(int argc, char *argv[])
+{
+ /////////////////////
+ // 合法操作
+ /////////////////////
+ // 取地址
+ printf("address = %p\n", &(*(int *)0));
+
+ // sizeof 运算
+ printf("sizeof = %ld\n", sizeof(*(int *)0));
+
+ // typeof 运算
+ typeof(*(int *)0) temp1 = 8;
+ printf("temp = %d\n", temp1);
+
+ /////////////////////
+ // 非法操作
+ /////////////////////
+ // 对 NULL 指针进行读
+ typeof(*(int *)0) temp2 = *(int *)0;
+
+ // 对 NULL 指针进行写
+ *(int *)0 = 7;
+ return EXIT_SUCCESS;
+}
+```
+
+#3 container_of宏函数分析
+
+了解了这点, 下面我们接着来分析 `container_of` 宏函数的实现
+
+**已知条件**
+
+* 条件1--已知结构体重成员字段 `container_field` 的地址 `point`
+
+* 条件2--结构体的定义 `container_type`, 其中已知字段的成员变量名为 `container_field`
+
+**求解** 结构体的起始地址
+
+**思路** 根据条件 `2` 可以很清楚的拿到已知字段相对于结构体起始地址的偏移, 再结合条件 `1`, 知道了已知字段的地址减去其相对于起始地址的偏移, 就可以直接拿到结构体的起始地址.
+
+
+首先获取已知字段相对于结构体起始地址的偏移
+
+```cpp
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+```
+
+接着我们就用已知地址减去偏移, 拿到起始地址
+
+```cpp
+#define container_of(ptr, type, member) \
+({ \
+ const typeof( ((type *)0)->member) *m_ptr = (ptr); \
+ (type *)( (char *)m_ptr - offsetof(type, member) ); \
+})
+```
+
+#4 测试程序
+-------
+
+
+```cpp
+#include
+#include
+
+/////////////////////
+// container_of
+/////////////////////
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+#define container_of(ptr, type, member) \
+({ \
+ const typeof( ((type *)0)->member) *m_ptr = (ptr); \
+ (type *)( (char *)m_ptr - offsetof(type, member) ); \
+})
+
+
+typedef struct CTest
+{
+ int m_a;
+ int m_b;
+}CTest;
+
+
+/////////////////////
+// test main
+/////////////////////
+int main(void)
+{
+ CTest cc = {
+ .m_a = 10,
+ .m_b = 20,
+ };
+
+ int *pa = &(cc.m_a);
+ CTest *pc = container_of(pa, CTest, m_a);
+ printf("%p %p\n", &cc, pc);
+ printf("%d %d\n", pc->m_a, pc->m_b);
+
+ return 0;
+}
+```
+
+
+
+
+
+* 本作品/博文 ( [AderStep-紫夜阑珊-青伶巷草 Copyright ©2013-2017](http://blog.csdn.net/gatieme) ), 由 [成坚(gatieme)](http://blog.csdn.net/gatieme) 创作
+
+* 采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可. 欢迎转载、使用、重新发布, 但务必保留文章署名[成坚gatieme](http://blog.csdn.net/gatieme) ( 包含链接: http://blog.csdn.net/gatieme ), 不得用于商业目的
+
+* 基于本文修改后的作品务必以相同的许可发布. 如有任何疑问,请与我联系.
\ No newline at end of file
diff --git a/study/pattern/container_of/null_operation.c b/study/pattern/container_of/null_operation.c
new file mode 100644
index 0000000..c3ea52b
--- /dev/null
+++ b/study/pattern/container_of/null_operation.c
@@ -0,0 +1,38 @@
+/*************************************************************************
+ > File Name: 1.c
+ > Author: GatieMe
+ > Mail: gatieme@163.com
+ > Created Time: Tue 18 Jul 2017 11:09:48 AM CST
+ ************************************************************************/
+
+#include
+#include
+
+
+int main(int argc, char *argv[])
+{
+ /////////////////////
+ // 合法操作
+ /////////////////////
+ // 取地址
+ printf("address = %p\n", &(*(int *)0));
+
+ // sizeof 运算
+ printf("sizeof = %ld\n", sizeof(*(int *)0));
+
+ // typeof 运算
+ typeof(*(int *)0) temp1 = 8;
+ printf("temp = %d\n", temp1);
+
+ /////////////////////
+ // 非法操作
+ /////////////////////
+ // 对 NULL 指针进行读
+ typeof(*(int *)0) temp2 = *(int *)0;
+
+ // 对 NULL 指针进行写
+ *(int *)0 = 7;
+ return EXIT_SUCCESS;
+}
+
+
diff --git a/study/pattern/container_of/test1.c b/study/pattern/container_of/test1.c
new file mode 100644
index 0000000..9b9959c
--- /dev/null
+++ b/study/pattern/container_of/test1.c
@@ -0,0 +1,26 @@
+/*************************************************************************
+ > File Name: test.c
+ > Author: GatieMe
+ > Mail: gatieme@163.com
+ > Created Time: Tue 18 Jul 2017 09:08:52 AM CST
+ ************************************************************************/
+
+#include
+#include
+
+
+struct test
+{
+ int a;
+ int b;
+};
+
+int main(int argc, char *argv[])
+{
+ int *temp = &(((struct test *)0)->b); // -=> OK
+ typeof(((struct test *)0)->b) t1 = 8; // -=> OK
+ int t2 = ((struct test *)0)->b;
+ printf("temp = %d\n", *temp);
+
+ return EXIT_SUCCESS;
+}
diff --git a/study/pattern/container_of/test2.c b/study/pattern/container_of/test2.c
new file mode 100644
index 0000000..16d4a5b
--- /dev/null
+++ b/study/pattern/container_of/test2.c
@@ -0,0 +1,29 @@
+/*************************************************************************
+ > File Name: test2.c
+ > Author: GatieMe
+ > Mail: gatieme@163.com
+ > Created Time: Tue 18 Jul 2017 09:21:55 AM CST
+ ************************************************************************/
+#include
+#include
+
+#define test1(a) \
+do { \
+ typeof(a) temp = 7; \
+}while(0);
+
+int test2(int a)
+{
+ typeof(a) temp = 7;
+}
+
+int main(int argc, char *argv[])
+{
+ printf("%d\n", __LINE__);
+ test1(*(int *)0); // -=> run OK
+ printf("%d\n", __LINE__);
+ test2(*(int *)0); // -=> run error
+ printf("%d\n", __LINE__);
+
+ return EXIT_SUCCESS;
+}
diff --git a/study/pattern/container_of/test_container_of.c b/study/pattern/container_of/test_container_of.c
new file mode 100644
index 0000000..29f0cd9
--- /dev/null
+++ b/study/pattern/container_of/test_container_of.c
@@ -0,0 +1,47 @@
+/*************************************************************************
+ > File Name: test_container_of.c
+ > Author: GatieMe
+ > Mail: gatieme@163.com
+ > Created Time: Tue 18 Jul 2017 09:06:55 AM CST
+ ************************************************************************/
+
+#include
+#include
+
+/////////////////////
+// container_of
+/////////////////////
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+#define container_of(ptr, type, member) \
+({ \
+ const typeof( ((type *)0)->member) *m_ptr = (ptr); \
+ (type *)( (char *)m_ptr - offsetof(type, member) ); \
+})
+
+
+typedef struct CTest
+{
+ int m_a;
+ int m_b;
+}CTest;
+
+
+/////////////////////
+// test main
+/////////////////////
+int main(void)
+{
+ CTest cc = {
+ .m_a = 10,
+ .m_b = 20,
+ };
+
+ int *pa = &(cc.m_a);
+ CTest *pc = container_of(pa, CTest, m_a);
+ printf("%p %p\n", &cc, pc);
+ printf("%d %d\n", pc->m_a, pc->m_b);
+
+ return 0;
+}
diff --git a/study/pattern/container_of/test_container_of.png b/study/pattern/container_of/test_container_of.png
new file mode 100644
index 0000000..ee0ce24
Binary files /dev/null and b/study/pattern/container_of/test_container_of.png differ