例说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段和 ...
随机推荐
- Python入门 老司机带你上路
Phthon安装 https://www.python.org/download/releases/2.7.5/ 安装结束还没完,我们还差最后一步:设置环境变量.这是什么东西我暂时先不解释,大家照着做 ...
- Gym - 101981I The 2018 ICPC Asia Nanjing Regional Contest I.Magic Potion 最大流
题面 题意:n个英雄,m个怪兽,第i个英雄可以打第i个集合里的一个怪兽,一个怪兽可以在多个集合里,有k瓶药水,每个英雄最多喝一次,可以多打一只怪兽,求最多打多少只 n,m,k<=500 题解:显 ...
- Run as ant build每次都执行两次-问题解决
在Eclipse里面,运行ant,整个测试流程总是执行两遍,其几天试了下在DOS命令行直接调用ant, 结果发现只执行了一次,并且内存消耗好像也没那么大了,估计是eclipse自己的问题.问题解决了, ...
- mac下配置nginx
nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器,下面我们来了解下nginx的用法. 安装nginx 使用brew安装nginx brew install ...
- C#设置开机启动项、取消开机启动项
如果想你写的程序随系统开机一起启动的话,那么你可以照下面这个方法来做. RunWhenStart(false, Application.ProductName, Application.Startup ...
- Qt无法用UTF-8编辑问题
原因: Windows默认编码格式是GBK. 而QT-各默认版本的编码格式是UTF-8. 解决方法": Windows环境下,Qt Creator,菜单->工具->选项-> ...
- Java+selenium+Firefox/ IE/ Chrome主流浏览器自动化环境搭建
一.java+selenium+firefox 1.环境准备:JDK1.8 2.安装firefox浏览器v59 3.下载驱动:https://github.com/mozilla/geckodrive ...
- 另一个维度:cocos-2d VS vue
最近再看cocos-2d的东西,期间得到了同事和老板的支持,在此感谢.之前一直在做vue网页,现在看游戏cocos-2d这块,刚接触肯定有点不适应.cocos-2d多了很多感念:导演.场景.节点等.这 ...
- Apex语言(五)循环结构
1.循环结构 循环语句允许我们多次执行一个语句或一组语句(重执行语句). 2.while语句 只要给定条件为真,目标语句就会重复执行. [格式] while (循环条件){ 语句; } [流程图] ...
- python时间序列按频率生成日期
有时候我们的数据是按某个频率收集的,比如每日.每月.每15分钟,那么我们怎么产生对应频率的索引呢?pandas中的date_range可用于生成指定长度的DatetimeIndex.我们先看一下怎么生 ...