PROC虚拟文件系统自制kmsg文件存储打印信息
像内核proc文件系统的dmsg命令一样,prink内核打印的信息都会存储在/proc/kmsg文件里,我们cat它就能读出所有的内核打印信息,但是该文件类似于管道,读完了就读走了,不会再有,除非有printk函数继续向里面写入东西。而dmsg会打印出所有log_buf里面的东西,应该是开机以来所有的printk打印出来的。
在这里先说明下我的软硬件平台:
硬件: 友善之臂的mini2440,淘宝里面嵌入式家园那里买的(绝对不是打广告,而是详细说明自己的板子)
软件:linux-2.6.32.2
好,现在进入我们的话题:
在我们进行驱动调试的时候,我不想用printk函数,因为这样会和其他内核打印出来的信息混在一块。所以我希望在建立自己的/proc/mymsg文件,使用自己的打印函数myprintk,需要查看我打印出来的驱动调试信息时只要简单的cat /proc/mymsg,里面全是myprintk打印出来的东西。
1. 写mymsg的驱动
贴出源码:
Ubuntu_myproc_dmsg.c |
#include #include #include #include #include #include #include #include
#define UBUNTU_LOG_BUF_LENGTH 1024 //»·Ðλº³åÇø³¤¶È
static DECLARE_WAIT_QUEUE_HEAD(ubuntu_log_waitq);
static char ubuntu_log_buf[UBUNTU_LOG_BUF_LENGTH]; //»·Ðλº³åÇøÊý×é static char tmp_buf[UBUNTU_LOG_BUF_LENGTH]; //ÔÝ´æ static int ubuntu_log_rp=0; //»·Ðλº³åÇø¶ÁÖ¸Õë, ²»±ä static int ubuntu_log_wp = 0; //»·Ðλº³åÇøдָÕë static int ubuntu_log_rp_for_read = 0; //»·Ðλº³åÇø¶ÁÖ¸Õ룬ΪÁ˶ÁÈ¡Êý¾Ý£¬¿É±ä
/*Åжϻ·Ðλº³åÇøÊÇ·ñÂú*/ static int ubuntu_Is_log_full(void) { return ((ubuntu_log_wp + 1) % UBUNTU_LOG_BUF_LENGTH == ubuntu_log_rp); }
/*Åжϻ·Ðλº³åÇøÊÇ·ñ¿Õ*/ static int ubuntu_Is_log_empty(void) { return (ubuntu_log_rp == ubuntu_log_wp); }
static int ubuntu_Is_log_empty_for_read(void) { return (ubuntu_log_rp_for_read == ubuntu_log_wp); }
/* Ïò»·Ðλº³åÇøÄÚдÈëÒ»¸ö×Ö·ûÊý¾Ý */ static void ubuntu_log_putchar(char c) { if(ubuntu_Is_log_full()) //Èç¹ûÂúÁË { /*¶ªÆúÒ»¸öÊý¾Ý*/ ubuntu_log_rp += 1;
if ((ubuntu_log_rp_for_read + 1) % UBUNTU_LOG_BUF_LENGTH == ubuntu_log_rp) { ubuntu_log_rp_for_read = ubuntu_log_rp; }
}
ubuntu_log_buf[ubuntu_log_wp] = c; ubuntu_log_wp = (ubuntu_log_wp + 1) % UBUNTU_LOG_BUF_LENGTH;
}
/*½«¸ñʽ»¯µÄÊý¾ÝдÈëÔݴ滺³åÇøÖÐ ×¢Òâ: ÓÉÓÚÕâ¸öº¯ÊýÒª±»ÆäËûÎļþËùÓà ËùÒÔÆäÇ°Ãæ²»ÄܼÓstatic ²Î¿¼: sprintf º¯Êý */
static int myprintk(const char* fmt , ...) { va_list args; int i; int j;
/* ·ÅÈëÔÝ´æ */ va_start(args, fmt); i = vsnprintf(tmp_buf, INT_MAX, fmt, args); va_end(args);
/* ÔÙ·ÅÈë»·Ðλº³åÇø */ for(j = 0 ; j < i ; j++) { ubuntu_log_putchar( tmp_buf[j] ); }
return i; }
/* µ¼³ö¸Ãº¯Êý */ EXPORT_SYMBOL(myprintk);
/* ´Ó»·Ðλº³åÇø¶Á³öÒ»¸öÊý¾Ý */ static int ubuntu_log_getchar(char *p) { if( ubuntu_Is_log_empty( )) //Èç¹ûÊÇ¿Õ£¬·µ»Ø¶Áȡʧ°Ü { return 0; } *p = ubuntu_log_buf[ubuntu_log_rp]; ubuntu_log_rp = (ubuntu_log_rp + 1) % UBUNTU_LOG_BUF_LENGTH; return 1; } static int ubuntu_log_getchar_for_read(char *p) { if( ubuntu_Is_log_empty_for_read( )) //Èç¹ûÊÇ¿Õ£¬·µ»Ø¶Áȡʧ°Ü { return 0; } *p = ubuntu_log_buf[ubuntu_log_rp_for_read]; ubuntu_log_rp_for_read = (ubuntu_log_rp_for_read + 1) % UBUNTU_LOG_BUF_LENGTH; return 1; }
static ssize_t ubuntu_kmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int error = 0; int i = 0; char c;
if((file->f_flags & O_NONBLOCK) && ubuntu_Is_log_empty_for_read()) //Èç¹û·Ç×èÈû´ò¿ª²¢ÇÒ»·Ðλº³åÇøΪ¿Õ return -EAGAIN;
error = wait_event_interruptible(ubuntu_log_waitq, !ubuntu_Is_log_empty_for_read()); //¿ÕÔò×èÈû
/* ¿½±´µ½Óû§¿Õ¼ä */ while( !error && (ubuntu_log_getchar_for_read(&c)) && (i < count)) { error = __put_user(c , buf); buf++; i++; }
if(!error) error = i;
return error; }
static int ubuntu_kmsg_open(struct inode * inode, struct file * file) { ubuntu_log_rp_for_read = ubuntu_log_rp; return 0; }
static const struct file_operations ubuntu_proc_kmsg_operations = { .read = ubuntu_kmsg_read, .open = ubuntu_kmsg_open, };
static int __init ubuntu_mymsg_init(void) {
proc_create("mymsg", S_IRUSR, NULL, &ubuntu_proc_kmsg_operations); return 0; }
static void __exit ubuntu_mymsg_exit(void) { remove_proc_entry("mymsg", NULL ); }
#define DRIVER_VERSION "v1.0" #define DRIVER_AUTHOR "Guoyu #define DRIVER_DESC "MINI2440 FOR simple proc dmsg driver"
module_init(ubuntu_mymsg_init); module_exit(ubuntu_mymsg_exit);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); |
2. 写测试程序的驱动
贴出源码
First_drv.c |
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
//Íⲿº¯Êý,ÓÐûÓаüº¬¸ÃÍ·Îļþ£¬Ö»ÄÜÓÃextern extern int myprintk(const char *fmt, ...);
static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev;
volatile unsigned long *gpbcon = NULL; volatile unsigned long *gpbdat = NULL;
//#define DBG_PRINTK printk //#define DBG_PRINTK(x...) //È¥µô´òÓ¡Óï¾ä
static int first_drv_open(struct inode *inode, struct file *file) { static int cnt = 0; myprintk("first_drv_open : %d\n", ++cnt); //printk("first_drv_open\n"); /* * LED1,LED2,LED4¶ÔÓ¦GPB5¡¢GPB6¡¢GPB7¡¢GPB8 */ /* ÅäÖÃGPB5,6,7,8ΪÊä³ö */
*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
return 0; }
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; static int cnt = 0; myprintk("first_drv_write : %d\n", ++cnt); //printk("first_drv_write\n");
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1) { // µãµÆ *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8)); } else { // ÃðµÆ *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8); }
return 0; }
static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* ÕâÊÇÒ»¸öºê£¬ÍÆÏò±àÒëÄ£¿éʱ×Ô¶¯´´½¨µÄ__this_module±äÁ¿ */ .open = first_drv_open, .write = first_drv_write, };
int major; static int first_drv_init(void) { myprintk("first driver init! \n");
major = register_chrdev(0, "first_drv", &first_drv_fops); // ×¢²á, ¸æËßÄÚºË
firstdrv_class = class_create(THIS_MODULE, "firstdrv"); //ΪÁËʹÓÃudev×Ô¶¯´´½¨É豸Îļþ
firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16); gpbdat = gpbcon + 1;
return 0; }
static void first_drv_exit(void) { myprintk("first driver exit! \n");
unregister_chrdev(major, "first_drv"); // жÔØ
device_destroy(firstdrv_class, MKDEV(major, 0)); class_destroy(firstdrv_class); iounmap(gpbcon); }
module_init(first_drv_init); module_exit(first_drv_exit);
MODULE_LICENSE("GPL"); |
再贴出测试要用的应用程序:
Firstdrvtest.c |
#include #include #include #include
/* firstdrvtest on * firstdrvtest off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/xyz", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("Usage :\n"); printf("%s return 0; }
if (strcmp(argv[1], "on") == 0) { val = 1; } else { val = 0; }
write(fd, &val, 4); return 0; } |
3. 编译要注意的地方
由于在ubuntu_myproc_dmsg.c文件中EXPORT_SYMBOL(myprontk),将该符号导出了,以让其他模块能用,所以,编译的时候有三种方法,我尝试了前两种成功了。
(1) 将ubuntu_myproc_dmsg.c和 first_drv.c放在同一个文件夹下,先编译ubuntu_myproc_dmsg.ko再编译first_drv.ko
(2) 将两个文件放在不同的文件夹下,先编译ubuntu_myproc_dmsg.ko,再把其中的Module.symvers文件拷贝到first_drv.c所在的文件夹中,再编译first_drv.ko
(3) 在first_drv.ko的Makefile中加入以下语句:
KBUILD_EXTRA_SYMBOLS+= /path/to/Module A/Module.symvers
export KBUILD_EXTRA_SYMBOLS
如:
这个方法没试,但是原理应该是一样的。
4. 测试结果:
[root@FriendlyARM 3th]#
[root@FriendlyARM 3th]#
[root@FriendlyARM 3th]#
[root@FriendlyARM 3th]#
[root@FriendlyARM 3th]# insmod ubuntu_myproc_dmsg.ko
[root@FriendlyARM 3th]# insmod first_drv.ko
[root@FriendlyARM 3th]# cat /proc/mymsg
first driver init!
^C
[root@FriendlyARM 3th]# ./firstdrvtest on
[root@FriendlyARM 3th]# cat /proc/mymsg
first driver init!
first_drv_open : 1
first_drv_write : 1
^C
[root@FriendlyARM 3th]# ./firstdrvtest off
[root@FriendlyARM 3th]# cat /proc/mymsg
first driver init!
first_drv_open : 1
first_drv_write : 1
first_drv_open : 2
first_drv_write : 2
^C
[root@FriendlyARM 3th]#
5. 参考帖子
http://bbs.chinaunix.net/thread-1918814-1-1.html
http://bbs.chinaunix.net/thread-1919530-1-1.html
http://hi.baidu.com/tracyangrad/blog/item/f668530a7d3785b50a7b82d9.html