linux 内核学习14-从一个简单的字符设备开始

linux 内核学习14-从一个简单的字符设备开始

1. 设备分类

  1. 字符设备
  2. 块设备
  3. 网络设备

2. 准备工作

  1. 编写一个简单的字符设备驱动,实现基本的open,read和write方法
  2. 编写相应的用户空间测试程序,要求测试程序调用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值