copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?

copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?,第1张

由于内核空间和用户空间是不能互让猜相访问的,如果需要访问就必须借助内核函数进行数据读写。

copy_to_user():完成内核空间到用户空间的复制。

copy_from_user():是完成用户空间到内核空间的复制。

一般用于file_operations结构里的read,write,ioctl等内存数据交换作用的函数。当然,如果ioctl没有用到内存数据复制,那么就不会用到这两个函数。

设备:和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备上能够容纳文件系统,如:u盘,SD卡,磁盘等。

字符设备和块设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内桐滑孙核中局链,和字符驱动程序相比,块驱动程序具有完全不同的接口。

由于内核空间与用物芹户空间的内存不能罩档毕直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制,借助copy_from_user()完成内核空间到用户空间蠢洞的复制。

将自己开裤蚂枯发的内核代码加入到Linux内核中,需要3个步骤:

1、确定把自己开发代码放入到内核合适的位置

将demo_chardev.c文件拷贝到.../drivers/char/目录下。

demo_chardev.c

[cpp] view plain copy

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

/*结构体file_operations定义的头文件*/

#include <linux/fs.h>

/*声明copy_to/from_user函数的头文件*/

#include <linux/uaccess.h>

/*声明胡洞class_create 和device_create相关信息物如*/

#include <linux/device.h>

#define DEMO_DEBUG

#ifdef DEMO_DEBUG

#define dem_dbg(fmt, arg...) printk(KERN_WARNING fmt, ##arg)

#else

#define dem_dbg(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)

#endif

#define DEVICE_COUNT 2

/*记录当前驱动所占用的主设备号*/

static int major = 0

static int demo_open (struct inode *pnode, struct file *filp)

{

dem_dbg("[kern func]: %s major: %d minor: %d\n",

__FUNCTION__, imajor(pnode), iminor(pnode))

return 0

}

static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)

{

unsigned char ary[100] = "you are reading successfully!"

unsigned long len = min(count, sizeof(ary))//min是个宏,用来获取两个数中较小的值

int retval

dem_dbg("[kern func]: %s major: %d minor: %d\n",

__FUNCTION__, imajor(filp->f_dentry->d_inode),

iminor(filp->f_dentry->d_inode))

//file结构体的f_flags成员可用来判断是否阻塞读取,然后进行相应处理

if(copy_to_user(buf, ary, len) != 0){

retval = -EFAULT

goto cp_err

}

return len//成功返回实际传输的字节数

cp_err:

return retval

}

static ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)

{

unsigned char ary[100] = ""

unsigned long len = min(count, sizeof(ary))//min是个宏,用来获取两个数中较小的值

int retval

dem_dbg("[kern func]: %s major: %d minor: %d\n",

__FUNCTION__, imajor(filp->f_dentry->d_inode),

iminor(filp->f_dentry->d_inode))

if(copy_from_user(ary, buf, len) != 0){

retval = -EFAULT

goto cp_err

}

printk("[msg]: writing context: %s\n",ary)

return len//成功返回实际传输的字节数

cp_err:

return retval

}

static int demo_release (struct inode *pnode, struct file *filp)

{

dem_dbg("[kern func]: %s major: %d minor: %d\n",

__FUNCTION__, imajor(pnode), iminor(pnode))

return 0

}

/*@定义file_operations结构体变量*/

static struct file_operations fops = {

.owner = THIS_MODULE,

.read = demo_read,

.write = demo_write,

.open = demo_open,

.release = demo_release,

}

static struct class *demo_class

static int __init drvdemo_init(void)

{

struct device *demo_device

int i

int retval

dem_dbg("[msg]:this is a driver demo, in module initial function\n")

/*注册字符驱动函数,成功 返回动态分配好的主设备号,失败

*返回错误码(负值)*/

major = register_chrdev(0, "demo_chrdev", &fops)

if(major <0){

retval = major

goto chrdev_err

}

/*创建设备类*/

demo_class = class_create(THIS_MODULE,"demo_class")

if(IS_ERR(demo_class)){

retval = PTR_ERR(demo_class)

goto class_err

}

/*创建设备文件,通知用户在“/dev/”目录下创件名字为demoX的设备文件*/

for(i=0i<DEVICE_COUNTi++){ //最多可创建255个设备节点(register_chrdev函数会申请0-254范围的从设备号)

demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i)

if(IS_ERR(demo_device)){

retval = PTR_ERR(demo_device)

goto device_err

}

}

return 0

device_err:

while(i--) //设备节点创建的回滚 *** 作 device_destroy(demo_class,MKDEV(major, i))

class_destroy(demo_class)//删除设备类

class_err:

unregister_chrdev(major, "demo_chrdev")

chrdev_err:

return retval

}

static void __exit drvdemo_exit(void)

{

int i

dem_dbg("[msg]:in module exit function\n")

/*注销字符驱动函数,无返回值,major为已分配的主设备号*/

unregister_chrdev(major, "demo_chrdev")

/*删除设备节点和设备类*/

for(i=0i<DEVICE_COUNTi++)

device_destroy(demo_class,MKDEV(major, i))

class_destroy(demo_class)

}

module_init(drvdemo_init)

module_exit(drvdemo_exit)

MODULE_LICENSE("Dual BSD/GPL")//BSD/GPL双重许可证

MODULE_AUTHOR("hanbo") //模块作者(可选)

MODULE_DESCRIPTION("used for studing linux drivers")//模块儿简介(可选)

2、把自己开发的功能增加到Linux内核的配置选项中,使用户能够选择此功能

vi drivers/char/Konfig 在文件结尾,endmenu的前面加入一个config选项

[cpp] view plain copy

config DEMO_CHARDEV

bool "demo_chardev driver for hanbo chardev boards"

default y

help

this is CHARDEV driver for hanbo chardev boards.

3、构建或修改Makefile,根据用户的选择,将相应的代码编译到最终生成的Linux内核中去

make menuconfig(添加配置选项)(如果提示找不到“ncurses”库则执行命令: sudo apt-get install libncurses5-dev )

Device driver -->

character devices ->

[*] demo_chardev driver for hanbo chardev boards

4、vi drivers/char/Makefile 添加内容如下:

..........

obj-$(CONFIG_DEMO_CHARDEV)+=demo_chardev.o (添加)

obj-$(CONFIG_JS_RTC) +=js-rtc.o(自带)

js-rtc-y = rtc.o (自带)

5、make (更新内核镜像到开发板)

6、交叉编译测试程序,放到开发板运行

arm-linux-gcc-gcc test.c -o demo

test.c

[cpp] view plain copy

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

int main(int argc, char *argv[])

{

int fd1 = 0, fd2 = 0

unsigned char buf1[100] = "I am a test program!"

unsigned char buf2[100] = {0}

int retval

//以读写、不阻塞方式打开设备文件

fd1 = open("/dev/demo0", O_RDWR | O_NONBLOCK)

if(fd1 <0){

perror("open /dev/demo1")

goto out

}

//以只读、阻塞方式打开设备文件

fd2 = open("/dev/demo1", O_RDONLY)

if(fd2 <0){

perror("open /dev/demo2")

goto out

}

//成功返回实际写入字节数,失败返回负值

retval = write(fd1, buf1, strlen(buf1)+1)

if(retval <0){

perror("writing fd1 failed!")

goto out

}

printf("<user space>: write bytes: %d write content: %s\n", retval, buf1)

//成功返回实际读取字节数,失败返回负值

retval = read(fd2, buf2, sizeof(buf2))

if(retval <0){

perror("reading fd2 failed!")

goto out

}

printf("<user space>: read bytes: %d read content: %s\n", retval, buf2)

return 0

out:

if(fd1 >0)

close(fd1)

if(fd2 >0)

close(fd2)

return -1

}

二、手动加载驱动 .ko文件

1、上面的demo_chardev.c文件放到内核下编译生成 .ko文件

Makefile

[cpp] view plain copy

#如果已定义KERNELRELEASE,说明是由内核构造系统调用的

#可以利用内建语句

ifneq ($(KERNELRELEASE),)

obj-m +=demo_chrdev.o

#此时由内核构造系统调用

else

#定义并记录内核源码路径

KERNELDIR = /home/hanbo/linux-2.6.35.7(自己源码路径,2.6.35.7指当前内核版本)

#记录当前工程目录

PWD := $(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

@rm -rf *.o .t* .m* .*.cmd *.mod.c *.order *.symvers

endif

clean:

rm -rf *.ko *.o .t* .m* .*.cmd *.mod.c *.order *.symvers

2、 然后用命令加载 .ko 驱动

lsmod 列举当前系统中的所有模块

lsmod 列举当前系统中的所有模块

rmmod xxx 卸载指定模块(不需要.ko后缀)

3、如果自己编译的代码中没有用

/*创建设备类*/

demo_class = class_create(THIS_MODULE,"demo_class")

/*创建设备文件,通知用户在“/dev/”目录下创件名字为demoX的设备文件*/

demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i)

则需要手动添加设备节点

mknod /dev/demo1 c 主设备号 0

mknod /dev/demo2 c 主设备号 1

注意:若卸载时出现提示 rmmod:chdir(2.6.35.7):No such file or directory

则在开发板根文件系统下创建目录:/lib/modules/2.6.35.7(跟当前内核版本同名)


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/12235286.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-22
下一篇 2023-05-22

发表评论

登录后才能评论

评论列表(0条)

保存