Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4842902
  • 博文数量: 930
  • 博客积分: 12070
  • 博客等级: 上将
  • 技术积分: 11448
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-15 16:57
文章分类

全部博文(930)

文章存档

2011年(60)

2010年(220)

2009年(371)

2008年(279)

分类: C/C++

2008-11-14 19:35:53

# vi hello.c
---------------------------------------
#include // Needed by all modules
#include
// Needed for KERN_ALERT
#include     // HZ

int init_module(void)
{
    printk("<1>Hello world 1.\n");
    printk("<1>HZ = %d\n", HZ);
    return 0;
}

void cleanup_module(void)
{
    printk(KERN_ALERT "Goodbye world 1.\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zengxiaolong ");
MODULE_DESCRIPTION("A sample driver");
MODULE_SUPPORTED_DEVICE("testdevice");



---------------
#include //Needed by all modules
#include //Needed for KERN_ALERT

#include

static int __init hello_init(void)
{
    printk("<1>Hello world 1.\n");
    printk("<1>HZ = %d\n", HZ);
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_ALERT "Goodbye world 1.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zengxiaolong ");
MODULE_DESCRIPTION("A sample driver");
MODULE_SUPPORTED_DEVICE("testdevice");


vi Makefile
--------------------------------
KERNEL_LOCATION := /home/zxl/soft/kernel/linux-2.6.22
obj-m += hello.o

all:
    make -C $(KERNEL_LOCATION) M=`pwd` modules
clean:
    make -C $(KERNEL_LOCATION) M=`pwd` clean



--------------------------------
# make
# insmod hello.ko
Hello world 1.
# modinfo hello.ko ---- 查看模块基本信息
# lsmod ---- 查看已插入模块
# cat /proc/modules ---- 已经被加载的内核模块都罗列此文件
# rmmod hello
Goodbye world 1.





printk():
--------------------------------------------
    每个printk()声明都会带一个优先级,比如<1>和
KERN_ALERT。如果你不指明优先级,默认的优先级DEFAULT_MESSAGE_LOGLEVEL "<4>"将被采用。
#define KERN_EMERG   "<0>" //用于紧急消息, 常常是那些崩溃前的消息
#define KERN_ALERT   "<1>" //需要立刻动作的情形
#define KERN_CRIT    "<2>" //严重情况,常常与严重的硬件或者软件失效有关
#define KERN_ERR     "<3>" //用来报告错误情况; 设备驱动常常使用KERN_ERR来报告硬件故障
#define KERN_WARNING "<4>" //有问题的情况的警告,这些情况自己不会引起系统的严重问题
#define KERN_NOTICE -"<5>" //正常情况, 但是仍然值得注意. 在这个级别一些安全相关的情况会报告
#define KERN_INFO    "<6>" //信息型消息. 在这个级别, 很多驱动在启动时打印它们发现的硬件的信息.
#define KERN_DEBUG   "<7>" //用作调试消息

    每个字串(在宏定义扩展里)代表一个在角括号中的整数. 整数的范围从0到7, 越小的数表示越大的优先级.
printk()中的优先级--小于console_loglevel(数值上小于),信息将直接打印在你的终端上。
    变量console_loglevel初始化成DEFAULT_CONSOLE_LOGLEVEL, 并且可通过sys_syslog系统调用修改.也可以通过文本文件/proc/sys/kernel/printk读写控制台记录级别. 这个文件有4个整型值:
    当前级别 (console_loglevel的值)
    适用没有明确级别的消息的缺省级别
    允许的最小级别
    启动时缺省级别.

    写一个数值到这个文件就改变"当前级别"成这个值:
    # echo 3 > /proc/sys/kernel/printk


注: (1)
console_loglevel的初始化:

#define console_loglevel (console_printk[0])
int console_printk[4] = {
    DEFAULT_CONSOLE_LOGLEVEL,
    DEFAULT_MESSAGE_LOGLEVEL,
    MINIMUM_CONSOLE_LOGLEVEL,
    DEFAULT_CONSOLE_LOGLEVEL,
};
#define DEFAULT_CONSOLE_LOGLEVEL 7
#define DEFAULT_MESSAGE_LOGLEVEL 4
#define MINIMUM_CONSOLE_LOGLEVEL 1

注: (2)
若是在图形界面下注册模块,看不到printk输出的信息。可以用命令dmesg来查看输出。




---------------------------------
    内核模块要么从函数init_module 或是用宏module_init指定的函数调用开始。这就是内核模块的入口函数。它告诉内核模块提供那些功能扩展并且让内核准备好在需要时调用它。当它完成这些后,该函数就执行结束了。模块在被内核调用前也什么都不做。所有的模块或是调用cleanup_module或是用宏module_exit指定的函数。这是模块的退出函数。它撤消入口函数所做的一切。 例如注销入口函数所注册的功能。

    程序员并不总是自己写所有用到的函数。一个常见的基本的例子就是 printf()——libc提供的库函数(C标准库)。你开发的程序中用到的这些库函数(像printf())实际上在连接之前并不进入你的程序。在连接时<这些函数调用>才会指向你调用的库,从而使你的代码最终可以执行。
    Programmers use functions they don't define all the time. A prime example of this is printf(). You use these library functions which are provided by the standard C library, libc. The definitions for these functions don't actually enter your program until the linking stage, which insures that the code (for printf() for example) is available, and fixes the call instruction to point to that code.

    内核模块有所不同。在hello world模块中你也许已经注意到了我们使用的函数 printk() 却没有包含标准I/O库。这是因为模块是在insmod加载时才连接的目标文件。那些要用到的函数的符号链接是内核自己提供的。 也就是说,你可以在内核模块中使用的函数只能来自内核本身。如果你对内核提供了哪些函数符号链接感兴趣,看一看文件/proc/kallsyms。

    需要注意的一点是库函数和系统调用的区别。库函数是高层的,完全运行在用户空间,是为程序员提供调用真正的在幕后完成实际工作的系统调用的更方便的接口。 系统调用在内核态运行并且由内核自己提供。标准C库函数printf()可以被看做是一 个通用的输出语句,但它实际做的是将数据转化为符合格式的字符串并且调用系统调用write()输出这些字符串。

    是否想看一看printf()究竟使用了哪些系统调用? 这很容易,编译下面的代码。
#include
int main(void)
{
    printf("hello");
    return 0;
}

    使用命令gcc -Wall -o hello hello.c编译。用命令 strace hello行该可执行文件。是否很惊讶? 每一行都和一个系统调用相对应。 strace是一个非常有用的程序,它可以告诉你程序使用了哪些系统调用和这些系统调用的参数,返回值。 这是一个极有价值的查看程序在干什么的工具。在输出的末尾,你应该看到这样类似的一行 write(1, "hello", 5hello)。这就是我们要找的。藏在面具printf() 的真实面目。
阅读(1214) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~