什么网站可以自己做配图,搜题公众号怎么制作,旅游景点,做笔记的网站我们在前面讲到了file_operations#xff0c;其是一个函数指针的集合#xff0c;用于存放我们定义的用于操作设备的函数的指针#xff0c;如果我们不定义#xff0c;它默认保留为NULL。其中有最重要的几个函数#xff0c;分别是open()、read()、write()、ioctl()#xff… 我们在前面讲到了file_operations其是一个函数指针的集合用于存放我们定义的用于操作设备的函数的指针如果我们不定义它默认保留为NULL。其中有最重要的几个函数分别是open()、read()、write()、ioctl()下面分别对其进行解析 一、 打开和关闭设备函数 a -- 打开设备 int (*open) (struct inode *, struct file *); 在操作设备前必须先调用open函数打开文件可以干一些需要的初始化操作。当然如果不实现这个函数的话驱动会默认设备的打开永远成功。打开成功时open返回0。 b -- 关闭设备 int (*release) (struct inode *, struct file *); 当设备文件被关闭时内核会调用这个操作当然这也可以不实现函数默认为NULL。关闭设备永远成功。 这两个函数已经讲过这里不再赘述主要看下面几个函数 二、read()、write() 函数 现在把 read()、write() 两个函数放一起讲因为两个函数非密不可分的先看一下两个函数的定义 a -- read() 函数 函数原型 ssize_t (*read) (struct file * filp, char __user * buffer, size_t size , loff_t * p); 参数含义 filp 为进行读取信息的目标文件 buffer 为对应放置信息的缓冲区即用户空间内存地址 size 为要读取的信息长度 p 为读的位置相对于文件开头的偏移在读取信息后这个指针一般都会移动 移动的值为要读取信息的长度值 b -- write() 函数 函数原型 ssize_t (*write) (struct file * filp, const char __user * buffer, size_t count, loff_t * ppos); 参数含义 filp 为目标文件结构体指针 buffer 为要写入文件的信息缓冲区 count 为要写入信息的长度 ppos 为当前的偏移位置这个值通常是用来判断写文件是否越界 两个函数的作用分别是 从设备中获取数据及发送数据给设备应用程序中与之对应的也有 write() 函数及 read() 函数 len read(fd,buf,len) len write(fd,buf,size) static ssize_t hello_read(struct file *filep, char __user *buf, size_t len, loff_t *pos) static ssize_t hello_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos) 我们知道应用程序工作在用户空间而驱动工作在内核空间二者不能直接通信的那我们用何种方法进行通信呢下面介绍一下内核中的memcpy---copy_from_user和copy_to_user虽然说内核中不能使用C库提供的函数但是内核也有一个memcpy的函数用法跟C库中的一样。 下面看一下copy_from_user() 及 copy_to_user() 函数的定义 [cpp] view plaincopy static inline int copy_from_user(void *to, const void __user volatile *from, unsigned long n) { __chk_user_ptr(from, n); volatile_memcpy(to, from, n); return 0; } static inline int copy_to_user(void __user volatile *to, const void *from, unsigned long n) { __chk_user_ptr(to, n); volatile_memcpy(to, from, n); return 0; } 可以看到两个函数均是调用了
_memcpy() 函数[cpp] view plaincopy static void volatile_memcpy(volatile char *to, const volatile char *from, unsigned long n) { while (n--) *(to) *(from); } 其实在这里我们可以思考既然拷贝的功能上面的_memcpy() 函数就可以实现为什么还要封装成 copy_to_user()和copy_from_user()呢?答案是_memcpy() 函数是有缺陷的譬如我们在用户层调用函数时传入的不是字符串而是一个不能访问或修改的地址那样就会造成系统崩溃。 出于上面的原因
内核和用户态之间交互的数据时必须要先对数据进行检测
如果数据是安全的才可以进行数据交互。上面的函数就是memcpy的改进版在memcpy功能的基础上加上的检查传入参数的功能防止有些人有意或者无意的传入无效的参数。 现在我们可以审视一下这两个函数了 [cpp] view plaincopy static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) 用法 和memcpy的参数一样但它根据传参方向的不同分开了两个函数。 to是相对于内核态来说的。所以to函数的意思是从from指针指向的数据将n个字节的数据传到to指针指向的数据。 from也是相对于内核来说的。所以from函数的意思是从from指针指向的数据将n个字节的数据传到to指针指向的数据。 返回值函数的返回值是指定要读取的n个字节中还剩下多少字节还没有被拷贝。 注意 一般的如果返回值不为0时调用copy_to_user的函数会返回错误号-EFAULT表示操作出错。当然也可以自己决定。 又到了摆实例的时候了这里只列出部分代码看看这两个函数的用法 [cpp] view plaincopy static ssize_t hello_read(struct file *filep, char __user *buf, size_t len, loff_t *pos) { if(len64) { len 64; } if(copy_to_user(buf,temp,len)) { return -EFAULT; } return len; } static ssize_t hello_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos) { if(len64) { len 64; } if(copy_from_user(temp,buf,len)) { return -EFAULT; } printk(write %s\n,temp); return len; } 测试程序 [cpp] view plaincopy #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h char buf[]111232342342342; char temp[64]{0}; main() { int fd,len; fd open(/dev/hello,O_RDWR); if(fd0) { perror(open fail \n); return ; } write(fd,buf,strlen(buf)); lenread(fd,temp,sizeof(temp)); printf(len%d,%s \n,len,temp); close(fd); } 到这里open、close、read、write四个函数已经学完下面我们来看一下四个函数使用时到底经历了一个怎样的过程 注箭头方向是从调用的一方指向受作用的一方