文章目录
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就可以了,
mount -t nfs -o nolock 192.168.1.103:/nfs/fs_root /mnt
// 默认udpmount -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_chrdev1
和remmod 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
可以看到。