Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1044304
  • 博文数量: 836
  • 博客积分: 43880
  • 博客等级: 大将
  • 技术积分: 5485
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-20 17:36
文章分类

全部博文(836)

文章存档

2011年(1)

2008年(835)

我的朋友

分类: LINUX

2008-08-20 18:28:03


内核版本:2.4.22 阅读此文的目的:学会编写Linux设备驱动。 阅读此文的方法:阅读以下2个文件:hello.c,asdf.c。 此文假设读者: 已经能用C语言编写Linux应用程序, 理解"字符设备文件,块设备文件,主设备号,次设备号", 会写简单的Shell脚本和Makefile。 1."hello.c" -------------------------------- /* *这是我们的第一个源文件, *它是一个可以加载的内核模块, *加载时显示"Hello,World!", *卸载时显示"Bye!"。 *需要说明一点,写内核或内核模块不能用写应用程序时的系统调用或函数库, *因为我们写的就是为应用程序提供系统调用的代码。 *内核有专用的函数库,如,,等, *现在还没必要了解得很详细, *这里用到的printk的功能类似于printf。 *"/usr/src/linux"是你实际的内核源码目录的一个符号链接, *如果没有现在就创建一个,因为下面和以后都会用到。 *编译它用"gcc-c-I/usr/src/linux/includehello.c", *如果正常会生成文件hello.o, *加载它用"insmodhello.o", *只有在文本终端下才能看到输出。 *卸载它用"rmmodhello" */ /* *小技巧:在用户目录的.bashrc里加上一行: *aliasmkmod=''gcc-c-I/usr/src/linux/include'' *然后重新登陆Shell, *以后就可以用"mkmodhello.c"的方式来编译内核模块了。 */ /*开始例行公事*/ #ifndef__KERNEL__ #define__KERNEL__ #endif #ifndefMODULE #defineMODULE #endif #include #include MODULE_LICENSE("GPL"); #ifdefCONFIG_SMP #define__SMP__ #endif /*结束例行公事*/ #include/*printk()在这个文件里*/ staticint init_module (){ printk("Hello,World!\n"); return0;/*如果初始工作失败,就返回非0*/ } staticvoid cleanup_module (){ printk("Bye!\n"); } ------------------------------------ 2."asdf.c" ------------------------------------ /* *这个文件是一个内核模块。 *内核模块的编译,加载和卸载在前面已经介绍了。 *这个模块的功能是,创建一个字符设备。 *这个设备是一块4096字节的共享内存。 *内核分配的主设备号会在加载模块时显示。 */ /*开始例行公事*/ #ifndef__KERNEL__ #define__KERNEL__ #endif #ifndefMODULE #defineMODULE #endif #include #include #ifdefCONFIG_SMP #define__SMP__ #endif MODULE_LICENSE("GPL"); /*结束例行公事*/ #include/*copy_to_user(),copy_from_user*/ #include/*structfile_operations,register_chrdev(),...*/ #include/*printk()在这个文件里*/ #include/*和任务调度有关*/ #include/*u8,u16,u32...*/ /* *关于内核功能库,可以去网上搜索详细资料, */ /*文件被操作时的回调功能*/ staticintasdf_open(structinode*inode,structfile*filp); staticintasdf_release(structinode*inode,structfile*filp); staticssize_tasdf_read(structfile*filp,char*buf,size_tcount,loff_t*f_pos); staticssize_tasdf_write(structfile*filp,constchar*buf,size_tcount,loff_t*f_pos); staticloff_tasdf_lseek(structfile*file,loff_toffset,intorig); /*申请主设备号时用的结构,在linux/fs.h里定义*/ structfile_operationsasdf_fops={ open:asdf_open, release:asdf_release, read:asdf_read, write:asdf_write, llseek:asdf_lseek, }; staticintasdf_major;/*用来保存申请到的主设备号*/ staticu8asdf_body[4096]="asdf_body\n";/*设备*/ staticint init_module (){ printk("Hi,This''ASimpleDeviceFile!\n"); asdf_major=register_chrdev(0,"ASimpleDeviceFile",&asdf_fops);/*申请字符设备的主设备号*/ if(asdf_major<0)returnasdf_major;/*申请失败就直接返回错误编号*/ printk("Themajoris:%d\n",asdf_major);/*显示申请到的主设备号*/ return0;/*模块正常初始化*/ } staticvoid cleanup_module (){ unregister_chrdev(asdf_major,"ASimpleDeviceFile");/*注销以后,设备就不存在了*/ printk("ASimpleDevicehasbeenremoved,Bye!\n"); } /* *编译这个模块然后加载它, *如果正常,会显示你的设备的主设备号。 *现在你的设备就建立好了,我们可以测试一下。 *假设你的模块申请到的主设备号是254, *运行"mknodabcc2540",就建立了我们的设备文件abc。 *可以把它当成一个4096字节的内存块来测试一下, *比如"catabc","cpabcimage","cpimageabc", *或写几个应用程序用它来进行通讯。 *介绍一下两个需要注意的事, *一是printk()的显示只有在非图形模式的终端下才能看到, *二是加载过的模块最好在不用以后卸载掉。 *如果对Linux环境的系统调用很陌生,建议先看APUE这本书。 */ staticint asdf_open/*open回调*/ ( structinode*inode, structfile*filp ){ printk("^_^:open%s\n",\ current->comm); /* *应用程序的运行环境由内核提供,内核的运行环境由硬件提供。 *这里的current是一个指向当前进程的指针, *现在没必要了解current的细节。 *在这里,当前进程正打开这个设备, *返回0表示打开成功,内核会给它一个文件描述符。 *这里的comm是当前进程在Shell下的command字符串。 */ return0; } staticint asdf_release/*close回调*/ ( structinode*inode, structfile*filp ){ printk("^_^:close\n"); return0; } staticssize_t asdf_read/*read回调*/ ( structfile*filp, char*buf, size_tcount, loff_t*f_pos ){ loff_tpos; pos=*f_pos;/*文件的读写位置*/ if((pos==4096)||(count>4096))return0;/*判断是否已经到设备尾,或写的长度超过设备大小*/ pos =count; if(pos>4096){ count-=(pos-4096); pos=4096; } if(copy_to_user(buf,asdf_body *f_pos,count))return-EFAULT;/*把数据写到应用程序空间*/ *f_pos=pos;/*改变文件的读写位置*/ returncount;/*返回读到的字节数*/ } staticssize_t asdf_write/*write回调,和read一一对应*/ ( structfile*filp, constchar*buf, size_tcount, loff_t*f_pos ){ loff_tpos; pos=*f_pos; if((pos==4096)||(count>4096))return0; pos =count; if(pos>4096){ count-=(pos-4096); pos=4096; } if(copy_from_user(asdf_body *f_pos,buf,count))return-EFAULT; *f_pos=pos; returncount; } staticloff_t asdf_lseek/*lseek回调*/ ( structfile*file, loff_toffset, intorig ){ loff_tpos; pos=file->f_pos; switch(orig){ case0: pos=offset; break; case1: pos =offset; break; case2: pos=4096 offset; break; default: return-EINVAL; } if((pos>4096)||(pos<0)){ printk("^_^:lseekerror%d\n",pos); return-EINVAL; } returnfile->f_pos=pos; }


下载本文示例代码
阅读(321) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~