本文作者:admin

驱动器标签太长,或包含无效字符?

芯岁网络 2025-03-08 03:18 0 0条评论

一、驱动器标签太长,或包含无效字符?

一、细节提要

1、与用户与内核数据交换有关的函数(1)copy_from_user()函数

该将数据从用户空间复制到内核空间。

如果成功复制则返回0,如果不成功复制则返回尚未成功复制的剩下的字节数。

(2)copy_to_user()函数

将数据从内核空间复制到用户空间。

(3)复制机制与使用mmap的对比

复制时,内核空间和用户空间的地址不一样,效率低。

好文推荐:

字节终面:CPU 是如何读写内存的?

全网最牛Linux内核分析--Intel CPU体系结构

一文让你读懂Linux五大模块内核源码,内核整体架构设计(超详细)

嵌入式前景真的好吗?那有点悬!

一文教你如何使用GDB+Qemu调试Linux内核

Linux内核必读五本书籍(强烈推荐)

全网独一无二Linux内核Makefle系统文件详解(一)(纯文字代码)

带你深度了解Linux内核架构和工作原理!

如何读懂GDB底层实现原理(从这几点入手~)

一文彻底理解Memory barrier(内存屏障)

一篇文带你搞懂,虚拟内存、内存分页、分段、段页式内存管理(超详细)

2、代码逻辑图

二、代码示例

1、代码编写

在ubuntu的/home/xjh/iot/embedded_basic/rootfs/tmp中编写代码:app.c与module_test.c。

2、代码编译

(1)编译驱动源代码

利用同目录下的Makefile文件编译module_test.c,得到module_test.ko驱动文件。

(2)编译应用层程序

怎样编译app.c?使用ubuntu的gcc还是交叉编译工具链?或者和驱动源代码一样的操作(这个如何完成编译的)?

因为此应用层程序要在开发板运行,因此需要使用交叉编译工具链中的gcc来编译,而不是 ubuntu 中的gcc。

在已经正确安装了交叉编译工具链的ubuntu系统中,使用如下命令编译app.c得到app.exe。

arm-linux-gcc app.c -o app.exe

因为之前实验中已经将ubuntu的/home/xjh/iot/embedded_basic/rootfs/tmp挂载到开发板/mnt目录,所以开发板完全启动后,可以在/mnt目录中看到刚才编译的文件。

3、代码测试

可以直接在开发板的/mnt目录下进行测试。

(1)装载测试

[root@xjh mnt]# lsmod
    Not tainted
[root@xjh mnt]# insmod module_test.ko //安装模块
[ 5278.035378] chrdev_init helloworld init
[ 5278.038524] register_chrdev success... mymajor = 250.//自动分配的主设备号为250
[root@xjh mnt]# lsmod //列出已经安装的模块
    Not tainted
module_test 1823 0 - Live 0xbf006000//这个具体表示什么意思?
[root@xjh mnt]#

装载后查看/proc/devices,是否有驱动源码中所写的驱动名字、自动分配的主设备号。

[root@xjh mnt]# cat /proc/devices
Character devices:
  1 mem
  2 pty
//省略……
250 testchar  //这里出现了我们在驱动程序中给驱动取的名字、自动分配的主设备号
//省略……
 
Block devices:
  1 ramdisk
259 blkext
//省略……
179 mmc
254 device-mapper

(2)创建设备文件

[root@xjh mnt]# cd /dev
[root@xjh dev]# ls
CEC                 ptyr6               sequencer2          ttyq5
HPD                 ptyr7               snd                 ttyq6
adc                 ptyr8               tty                 ttyq7
//省略……这里没有test这设备文件。
//接下来看执行“mknod /dev/test c 250 126”之后的效果如何
[root@xjh dev]#
 
[root@xjh ]# mknod /dev/test c 250 1
[root@xjh ]# ls /dev
CEC                 ptyr6               sequencer2                    ttyq4
HPD                 ptyr7               snd                           ttyq5
adc                 ptyr8               test //出现了设备文件test      tyq6
alarm               ptyr9               tty                           ttyq7
//省略……
[root@xjh ]# ls -l /dev/test         
crw-r--r--    1 root     root      250,      126     Jan  1 14:02 /dev/test
[root@xjh ]#                    //主设备号  //次设备号  和mknod时的设置一样

(3)操作设备文件

运行应用层程序app.exe,观察运行效果。

[root@xjh mnt]# ./app.exe 
[ 1587.469172] test_chrdev_open
[ 1587.470769] test_chrdev_write
[ 1587.473521] copy_from_user success..
[ 1587.477106] test_chrdev_read
[ 1587.479949] copy_to_user success..
[ 1587.483352] test_chrdev_release
open /dev/test success..  //这是应用层的判断是否open成功的代码,为何那么迟才输出?
璇诲嚭鏉ョ殑鍐呭鏄細helloworld2222. //为何乱码,scrt的缘故?
[root@xjh mnt]#

文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!

点击报名免费内核学习直播课程:

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639ke.qq.com/course/4032547?flowToken=1042639

说明

1)Linux系统字符编码默认是UTF-8格式的,如果SecureCRT没有设置成UTF-8格式,中文显示会出现乱码。设置SCRT格式的方法见博客:SecureCRT显示乱码的解决办法

2)为何应用层判断是否open成功的代码很迟才输出?(待解决)

(4)卸载模块测试

[root@xjh mnt]# rmmod module_test.ko 
[ 1385.257231] chrdev_exit helloworld exit
[root@xjh mnt]# lsmod
    Not tainted
[root@xjh mnt]#

4、总结说明

(1)应用层的代码编译,要使用交叉编译工具链。

(2)驱动源代码的编译,要使用与开发板系统内核版本一致的内核源码进行编译。其实应该还是借用ubuntu的 gcc工具来进行编译的,因为内核源码中没有gcc编译器。Makefile 文件指明要进入这个与开发板系统内核版本一致的内核源码中,然后make modules。这说明是根据内核源码中的Makefile文件的指示,执行Makefile文件中的一个目标modules。

//在Makefile的1300行
 
modules: $(module-dirs)
    @$(kecho) '  Building modules, stage 2.';
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

(3)mymajor = register_chrdev(0, MYNAME, &test_fops);

参数 0 表示让系统自动分配主设备号。

参数MYNAME表示设备(或者说驱动)的名字。

参数&test_fops是指向struct file_operations变量的指针,这个变量代表着驱动的实质内容。

返回值mymajor代表系统为设备分配的主设备号。

/proc/devices文件记录着系统中已经注册的块设备和字符设备。可以通过cat或者vim(但只能读不能修改)来查看此文件内容。注册成功后,可以通过/proc/devices文件查看到该设备的名字MYNAME及其设备号mymajor。

此函数成功后,主设备号为mymajor的设备与它的驱动内容&test_fops就关联起来了,也就是说一个设备对应了一个驱动内容。那在应用层如何表示这个设备呢?见(4)。

(4)/dev/test是设备文件,是命令行中使用mknod命令创建的。安装好驱动模块后,得到系统分配的主设备号mymajor(这里是250)。然后利用这个主设备号来创建设备文件,即执行“mknod /dev/test c 250 126”。这样一来,主次设备号为250、126的设备就和设备文件/dev/test关联了,应用层通过API操作/dev/test文件,也就是操作主次设备号为250、126的设备,而应用层操作里的API就对应着这个设备对应的驱动&test_fops。

设备文件不能使用vim打开,可以使用ls -l 命令查看。

综合(3)(4),主次设备号、设备名字、驱动内容、设备文件,这几个概念要清楚。

主次设备号:主设备号是register_chrdev()的返回值mymajor,次设备号在mknod时设定。

设备名字:MYNAME (主设备号、设备名字在/proc/devices文件中)

驱动内容:&test_fops (程序猿在驱动源代码中编写)

设备文件:/dev/test,利用mknod命令手动创建

(5)设备文件是手动创建的,能不能让它自动创建呢?字符设备驱动高级篇4——自动创建设备文件的函数代码分析_天糊土的博客-CSDN博客

(6)应用层的open、read等API,与驱动源码的test_chrdev_open、test_chrdev_read等具体操作函数,通过struct file_operations的填充而关联起来。不过这里的驱动源码中的test_chrdev_open、test_chrdev_read等具体操作函数,并没有做什么事情,按理应该操作一些硬件的。这里只是为了演示应用层读写操作与驱动层的读写如何关联起来的,因而没有硬件操作细节(比如硬件寄存器操作这些行为)。

关于具体的硬件操作见:字符设备驱动基础5——驱动如何操控硬件(动静态映射操作LED)

1、应用层代码:app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//这是应用层
 
#define FILE "/dev/test" 
//“/dev/test”是利用mknod手工创建的设备文件,创建之后,
//这个设备文件就和该设备编号的设备对应上了,操作此设备文件就是操作该设备。
 
//问题是mknod需要输入主设备号,和驱动源码中自动获取的主设备号不相冲突吗?
//不冲突,因为这里就是根据驱动程序安装后得到的主设备号后
//才利用这个主设备号来创建设备文件的。我理解顺序相反了。
//应该驱动程序安装在前,应用程序运行在后。
 
 
char buf[100];
 
int main(void)
{
	int fd = -1;

	fd = open(FILE, O_RDWR);//这里的open,对应的是驱动文件中的.open指定的函数
	if (fd < 0)
	{
		printf("open %s error.\n", FILE);
		return -1;
	}
	printf("open %s success..\n", FILE);

	// 读写文件
	write(fd, "helloworld2222", 14);
	read(fd, buf, 100);
	printf("读出来的内容是:%s.\n", buf);

	// 关闭文件
	close(fd);

	return 0;
}

2、驱动文件:module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
 
#define MYNAME		"testchar"
 
int mymajor;        //内核自动分配的主设备号
char kbuf[100];		//内核空间(即驱动空间,毕竟内核和驱动属于同一层)的buf
 
 
static int test_chrdev_open(struct inode *inode, struct file *file)
{
	// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
	// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
	printk(KERN_INFO "test_chrdev_open\n");

	return 0;
}
 
static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release\n");

	return 0;
}
 
//读函数,即从内核空间(驱动空间)读取数据到用户空间
ssize_t test_chrdev_read(struct file *file, char __user *ubuf, \
                                    size_t count, loff_t *ppos)
{
	int ret = -1;

	printk(KERN_INFO "test_chrdev_read\n");

	//将内容从内核空间(驱动空间)读取到用户空间
	//返回值为0说明读取成功,读取不成功时返回值是剩余没有读取的字节数
	ret = copy_to_user(ubuf, kbuf, count);
	                                 
	if (ret)
	{
		printk(KERN_ERR "copy_to_user fail\n");
		return -EINVAL;
	}
	printk(KERN_INFO "copy_to_user success..\n");

导读-最新发表 - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛 (0voice.com)

原文地址:一文讲解字符设备驱动基础——读写接口的操作实践 - 圈点 - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛 (0voice.com)

二、网站域名可以包含哪些字符?

一个域名最多可以注册63个字符,域名的总长度为253个字符。注册域名时,我们的字符限制不超过63个字符,包括字母、数字、特殊符号等。

域名是人们进入互联网时对相应网站的第一印象,域名非常重要。一般来说,主要是短域名。不过,有些域名的长度很长,但含义很清楚,也能给人留下很好的印象,特别是喜欢英语的单词、组合等,会很长。

注册域名时,域名的最小长度为1个字符,例如单个字母、ABC、单个数字等。最大长度为63个字符,域名的总长度不得超过253个字符。注册长域名时,一定要记住计算字符长度。数字、字母和符号都表示字符,并且不区分大小写。

三、安装路径包含无效字符怎么解决?

原因:1.“Windows”文件夹中的“temp”文件被删或被修改。

2.解决方法:鼠标右击桌面,新建文件夹将其重命名为“temp”,将这个“temp”文件移动到“Windows”文件夹中即可。(“Windows”文件夹包含很多系统文件,不要轻易删除或修改)。

3.temp是临时文件夹,在C:\Documents and Settings\Administrator\LocalSettings\内。很多临时文件放在这里,收藏夹,浏览网页的临时文件,编辑文件等。这是根据操作的过程进行临时保存的。

四、ps注册,提示姓氏包含无效字符?

仅使用英文字母和数字,不要用符号和汉字

五、wps安装路径包含无效字符怎么解决?

步骤/方式1

在wps的软件使用过程中,可以在运行内输入内容打开Personal避免安装无效字符。

品牌型号:联想小新Air14

系统版本:windows10

软件版本:wps2020

点击运行

点击桌面上的”开始“,点击”运行“。

步骤/方式2

输入regedit

在运行窗口中输入”regedit“,点击”确定“。

步骤/方式3

打开编辑器

在打开的注册表编辑器中按路径”HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User shell Folders“,在窗口右侧找到并打开”Personal“。

步骤/方式4

输入内容

在”数值数据”输入:%USERPROFILE%\Documents,点击“确定”即可。

六、ps注册怎么老是姓氏包含无效字符?

你第一个输的是手机号吧,第一个空输姓,第二个输名

七、下载wps显示安装路径包含无效字符?

下载wps显示安装路径包含的无效字符,应该是你自己手工在注册表中修改了程序默认安装路径所致。改回来即可,位置在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion 键右则将ProgramFilesDir 值改为:C:\Program Files 即可。 有的软件安装时,不允许安装目录中有空格

八、用户名包含无效字符是什么意思?

禁止的是连续的连字符。因为连续的连字符的数量不易辨识。而且,两个连字符可能会被输入法自动变为破折号。

九、ipad提示电子邮件包含无效字符怎么办?

输入有问题,全部删除后,重新输入。确保邮件或者邮箱格式正确

十、我的世界服务器用户名包含无效字符?

大部分服务器用/op <用户名> 如果装了lp插件必须使用/lp user <用户名> permission set * true