例说linux内核与应用数据通信(三):读写内核设备驱动文件
读写设备文件也就是调用系统调用read()和write(),系统调用就是内核提供给应用程序的接口,应用程序对底层的操作大部分都是通过系统调用来完毕。差点儿全部的系统调用都涉及到内核和应用的数据交换。本节并不是讲述怎样加入一个系统调用(那是第一节的内容),而是解说怎样利用现有系统调用来实现特定的内核与应用交互需求。
int register_chrdev_region(dev_t from, unsigned count, const char *name)。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
第二步、初始化设备结构体。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)。
在调用该函数之前。还须要初始化file_operations结构体。该结构体成员函数是字符设备驱动设计的主要内容,当在应用上调用read和write等函数时。该结构体中相应的函数会被调用。
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
第四步、实现file_operations结构体中open()、read()、write()、ioctl()等函数。
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); put_user(local,user);
get_user(local,user);当不是该设备时,应当删除该设备、释放申请的设备号。 void cdev_del(struct cdev *dev);
void unregister_chrdev_region(dev_t from, unsigned count);这两函数一般在卸载模块中调用。 以下看一下字符设备的完整实现代码:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h> #include <linux/kernel.h>
#include "chrdev.h" int chr_major;
struct chr_dev *chr_devp; int chr_open(struct inode *inode, struct file *filp)
{
filp->private_data = chr_devp; return 0;
} static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
{
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__); switch(cmd) {
case MEM_CLEAR:
memset(dev->data, 0, CHAR_DEV_DATA_SIZE);
break;
case MEM_RESET:
snprintf(dev->data, CHAR_DEV_DATA_SIZE, "%s", "hello, user!");
break; default:
return -EINVAL;
} return 0;
} static ssize_t chr_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__);
if(p >= CHAR_DEV_DATA_SIZE) {
return 0;
} if(count > CHAR_DEV_DATA_SIZE - p) {
return 0;
}
//将内核中数据dev->data,读取到用户空间buf中,读取count字节
if(copy_to_user(buf, (void*)(dev->data + p), count)) {
return -EINVAL;
} else {
*ppos += count;
ret = count;
} return ret;
} static ssize_t chr_write(struct file* filp, const char __user* buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct chr_dev* dev = filp->private_data; printk(KERN_ALERT"=========%s()=========\n", __func__);
if(p >= CHAR_DEV_DATA_SIZE) {
return 0;
} if(count > CHAR_DEV_DATA_SIZEE - p) {
count = CHAR_DEV_DATA_SIZE - p;
}
//将用户空间buf中数据copy到内核空间dev->data中,copy count字节数据
if(copy_from_user(dev->data + p, buf, count)) {
ret = -EINVAL;
} else {
*ppos += count;
ret = count;
} return ret;
} static loff_t chr_llseek(struct file* filp, loff_t offset, int orig)
{
loff_t ret = 0; printk(KERN_ALERT"=========%s()=========\n", __func__);
/* orig can be SEEK_SET, SEEK_CUR, SEEK_END */
switch(orig) {
case 0:
if(offset < 0) {
ret = -EINVAL;
break;
} if((unsigned int) offset > CHAR_DEV_DATA_SIZE) {
ret = -EINVAL;
break;
} filp->f_pos = (unsigned int) offset;
ret = filp->f_pos;
break; case 1:
if((filp->f_pos + offset) > CHAR_DEV_DATA_SIZE) {
ret = -EINVAL;
break;
} if((filp->f_pos + offset) < 0) {
ret = -EINVAL;
break;
} filp->f_pos += offset;
ret = filp->f_pos;
break; default:
ret = - EINVAL;
break;
} return ret;
} int chr_release(struct inode *inode, struct file *filp)
{
return 0;
} static const struct file_operations chr_fops =
{
.owner = THIS_MODULE,
.open = chr_open,
.release = chr_release,
.read = chr_read,
.write = chr_write,
.llseek = chr_llseek,
.ioctl = chr_ioctl, }; static int chr_dev_init(void)
{
int result;
dev_t devno; /* 注冊设备号 */
result = alloc_chrdev_region(&devno, 0, 1, "chardev");
if (result < 0) {
return result;
} // 分配自己定义设备结构体内存
chr_devp = kmalloc(CHAR_DEV_NO * sizeof(struct chr_dev), GFP_KERNEL);
if (!chr_devp) {
result = - ENOMEM;
goto err;
}
memset(chr_devp, 0, sizeof(struct chr_dev)); /*初始化设备*/
cdev_init(&chr_devp->cdev, &chr_fops);
chr_devp->cdev.owner = THIS_MODULE; /* 加入设备 */
chr_major = MAJOR(devno);
cdev_add(&chr_devp->cdev, MKDEV(chr_major, 0), CHAR_DEV_NO); /*初始自己定义设备结构体内存数据*/
chr_devp->data = kmalloc(CHAR_DEV_DATA_SIZE, GFP_KERNEL);
memset(chr_devp->data, '*', CHAR_DEV_DATA_SIZE / 100 ); //为避免输出太多影响结果显示,此处只初始化40个字节。 return 0; err:
unregister_chrdev_region(devno, 1); return result;
} static void chr_dev_exit(void)
{
cdev_del(&chr_devp->cdev); //delete device
kfree(chr_devp); // release device memory
unregister_chrdev_region(MKDEV(chr_major, 0), 1); // unregister char device No.
} module_init(chr_dev_init);
module_exit(chr_dev_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("shallnet");
MODULE_DESCRIPTION("blog.csdn.net/shallnet");应用程序实现代码例如以下:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h> #define SHR_MEMSIZE 4096
#define MEM_CLEAR 0x0
#define MEM_RESET 0x1
#define CHAR_DEV_FILENAME "/dev/sln_chardev" int main()
{
int fd;
char shm[SHR_MEMSIZE]; /* 打开设备文件 */
fd = open(CHAR_DEV_FILENAME, O_RDWR);
if(fd < 0) {
printf("open <%s> failed!\n", CHAR_DEV_FILENAME);
return -1;
} /* 直接设置共享内存数据 */
snprintf(shm, sizeof(shm), "this data is writed by user!"); /* 写入数据 */
printf("======== Write data========\n");
if (write(fd, shm, strlen(shm)) < 0) {
printf("write(): %s\n", strerror(errno));
return -1;
} /* 再读取数据,以验证应用上设置成功 */
printf("======== Read data========\n");
if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); /* 再清空数据之后再读取 */
printf("========= Clear it now: =======\n");
if (ioctl(fd, MEM_CLEAR, NULL) < 0) {
printf("ioctl(): %s\n", strerror(errno));
return -1;
} if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); /* reset all data, read it and check whether it is ok */
printf("========= Reset it now: =======\n");
if (ioctl(fd, MEM_RESET, NULL) < 0) {
printf("ioctl(): %s\n", strerror(errno));
return -1;
} if (lseek(fd, 0, SEEK_SET) < 0) {
printf("llseek(): %s\n", strerror(errno));
return -1;
} if (read(fd, shm, SHR_MEMSIZE) < 0) {
printf("read(): %s\n", strerror(errno));
return -1;
}
printf("read data: %s\n", shm); close(fd);
return 0;
}在编译驱动和应用程序之后就能够验证内核和应用的数据交换是否成功了。执行验证 例如以下: # insmod chardev.ko //首先插入模块
# cat /proc/devices | grep chardev //查看驱动模块相应主设备号
248 chardev
# mknod /dev/sln_chardev c 248 0 //为设备创建相应节点
#然后再执行应用程序:
# ./read_app
======== Write data========
======== Read data========
read data: this data is writed by user!************ //读取应用设置数据成功
========= Clear it now: ======= //在内核清空数据,应用读取数据为空
read data:
========= Reset it now: ======= //在内核重设空间数据为hello, user!,应用上再读取该数据,输出预期值!
read data: hello, user!
# 本节仅仅实现了file_operations结构体中部分函数,在后面我们还能够实现其他的函数,也能够实现内核和应用交互数据。 本节源代码下载:
http://download.csdn.net/detail/gentleliu/9035821
例说linux内核与应用数据通信(三):读写内核设备驱动文件的更多相关文章
- Linux input子系统学习总结(三)----Input设备驱动
Input 设备驱动 ---操作硬件获取硬件寄存器中设备输入的数据,并把数据交给核心层: 一 .设备驱动的注册步骤: 1.分配一个struct input_dev : struct ...
- 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- Linux usb子系统(二):USB设备驱动usb-skeleton.c
usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析u ...
- linux驱动开发(三) 字符设备驱动框架
还是老规矩先上代码 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/ke ...
- VxWorks6.6 pcPentium BSP 使用说明(三):设备驱动
本文主要介绍了pcPentium BSP中包含的驱动程序.包含了官方提供的所有驱动程序,除了aic7888Lib--现在已用得很少的一个AIC-7888 SCSI控制器的驱动介绍.建议重点阅读at ...
- 例说linux内核与应用数据通信系列【转】
转自:http://blog.csdn.net/shallnet/article/details/47865169 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章对您有用,请点击文 ...
- 例说linux内核与应用数据通信系列
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 本系列通过源代码演示样例解说linux内核态与用户态数据通信的各种方式: 例说 ...
- 例说linux内核与应用数据通信(四):映射设备内核空间到用户态
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 一个进程的内存映象由以下几部分组成:代码段.数据段.BSS段和 ...
随机推荐
- Java并发基础知识点详解
1.synchronized与Lock区别 父类有synchtonized,子类调用父类的同步方法,是没办法同步的,因为synchronized不是修饰符,不会被继承下来. synchronized ...
- 【Codeforces】Codeforces Round #373 (Div. 2) -C
C. Efim and Strange Grade Efim just received his grade for the last test. He studies in a special sc ...
- struts2拦截器(四)
struts2拦截器原理: 当请求action时,struts2会查找配置文件,并根据配置实例化相对的 拦截器对象,然后串成一个列表,然后一个一个的调用列表中的拦截器. 比如:某些页面必须登录才可以访 ...
- var _this = this 是干什么的
因为JS可以多层嵌套代码可能下面还可以再嵌一个方法引用this就会变成子方法控制的对象如果需要上级的对象在没有参数的情况下前面前提做了一个临时变量_this可以保存上级对象子方法中就可以用_this来 ...
- Django学习案例一(blog):三. 模型生成数据
1. 什么是模型models Django中以创建类的形式来创建数据表. 在编写代码的过程中,所有对数据库的操作,都是对类和类的对象进行操作. ORM对象关系映射(Object relation ma ...
- 【Oracle】体系结构
1. 理解实例和数据库 ☞ 实例是一组后台进程和共享内存 ☞ 数据库是磁盘上存储的数据集合 ☞ 实例“一生”只能装载并打开一个数据库 ☞ 数据库可以由一个或多个实例(RAC)装载和打开 [oracle ...
- Canopy聚类算法分析
原文链接:http://blog.csdn.net/yclzh0522/article/details/6839643 Canopy聚类算法是可以并行运行的算法,数据并行意味着可以多线程进 ...
- swiper和Navigator组件
<swiper class="index-banner" indicator-dots="{{true}}" autoplay="{{true} ...
- js-url解析函数
//Url解析 function parseURL(url) { var a = document.createElement('a'); a.href = url; return { source: ...
- python中*的用法
在python中,很多情况下会用到*,下面举一些例子来说明*的用法 1.数字计算中,*代表乘法,**代表求幂 print('2乘以3值为:%s'%(2*3)) print('2的3次方值为:%s'%( ...