发新话题
打印

字符设备学习实例

字符设备学习实例

这是一个字符驱动的框架,使用固定分配主设备号和次设备号,功能只是读取内核
中的一段字符串“hello world!”但是麻雀随小无脏俱全!
模块代码
/*chardev.c*/
#include <linux/kernel.h>
#include <linux/fs.h> /*for file-f_op*/
#include <linux/module.h>
#include <asm/uaccess.h> /*for copy_to_user()*/
#include <linux/cdev.h> /*for cdev ,cdev_init,cdev_add....*/

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Helight");

#define DP_MAJOR 250 /*the major number of the chardev*/
#define DP_MINOR 0 /*the minor number of the chardev*/
static int char_read(struct file *filp,char __user *buffer,size_t,loff_t
*); /*the read operation of the chardev----read the data from kernel*/
static int char_open(struct inode *,struct file *); /*open the chardev*/
static int char_write(struct file *filp,const char __user *buffer,size_t
,loff_t*); /*write data to kernel*/
static int char_release(struct inode *,struct file *); /*release the
chardev*/
static char *arr,*p;
static int chropen; /*the chardev open or not*/
struct cdev *chardev; /*define a char device*/
static int len;

struct file_operations char_ops = {
.read = char_read,
.write = char_write,
.open = char_open,
.release = char_release,
};



static int __init char_init(void)
{
dev_t dev;
printk(KERN_ALERT"Initing......\n");
dev=MKDEV(DP_MAJOR,DP_MINOR);
chardev = cdev_alloc( );
if(chardev==NULL){
return -1;
}
if(register_chrdev_region(dev,10,"chardev")<0){
printk(KERN_ALERT"Register char dev error\n");
return -1;
}
chropen=0;
len=0;

cdev_init(chardev,&char_ops);
if(cdev_add(chardev,dev,1)<0)
{
printk(KERN_ALERT"Add char dev error\n");
}

return 0;
}

static int char_open(struct inode *inode,struct file *file)
{
if(chropen==0)
chropen++;
else{
printk(KERN_ALERT"Another process open the char device\n");
return -1;
}

try_module_get(THIS_MODULE);
return 0;
}

static int char_release(struct inode *inode,struct file *file)
{
chropen--;
module_put(THIS_MODULE);
return 0;
}

static int char_read(struct file *filp,char __user *buffer,size_t
length,loff_t *offset)
{


if(copy_to_user(buffer,"hello world!",length))
{
return 0;
}

return 0;
}

static int char_write(struct file *filp,const char __user *buffer,size_t
length,loff_t *offset)
{

return 0;
}


static void __exit module_close(void)
{
len=0;
printk(KERN_ALERT"Unloading..........\n");
unregister_chrdev_region(MKDEV(DP_MAJOR,DP_MINOR),10);
cdev_del(chardev);
}

module_init(char_init);
module_exit(module_close);

用户程序
/*main.c*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>


int main(void)
{
int testdev;
int i;
char buf[15];

testdev = open("/dev/chardev0",O_RDWR);
if ( testdev == -1 )
{
printf("Cann't open file \n");
exit(0);
}
memset(buf, 0, sizeof(buf));
read(testdev,buf,12);

printf("%s\n",buf);
close(testdev);

return 0;
}

Makefile文件:

obj-m:=chardev.o
KERNELDIR=/usr/src/linux-headers-2.6.22-14-generic/
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
gcc -o main main.c

使用步骤:
1。make模块
2。insmod模块
3。mknod结点,具体命令如下:
mknod /dev/chardev0 c 250 0
命令解释:
mknod是建立节点的命令;
/dev/chardev0:在/dev/目录下建立chardev0这样一个节点,
c:这个节点是指向一个字符设备的节点
250:这个设备的主设备号;
0:次设备号
4。chmod 666 /dev/chardev0 使其他用户也可以对这个设备进行读写操作,否则
只有root用户可以对他进行读写
5。编译用户程序
6,运行用户程序./main
7.如果没有什么问题的话应该要输出
hello world!
这几个字符。
Zhenwen Xu - Open and Free
Home Page:    http://dim4.cn
由于匆忙这里写的有点问题:

if(copy_to_user(buffer,"hello world!",length)) //未对length的大小作判断
{
return 0;
}

read(testdev,buf,12);      //未对返回值作检查

[ 本帖最后由 helight 于 2008-4-11 18:18 编辑 ]
好贴!
等会实验一下。
我有几个疑问,因为我内核是2.6.11,没有最后到2.6.16
1---
如果想把这个模块加入内核,不需要配置kernel吗?
linux 2.4之后,从2.6开始,内核配置就开始了很大改变,那就是可以动态
配置,即可以动态加载,也可以动态卸载.但是内核配置总是需要的.
2----
其次,不需要自己去写配置文件?我记得我那时候开发内核驱动,是要写kconfig 和 Makfile的
内核编译后以*.ko为标记
3---
升级工具,2.4的加载工具不再适用2.6以后的内核,必须升级,所有内核驱动的工具都需要升级

后来的新手估计会在这三个问题上应该遇到很多问题,
既然作者已经完全成功解决了,就应该说明清除,
否则后来的新手还要遇到很多麻烦和困难.写好文档说明.

[ 本帖最后由 someone 于 2008-4-12 20:18 编辑 ]
1---
>如果想把这个模块加入内核,不需要配置kernel吗?
>linux 2.4之后,从2.6开始,内核配置就开始了很大改变,那就是可以动态
>配置,即可以动态加载,也可以动态卸载.但是内核配置总是需要的.

首先动态加载模块2.4就可以,不是从2.6开始的。而且最新的内核是2.6.25-x了.
不需要配置内核,只要有何当前内核版本相一致的内核源码就可以了。

2----
>其次,不需要自己去写配置文件?我记得我那时候开发内核驱动,是要写kconfig 和 Makfile的
>内核编译后以*.ko为标记

只需要写一个内核模块的Makefile,当然也可以不写,你可以直接写上你的编译命令。
内核模块编译后,在2.4下是以.o为后缀的,2.6下是以.ko为后缀的。

3---
>升级工具,2.4的加载工具不再适用2.6以后的内核...

2.4下和2.6下内核模块的加载和卸载的命令是insmod和rmmod。当然要是是内核升级的话,我不推荐直接从2.4升级到2.6。
如果非要升的话,是要安装一些工具的。

>后来的新手估计会在这三个问题上应该遇到很多问题..

我是以为要做这个例子之前是至少看过ldd的第一章和第二章的。

[ 本帖最后由 helight 于 2008-4-13 10:35 编辑 ]
2年不练手了,都生锈了,手生了,
好多都忘记了,现在linux升级太快了
科技发展真是日新月异,


不过,linux升级后,不能兼容以前的也一直被诟病,
比如从2.4到2.6升级攻击最多,这一步跨的太大,从工具到驱动
有了脱胎换骨的感觉.很多以前的驱动都需要重新写.
工具module-init-tool也不能用



谢谢你,让我重新开始了 linux 回顾.
说真的,正是看到你的文章,我才想试试自己还有多少兴趣去玩linux
发新话题