WDS1期第12课 字符设备驱动 2 board,insmod rmmod mknod mdev自动创建设备节点 name对应关系 问题NFS: server not responding,still


NFS: server 192.168.81.32 not responding,still trying…
在使用nfs插入内核模块时候,可能出现问题: NFS: server 192.168.81.32 not responding,still trying…
解决办法: https://blog.csdn.net/gatieme/article/details/48625481
大意是:NFS 的默认传输协议是 UDP,而PC机与嵌入式系统通过UPD交互时就会出现严重的网卡丢包现象; nfs挂载时选择tcp就可以了,

  1. mount -t nfs -o nolock 192.168.1.103:/nfs/fs_root /mnt // 默认udp
  2. mount -t nfs -o tcp,nolock 192.168.1.103:/nfs/fs_root /mnt // tcp,可以传输大文件

服务端和board端的nfs安装和连接参考https://editor.csdn.net/md/?articleId=103464574之挂接NFS。

编写简单的board字符设备驱动,插入到内核,从内核删除,创建节点

1. 简单的字符设备驱动程序

驱动程序名称demo_chrdev.c,
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> 
#include <linux/cdev.h> 

// 操作方法
static int demo_cdev_open(struct inode *inode, struct file *filep)
{
    printk("--%s--%s--%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
static ssize_t demo_cdev_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos)
{
    printk("--%s--%s--%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 操作方法集
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open  = demo_cdev_open,
    .write = demo_cdev_write,
};

// 入口函数
static int demo_cdev_init(void)
{
    register_chrdev(111, "demo_chrdev_1", &fops); // 给主设备号为111,0时自动分配,带返回值
    return 0;
}
// 出口函数
static void demo_cdev_exit(void)
{
    unregister_chrdev(111, "demo_chrdev_1");
}

// 内核模块相关
module_init(demo_cdev_init);
module_exit(demo_cdev_exit);
MODULE_LICENSE("GPL");
Makefile,
KERN_DIR := /home/wuyexkx/Desktop/韦东山/system/linux-2.6.22.6

PWD := $(shell pwd)		# 执行pwd命令并把结果赋给PWD

obj-m := demo_chrdev.o  # .ko的生成依赖于.o,.o默认依赖.c

all:
	make -C $(KERN_DIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm # modules为编译目标
clean:
	make -C $(KERN_DIR) M=$(PWD) clean

2. 将.ko插入到内核

make之后就生成了demo_chrdev.ko模块文件。
查看当前系统的设备有哪些(主设备号 名字): cat /proc/devices
在这里插入图片描述 在这里插入图片描述
加载驱动程序到内核,insmod demo_chrdev.ko,可能遇到最前面提到的问题 NFS: server 192.168.81.32 not responding,still trying… 。成功之后可以看到对应的设备信息,cat /proc/devices
在这里插入图片描述

3. 将.ko从内核中删除

从内核删除 rmmod demo_chrdev,查看一下,没了,
在这里插入图片描述

4. 创建驱动对应的节点

前提是驱动模块已经插入到内核,insmod demo_chrdev.ko
然后创建节点 mknod /dev/xxx c 111 0,设备节点的名称/dev/xxx,设备类型c字符设备,主设备号111,次设备号0,

用户程序user_open.c,
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    fd = open("/dev/xxx", O_RDWR);
    if (fd < 0)
        printf("can't open '/dev/xxx'.\n");
    write(fd, &val, 4);

    return 0;
}

得到输出,对应前面驱动代码里的操作方法里的输出,
在这里插入图片描述

自动分配主设备号,自动创建设备节点

自动创建设备节点,在应用程序中有udev机制,在busybox中有mdev机制。
https://www.cnblogs.com/OpenShiFt/p/6008562.html mdev是busybox自带的一个简化版的udev,适合嵌入式应用场合。其具有使用简单的特点。它的作用就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需要的节点文件。在以busybox为基础构建嵌入式linux根文件系统时,使用它时最优的选择。
mdev可以根据系统信息自动创建设备节点,系统信息是 在插入内核模块之后系统将该模块信息导入到/sys/class中,这个需要在驱动代码中配置才能导入,在入口和出口函数中使用,

驱动程序 demo_chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> 
#include <linux/cdev.h> 
#include <linux/kernel.h>
#include <linux/device.h> 

// 用于自动创建设备节点的结构
static struct class *demo_class;
static struct class_device *demo_class_devs;

// 操作方法
static int demo_cdev_open(struct inode *inode, struct file *filep)
{
    printk("--%s--%s--%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
static ssize_t demo_cdev_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos)
{
    printk("--%s--%s--%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 操作方法集
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open  = demo_cdev_open,
    .write = demo_cdev_write,
};

// 用于保存获取到的主设备号
unsigned int major = 0; 

// 入口函数
static int demo_cdev_init(void)
{
    // 自动获取主设备号资源
    major = register_chrdev(0, "demo_chrdev_1", &fops); //  0自动分配
    
    // 以下为 在系统中生成设备信息的步骤
    // 1. 新建一个class
    demo_class = class_create(THIS_MODULE, "demo_chrdev_class");
    if (IS_ERR(demo_class))
        return PTR_ERR(demo_class);
    // 2. 在class里边创建一个设备叫xxx,然后mdev自动创建设备节点/dev/xxx
    //  在/dev目录下创建相应的设备节点
    demo_class_devs = class_device_create(demo_class, NULL, MKDEV(major, 0), NULL, "demo_cdev_nodeName"); 
    if (unlikely(IS_ERR(demo_class_devs)))
        return PTR_ERR(demo_class_devs);

    return 0;
}
// 出口函数
static void demo_cdev_exit(void)
{
    unregister_chrdev(major, "demo_chrdev_1");
    // 对应卸载
    class_device_unregister(demo_class_devs);  
    class_destroy(demo_class);
}

// 内核模块相关
module_init(demo_cdev_init);
module_exit(demo_cdev_exit);
MODULE_LICENSE("GPL");

class_create和class_device_create在drivers/base/class.c中定义,

struct class *class_create(struct module *owner, const char *name)
{
	struct class *cls;
	int retval;

	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}
...
struct class_device *class_device_create(struct class *cls,
					 struct class_device *parent,
					 dev_t devt,
					 struct device *device,
					 const char *fmt, ...)

用户程序user_open.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    fd = open("/dev/demo_cdev_nodeName", O_RDWR);
    if (fd < 0)
        printf("can't open '/dev/demo_cdev_nodeName'.\n");
    write(fd, &val, 4);

    return 0;
}

只需插入内核模块就自动创建了设备节点:
= 在插入内核模块insmod demo_chrdev.ko后,可以看到252设备和设备名称;
= 然后在用户程序user_open中修改对应设备节点名称demo_cdev_nodeName后运行, 可以打开设备节点就自动创建了252设备节点;
= 然后查看/dev下的那个设备节点,存在;
= 内核信息在/sys/class/class_name/dev_node_name/dev中,cat dev可以看到主设备号和次设备号。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为什么会自动创建设备节点

class_create和class_device_create会在/sys/class下面建立对应的设备节点信息,mdev会根据这些信息创建设备节点。
为什么一插入内核模块insmod xxx.ko就会生成这些信息,是因为那两个函数;
为什么一在sys/class下一生成这些信息mdev就自动创建设备节点,是因为在脚本文件/etc/init.d/rcS中有 /sbin/mdev > /proc/sys/kernel/hotplug

在这里插入图片描述
简单解析一下mdev在rcS这个脚本中的使用,
mdev有两个主要的用途:初始化常用设备和动态更新。这两个用途都要需要内核的sysfs的支持并且需要把sysfs挂载到/sys。对于动态更新来说,你也需要开启内核的热插拔功能。

**各个name对应的对应关系

1. 内核驱动模块名

内核驱动模块的名字是Makefile里指定的obj-m := led_chrdev1.o(一般跟驱动.c文件名字对应)。
insmod led_chrdev1remmod led_chrdev1时必须是与Makefile中的一致。

2. 主设备号对应的名称/proc/devices

主设备号对应的名字在register_chrdev(0, "major_name_led", &fops)中指定,在/proc/devices可以看到。

3. mdev设备class名称

class_create(THIS_MODULE, "led_chrdev_class1")中指定,在/dev/led_chrdev_class1显示。

4. mdev设备自动创建的节点名称

class_device_create(demo_class, NULL, MKDEV(major, 0), NULL, "led_on_off1")中指定,在/dev/led_chrdev_class1/led_on_off1可以看到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值