linux 内核学习14-从一个简单的字符设备开始
1. 设备分类
- 字符设备
- 块设备
- 网络设备
2. 准备工作
- 编写一个简单的字符设备驱动,实现基本的open,read和write方法
- 编写相应的用户空间测试程序,要求测试程序调用read的函数,并能看到对应的驱动程序执行了相对的read方法
- Makefile
BASEINCLUDE ?=/lib/modules/`uname -r`/build
mytest-objs :=my_demodev.o
obj-m :=mydemo.o
all :
$(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules;
clean:
$(MAKE) -C $(BASEINCLUDE) SUBDIRS=$(PWD) clean;
rm -f *.ko;
- my_demodev.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/cdev.h>
#define DEMO_NAME "my_demo_dev"
static dev_t dev;
static struct cdev *demo_cdev;
static signed count=1;
static int demodrv_open(struct inode *inode,struct file *file)
{
int major =MAJOR(inode->i_rdev);
int minor =MINOR(inode->i_rdev);
printk("%s: major=%d,minor=%d\n",__func__,major,minor);
return 0;
}
static int demodrv_release(struct inode *inode,struct file *file)
{
return 0;
}
static ssize_t demodrv_read(struct file *file,char __user *buf,size_t lbuf,loff_t *ppos)
{
printk("%s enter\n",__func__);
return 0;
}
static ssize_t demodrv_write(struct file *file,const char __user *buf ,size_t count,loff_t *f_pos)
{
printk("%s enter\n",__func__);
return 0;
}
static const struct file_operations demodrv_fops={
.owner=THIS_MODULE,
.open=demodrv_open,
.release=demodrv_release,
.read=demodrv_read,
.write=demodrv_write
};
static int __init simple_char_init(void)
{
int ret;
ret=alloc_chrdev_region(&dev,0,count,DEMO_NAME);
if(ret){
printk("failed to allocate char device region");
return ret;
}
demo_cdev=cdev_alloc();
if(!demo_cdev){
printk("cdev_alloc failed\n");
goto unregister_chrdev;
}
cdev_init(demo_cdev,&demodrv_fops);
ret=cdev_add(demo_cdev,dev,count);
if(ret){
printk("cdev_add failed\n");
goto cdev_fail;
}
printk("successed register char device : %s\n",DEMO_NAME);
printk("Major numbner =%d,minor number =%d\n",MAJOR(dev),MINOR(dev));
return 0;
cdev_fail:
cdev_del(demo_cdev);
unregister_chrdev:
unregister_chrdev_region(dev,count);
return ret;
}
static void __exit simple_char_exit(void)
{
printk("removing device\n");
if(demo_cdev)
cdev_del(demo_cdev);
unregister_chrdev_region(dev,count);
}
module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("xiao");
MODULE_DESCRIPTION("simple chararcter device");
MODULE_ALIAS("xiao");
- 编译笨叔叔的linux内核
➜ runninglinuxkernel_4.0-rlk_basic export ARCH=arm
➜ runninglinuxkernel_4.0-rlk_basic export CROSS_COMPILE=arm-linux-gnueabi-
➜ runninglinuxkernel_4.0-rlk_basic make vexpress_defconfig
➜ runninglinuxkernel_4.0-rlk_basic make -j2
➜ simple_module make
make -C /home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic M=/home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic/simple_module modules;
make[1]: Entering directory '/home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic'
CC [M] /home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic/simple_module/my_test.o
LD [M] /home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic/simple_module/mytest.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic/simple_module/mytest.mod.o
LD [M] /home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic/simple_module/mytest.ko
make[1]: Leaving directory '/home/groot/linuxstudy/runninglinuxkernel_4.0-rlk_basic'
在编译成功之后,检查mytest.ko是否为ARM架构
file mytest.ko
mytest.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=dbe32291071e7a09c599a2d91ec4c1fa0a73e0f9, with debug_info, not stripped
这个时候需要将mytest.ko
复制到runninglinuxkernel_4.0-rlk_basic/kmodules目录下
cp mytest.ko runninglinuxkernel_4.0-rlk_basic/kmodules
接着运行run.sh
脚本
./run.sh arm32
启动QEMU虚拟机之后,首先检查/mnt 目录是否有mytest.ko文件。
/ # cd /mnt/
/mnt # ls
README mytest.ko
/mnt #
接着使用insmod命令加载内核
/mnt # insmod mytest.ko
[ 317.326554] successed register char device : my_demo_dev
[ 317.342293] Major numbner =252,minor number =0
查看设备结点
$ cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 ttyAMA
252 my_demo_dev
253 usbmon
254 rtc
Block devices:
259 blkext
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 virtblk
可以发现设备id是252
mknod /dev/demo_drv c 525 0
生成之后可以通过ls -l
命令查看/dev/目录的情况
/dev # ls -l
total 0
crw-rw---- 1 0 0 14, 4 Sep 2 10:07 audio
crw-rw---- 1 0 0 5, 1 Sep 2 10:07 console
crw-r--r-- 1 0 0 252, 0 Sep 2 10:14 demo_drv
下面使用一个用户控件的测试程序来测试
test.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEMO_DEV_NAME "/dev/demo_drv"
int main(){
char buffer[64];
int fd;
fd=open(DEMO_DEV_NAME,O_RDONLY);
if(fd<0){
printf("open device %s failded\n",DEMO_DEV_NAME);
return -1;
}
read(fd,buffer,64);
close(fd);
return 0;
}
测试这个程序很简单,打开这个/dev/demo_drv 设备,调用一次read函数,就关闭了设备
编译test.c的内容
arm-linux-gnueabi-gcc test.c -o test --static
把编译好的test复制到kmodules目录下,然后回到QEMU虚拟机在/mnt目录已经可以看到test了
/mnt # ./test
[ 1107.879892] demodrv_open: major=252,minor=0
[ 1107.888865] demodrv_read enter