对于做嵌入式或则熟悉linux内核的人来说linux 驱动串口,对printk这个函数一定不会倍感陌生。printk相当于printf的双胞姊妹,他们一个运行在用户态,另一个则在内核态被人们所熟知。
printk是在内核中运行的向控制台输出显示的函数,Linux内核首先在内核空间分配一个静态缓冲区,作为显示用的空间,之后调用sprintf,低格显示字符串,最后调用tty_write向终端进行信息的显示。
printk与printf的差别,是哪些造成一个运行在内核态而另一个运行用户态?虽然这两个函数的几乎是相同的,出现这些差别是由于tty_write函数须要使用fs指向的被显示的字符串,而fs是专门用于储存用户态段选择符的,为此,在内核态时linux应用程序,为了配合tty_write函数,printk会把fs更改为内核态数据段选择符ds中的值,这样就能正确指向内核的数据缓冲区,其实这个操作会先对fs进行压栈保存,调用tty_write完毕后再出栈恢复。总结说来,printk与printf的差别是由fs引起的,所以差别也是围绕对fs的处理。
【原型】
intprintk(constchar*fmt,…);
【示例】
与大多数展示printf的功能一样,我们也用一个helloworld的程序来演示printk的输出:
编撰一个内核模块:
#include
#include
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include
#endif
MODULE_LICENSE("GPL");
int init_module()
{
printk("hello.word-this is the kernel speakingn");
return 0;
}
void cleanup_module()
{
printk("Short is the life of a kernel modulen");
}
保存为文件hello.c
编撰一个Makefile:
CC=gcc
MODCFLAGS:=-O6 -Wall -DMODULE -D__KERNEL__ -DLINUX
hello.o:hello.c /usr/include/linux/version.h
$(CC) $(MODCFLAGS) -c hello.c
echo insmod hello.o to turn it on
保存为文件Makefile
执行make
我们可以看见生成了一个hello.o的内核模块,我们想通过这个模块在插入内核的时侯输出
"hello.word-thisisthekernelspeaking
这样一条信息。
之后我们开始:
[root@localhostroot]#insmodhello.o
[root@localhostroot]#
并没有输出任何消息。why?
这也是printf和printk的一个不同的地方
用printk,内核会依照日志级别,可能把消息复印到当前控制台上,这个控制台一般是一个字符模式的终端、一个并口复印机或是一个串口复印机。那些消息正常输出的前提是──日志输出级别大于console_loglevel(在内核中数字越小优先级越高)。
没有指定日志级别的printk句子默认采用的级别是DEFAULT_MESSAGE_LOGLEVEL(这个默认级别通常为,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到
日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):
#defineKERN_EMERG0/*紧急风波消息,系统崩溃之前提示,表示系统不可用*/
#defineKERN_ALERT1/*报告消息,表示必须立刻采取举措*/
#defineKERN_CRIT2/*临界条件,一般涉及严重的硬件或软件操作失败*/
#defineKERN_ERR3/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/
#defineKERN_WARNING4/*警告条件,对可能出现问题的情况进行警告*/
#defineKERN_NOTICE5/*正常但又重要的条件,用于提醒*/
#defineKERN_INFO6/*提示信息,如驱动程序启动时,复印硬件信息*/
#defineKERN_DEBUG7/*调试级别的消息*/
如今我们来更改hello.c程序,使printk的输出级别为最高:
printk("""hello.word-thisisthekernelspeakingn");
之后重新编译hello.o,并插入内核:
[root@localhostroot]#insmodhello.o
[root@localhostroot]#
Messagefromsyslogd@localhostatSatAug1505:32:222009...
localhostkernel:hello.word-thisisthekernelspeaking
hello,world信息出现了。
虽然printk仍然是能输出信息的红帽子linux,只不过不一定是到了终端上。我们可以去
/var/log/messages这个文件上面去查看。
假如klogd没有运行,消息不会传递到用户空间,只能查看/proc/kmsg
通过读写/proc/sys/kernel/printk文件可读取和更改控制台的日志级别。查看这个文件的方式如下:
#cat/proc/sys/kernel/printk6417
里面显示的4个数据分别对应控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别。
可用下边的命令设置当前日志级别:
#echo8>/proc/sys/kernel/printk
这样所有级别假如变量类型是,使用prink的格式说明符:
int%d或则%x(注:%d是十补码,%x是十六补码)
unsignedint%u或则%x
long%ld或则%lx
unsignedlong%lu或则%lx
longlong%lld或则%llx
unsignedlonglong%llu或则%llx
size_t%zu或则%zx
ssize_t%zd或则%zx
原始表针值必须用%p输出。
u64,即(unsignedlonglogn),必须用%llu或则%llx输出,如:
printk("%llu",(unsignedlonglong)u64_var);
s64,即(longlong)linux 驱动串口,必须用%lld或则%llx输出,如:
printk("%lld",(longlong)s64_var);
假如(变量类型)的宽度依赖一个配置选项(比如:sector_t,blkcnt_t,phys_addr_t,resource_size_t)或则依赖相关的体系结构(比如:tcflag_t),使用一个可能最大类型的格式说明符,但是显示转换它。如:
printk("test:sectornumber/totalblocks:%llu/%llun",(unsignedlonglong)sector,(unsignedlonglong)blockcount);