Sholck

不积跬步,无以至千里.不积小流,无以成江海

0%

x86下系统调用

背景

经过之前的汇编实践-解析命令行参数学习,发现在进行系统调用时,不管是调用syscall(), 还是libc封装的系统调用,最终都是通过int $0x80(32位 i386)或者syscall(x86_64)进入内核态从而调用对应的底层实现,x86_32是通过一个0x80的软中断实现的,而x86_64是通过汇编指令syscall实现。

疑问:

  1. 0x80的软中断策略如何实现系统调用
  2. syscall汇编指令具体做了什么?(64bit)
阅读全文 »

模块接口分析

模块编译

在代码编译前宏展开时,需要进行条件编译,这需要gcc指定参数,而指定什么参数由Makefile来控制.
trace.c在编译时gcc中的参数中会带有 -D__KERNEL__, -DMODULE, -D__KBUILD_MODNAME=kmod_trace, 可以参考linux-likely学习,这用在之后的宏展开。

阅读全文 »

linux-通知链学习

实现

通知链notifier的实现为:A模块实现维护一个具备高优先级顺序的单向节点链表notifier,每一个节点有对应的函数指针和优先级。内核中的其他子系统模块B,C 如果对A中的事件感兴趣,那么需要向notifier上挂在一个子节点,并在此节点上注册回调。当事件发生时,遍历notifier上所有节点,并通过回调通知模块B,C,然后根据事件进行对应的处理。

阅读全文 »

gdb调试linux内核&驱动模块

在linux学习过程中,需要针对内核和动态模块进行调试,最常用的方法是gdb,但是linux gdb文档中描述的不够详细,在学习调试过程中碰到了不少坑并做如下记录。

阅读全文 »

linux-likely学习

背景

在日常的工作和学习中,经常发现likely和unlikely的使用,在通知链节点注册时,有如下函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int notifier_chain_register(struct notifier_block **nl,                                                                                                                                                      
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (unlikely((*nl) == n)) {
WARN(1, "notifier callback %ps already registered",
n->notifier_call);
return -EEXIST;
}
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}

这里通过遍历节点和插入节点地址对比,防止程序运行时重复插入通知链节点。当然,重复插入这种情况下是非常少见的,因此编译器针对此进行了性能优化。

阅读全文 »

动态模块加载异常

背景

动态模块加载,出现以下异常

1
disagrees about version of symbol module_layout"
阅读全文 »

linux内核开发学习手册-内核许可证

原文见:内核开发文档-Linux kernel licensing rules(Linux内核许可规则)

内核通用许可证

  • linux内核虽然是开源的,但是为了避免开发者的心血被不良厂商赚取利益等和保护开源环境,因此是有基于GPL-2.0() 的通用公共许可证作为法律保护的,具体的描述见COPYING文件。
  • 所有的内核源文件都需要标志许可证,而且这个许可证是可以与GPL-2.0兼容的,但是UAPI是作为例外的,因为是非内核的软件如果与内核通信,需要包含此头文件,但是我们不能要求该通信软件也受此许可证限制,因此此类特殊的例外许可描述也会被记录。
阅读全文 »

内核开发学习手册-内核驱动接口

针对内核,无论是开发还是用户,我们所希望的是稳定的驱动功能,而稳定的驱动和有稳定的内核接口之间的联系,需要我们去思考,有助于理解linux的开发哲学。

下文解释了:

  1. 内核没有二进制内核接口
  2. 内核的源代码接口不稳定
    阅读全文 »