http://blog.sina.com.cn/s/blog_6a1837e90100onsb.html 2011
static spinlock_t
RequestResponseLock = SPIN_LOCK_UNLOCKED;
spin_lock_bh(&RequestResponseLock);
//spin_lock_bh 在获取锁之前禁止软件中断, 但是硬件中断留作打开的.
spin_unlock_bh(&RequestResponseLock);
spin_lock(&RequestResponseLock);
spin_unlock(&RequestResponseLock);
//上层调用函数内包含自旋锁时使用spin_lock_bh,而内核层底半步调用时使用spin_lock。bh指bottom_half,即底半。
--------------------------------------------------------------------------------------------------
create_proc_info_entry( REQUEST_RESPONSE_STATUS_NAME, 0,
proc_dir, RequestResponseCacheStat
remove_proc_entry(REQUEST_RESPONSE_STATUS_NAME,
proc_dir);
--------------------------------------------------------------------------------------------------
init_timer(&m->timer);
m->timer.function =
&RequestResponseEntryExpi
m->timer.data = (unsigned
long)m;
mod_timer(&m->timer,
jiffies + GetRadiusResponseTimeout
del_timer(&m->timer);
--------------------------------------------------------------------------------------------------
struct list_head e;
list_add(&m->e,
&RequestResponseCache.hash_list[h].head);
while
(!list_empty(&RequestResponseCache.hash_list[i].head))
m =
list_entry(RequestResponseCache.hash_list[i].head.next, struct
RequestResponseEntry, e);
list_for_each_entry(m,
&RequestResponseCache.hash_list[h].head,
e)
list_del(&m->e);
--------------------------------------------------------------------------------------------------
insmod
/lib/modules/2.6.18-8.el5/kernel/drivers/rtc/rtc-lib.ko
insmod
monitor.ko
mknod /dev/mapdrv0 c
250 0
rmmod
--------------------------------------------------------------------------------------------------
内核编程中:
#include
<ctype.h>
存在错误,而
#include
<linux/ctype.h>
正常
--------------------------------------------------------------------------------------------------
#ifdef __KERNEL__
#include
<linux/ctype.h>
#else
#include
<ctype.h>
#endif
宏__KERNEL__能够区别当前程序是在用户层还是内核层。
上述代码包含在.h中,此头文件可以被用户层和内核层的程序包含。
--------------------------------------------------------------------------------------------------
在strnpcy之后,需在目标字符串的结尾加上'\0',即
strncpy(dest, src,
size);
dest[size] =
'\0'
--------------------------------------------------------------------------------------------------
2的n次方使用(1
<< n)的形式
--------------------------------------------------------------------------------------------------
kmalloc只能申请128K的内存,建议使用vmalloc
vfree()不能放在spin_lock_bh和spin_unlock_bh之间;
--------------------------------------------------------------------------------------------------
module_init()
module_exit()
函数module_init()和module_exit()是模块编程中最基本也是必须的两个函数。
module_init()向内核注册模块所提供的新功能,
而module_exit()注销由模块提供的所有功能。
MODULE_LICENSE("GPL")用于声明模块的许可证
--------------------------------------------------------------------------------------------------
Linux内核模块的编译需要给gcc指示-D__KERNEL__
-DMODULE -DLINUX参数
--------------------------------------------------------------------------------------------------
void *kmalloc(unsigned int len, int
priority);
void kfree(void
*__ptr);
priority: GFP_KERNEL
GFP_ATOMIC
--------------------------------------------------------------------------------------------------
unsigned long copy_from_user(void *to,
const void *from, unsigned long n);
unsigned long
copy_to_user (void * to, void * from, unsigned long
len);
put_user
get_user
--------------------------------------------------------------------------------------------------
内核编程用printk替代printf
内核一共有8个优先级.如果优先级数字比int
console_loglevel变量小的话,消息就会打印到控制台上。如果syslogd和klogd守护进程在运行的话,则不管是否向控制台输出,消息都会被追加进/var/log/messages文件。klogd
只处理内核消息,syslogd 处理其他系统消息,比如应用程序。
--------------------------------------------------------------------------------------------------
include/linux/module.h中定义的宏MODULE_PARM(var,type)
用于向模块传递命令行参数。var为接受参数值的变量名,type为采取如下格式的字符串[min[-max]]{b,h,i,l,s}。min及max用于表示当参数为数组类型时,允许输入的数组元素的个数范围;
b:byte;h:short;i:int;l:long;s:string。
有了MODULE_PARM,在装载内核模块时,用户可以向模块传递一些参数,如:
insmod modname
var=value
--------------------------------------------------------------------------------------------------
#
Makefile2.6
obj-m += hellomod.o
CURRENT_PATH :=
$(shell pwd)
LINUX_KERNEL :=
$(shell uname -r)
LINUX_KERNEL_PATH :=
/usr/src/linux-headers-$(LINUX_KERNEL)
#Linux内核源代码的绝对路径
all:
make -C
$(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C
$(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
有了Makefile,执行make命令,会自动形成相关的后缀为.o和.ko文件。
--------------------------------------------------------------------------------------------------
模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。
模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
因为内核版本的每次变化,其中的某些函数名也会相应地发生变化,因此模块编程与内核版本密切相关
内置模块:可加载模块
--------------------------------------------------------------------------------------------------
这些变量和函数就统称为符号。
其中宏定义EXPORT_SYMBOL()本身的含义是“移出符号”。为什么说是“移出”呢?因为这些符号本来是内核内部的符号,通过这个宏放在一个公开的地方,使得装入到内核中的其他模块可以引用它们。
在模块编程中,可以根据符号名从这个文件中检索出其对应的地址,然后直接访问该地址从而获得内核数据。
第三列“所属模块”指符号所在的模块名,对于从内核这一母模块移出的符号,这一列为空。
模块加载后,2.4内核下可通过
/proc/ksyms、 2.6 内核下可通过/proc/kallsyms查看模块输出的内核符号
--------------------------------------------------------------------------------------------------
模块依赖
为了确保模块安全地卸载,每个模块都有一个引用计数器
--------------------------------------------------------------------------------------------------
1.Insmod命令:
2.
rmmod命令:
3.lsmod命令:读取/proc文件系统中的文件/proc/modules中的信息
4.ksyms命令:读取/proc文件系统中的文件/proc/kallsyms。
--------------------------------------------------------------------------------------------------
//MODULE_PARM_DESC(interface,”A network
interface”);
molule_parm(interface,charp,0644) //2.6内核中的宏
//MODULE_PARM_DESC(irq,”The IRQ of the network
interface”);
module_param(irq,int,0644);
insmod myirq.ko
interface=eth0 irq=9
if
(request_irq(irq, &myinterrupt, SA_SHIRQ,interface,
&irq)) //注册中断,中断值为irq,中断函数myinterrupt
free_irq(irq,
&irq);
具体网卡 irq的值可以查看
cat /proc/interrupts
可动态更改
--------------------------------------------------------------------------------------------------
insmod(安装 LKM),
rmmod (删除
LKM),
modprobe(insmod
和 rmmod
的包装器),加载当前当前模块与其相关联的其他模块,单一模块无关联时,必须使用insmod,否则会报错,当自写编写模块时,建议不要使用。
depmod(用于创建模块依赖项),
modinfo(用于为模块宏查找值)。
LKM
只不过是一个特殊的可执行可链接格式(Executable and Linkable
Format,ELF)对象文件。
在模块的加载和卸载期间,模块子系统维护了一组简单的状态变量,用于表示模块的操作。
--------------------------------------------------------------------------------------------------
内核中有一个叫做 HZ
的频率变量,它表示每秒的时钟节拍数。一般的,在某种平台上它会有一个固定值,这个固定值是人为设定的,
也就是可编程的(对系统定时器编程)。设定 HZ
的大小需要权衡。这个值设大了,带来的好处是定时器间隔变小,
从而使进程(任务)的调度的精确性得以提高,但带来的缺点是导致开销过大,让系统变得耗电,
这样在一些经常使用电池的设备来说(比如笔记本,平板电脑)是难以接受的。
在现在一般的 x86
平台,2.6 内核的 linux 下,这个值会被设为 100 。也就是说,一个时钟节拍为 1/100 = 0.01s = 10ms
。
一个时钟节拍也称为 1 个
jiffy 。
内核中还有一个重要的变量叫
jiffies 。它记录了系统从启动到当前所触发定时器的次数。jiffies 每秒钟增加 HZ 个计数,
实际上就是 N 个 jiffy
。
--------------------------------------------------------------------------------------------------
中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。
下半部运行时是允许中断请求的,而上半部运行时是关中断的,这是二者之间的主要区别。
--------------------------------------------------------------------------------------------------
小任务(Tasklet)机制
Count域是小任务的引用计数器。如果它不为0,则小任务被禁止,不允许执行;只有当它为零,小任务才被激活,并且在被设置为挂起时,小任务才能够执行。
DECLARE_TASKLET(name, func, data)
DECLARE_TASKLET_DISABLED(name, func, data)
DECLARE_TASKLET(my_tasklet, my_tasklet_handler,
dev);
这行代码其实等价于
struct
tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0),
tasklet_handler, dev};
static void
tasklet_handler (unsigned long data)
tasklet_init(&my_tasklet,
tasklet_handler, 0);
tasklet_schedule(&my_tasklet);
tasklet_kill(&my_tasklet);
--------------------------------------------------------------------------------------------------
如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就选择tasklet。
另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。
void
work_handler(void *data); //工作队列待执行的函数
DECLARE_WORK(name, void (*func) (void *), void *data);
//这样就会静态地创建一个名为name,待执行函数为func,参数为data的work_struct结构。
INIT_WORK(struct work_struct *work, woid(*func) (void *),
void *data); //这会动态地初始化一个由work指向的工作。
queue =
create_singlethread_workqueue(“helloworld”);
if
(!queue)
goto
err;
destroy_workqueue(queue);
schedule_work(&work);//把给定工作的待处理函数提交给缺省的events工作线程
schedule_delayed_work(&work, delay);
//&work指向的work_struct直到delay指定的时钟节拍用完以后才会执行。
--------------------------------------------------------------------------------------------------
|
|
来自: 心不留意外尘 > 《task sys》