虚拟字符设备的完整制作过程 模块源代码,应用,编译所有步骤均有详细介绍
来源:互联网 发布:淘宝鱼塘是什么意思 编辑:程序博客网 时间:2024/05/01 02:35
字符设备:设备发送与接收数据是以字符的形式进行;
块设备:是以数据缓冲区的形式进行
虚拟字符设备
驱动部分:
注册设备函数:register_chrdev()
举例: register_chrdev(MAJOR_NUM, " gobalvar ", &gobalvar_fops)
MAJOR_NUM 为主设备号,“gobalvar”为设备名,gobalvar_fops 为包含基本函数入口点的结构体,类型为 file_operations
注销设备函数:unregister_chrdev(MAJOR_NUM, " gobalvar ")
对于字符设备来说,要提供的主要入口有:open ()、release ()、read ()、write ()、ioctl ()、llseek()、poll()等
open()函数 对设备特殊文件进行 open()系统调用时,将调用驱动程序的 open () 函数
int (*open)(struct inode * ,struct file *); 参数 inode 为设备特殊文件的 inode (索引结点) 结构的指针,参数 file 是指向这一
设备的文件结构的指针。
返回状态码(0 表示成功,负数表示存在错误)
举例:
static int globalvar_open(struct inode *inode, struct file *filp)
struct file_operations globalvar_fops =
{
read: globalvar_read,
write: globalvar_write,
open: globalvar_open,
release: globalvar_release,
};
release()函数 当最后一个打开设备的用户进程执行 close ()系统调用时,内核将调用驱
动程序的 release () 函数: void (*release) (struct inode * ,struct file *) ;
release 函数的主要任务是清理未结束的输入/输出操作、释放资源、用户自定义排他标
志的复位等。
举例:
static int globalvar_release(struct inode *inode, struct file *filp)
{
globalvar_count--;
return 0;
}
read()函数 当对设备特殊文件进行 read() 系统调用时,将调用驱动程序 read() 函数:
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
举例:
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t
*off)
{
if (down_interruptible(&sem)) //获取信号量sem,不能被中断打断
{
return - ERESTARTSYS;
}
if (copy_to_user(buf, &global_var, sizeof(int)))
{
up(&sem); //释放信号量sem,唤醒等待者
return - EFAULT;
}
up(&sem); //释放信号量sem,唤醒等待者
return sizeof(int);
}
write( ) 函数 当设备特殊文件进行 write () 系统调用时,将调用驱动程序的 write () 函
数:
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,
loff_t *off)
{
if (down_interruptible(&sem)) //获取信号量sem,不能被中断打断
{
return - ERESTARTSYS;
}
if (copy_from_user(&global_var, buf, sizeof(int)))
{
up(&sem); //释放信号量sem,唤醒等待者
return - EFAULT;
}
up(&sem); //释放信号量sem,唤醒等待者
return sizeof(int);
}
ioctl() 函数 该函数是特殊的控制函数,可以通过它向设备传递控制信息或从设备取得状态信息
llseek()函数 该函数用来修改文件的当前读写位置,并将新位置作为(正的)返回值返回
poll()函数 poll 方法是 poll 和 select 这两个系统调用的后端实现,用来查询设备是否可读或可写,或是否处于某种特殊状态
驱动代码:用信号量来控制每次只能有一个进程访问这个文件
#include <linux/module.h>
#include <linux/init.h>#include <linux/fs.h>
#include <asm/uaccess.h>
//#include <asm/semaphore.h>
MODULE_LICENSE("GPL");
#define MAJOR_NUM 255
static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
static int globalvar_open(struct inode *inode, struct file *filp);
static int globalvar_release(struct inode *inode, struct file *filp);
struct file_operations globalvar_fops =
{ read: globalvar_read, write: globalvar_write, open: globalvar_open, release:
globalvar_release,
};
static int global_var = 0;
static int globalvar_count = 0;
static struct semaphore sem;
static spinlock_t spin = SPIN_LOCK_UNLOCKED;
static int __init globalvar_init(void)
{
int ret;
ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
if (ret)
{
printk("globalvar register failure");
}
else
{
printk("globalvar register success");
init_MUTEX(&sem); //初始化一个互斥锁
}
return ret;
}
static void __exit globalvar_exit(void)
{
//int ret;
//ret =
unregister_chrdev(MAJOR_NUM, "globalvar");
/* if (ret)
{
printk("globalvar unregister failure");
}
else
{
printk("globalvar unregister success");
} */
}
static int globalvar_open(struct inode *inode, struct file *filp)
{
//获得自选锁
spin_lock(&spin); //获得自旋锁,如果立即获得,它将马上返回,如果没有立即获得,则自旋在那里,直到自旋锁的保持者释
//临界资源访问
if (globalvar_count) //借助自旋锁来保护全局变量globalvar_count (记录打开设备的进程数)的访问来实现设备只能被一个进程打开
{
spin_unlock(&spin); //释放自旋锁,与spin_lock搭配使用
return - EBUSY;
}
globalvar_count++;
//释放自选锁
spin_unlock(&spin);
return 0;
}
static int globalvar_release(struct inode *inode, struct file *filp)
{
globalvar_count--;
return 0;
}
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t
*off)
{
if (down_interruptible(&sem)) //获取信号量sem,不能被中断打断
{
return - ERESTARTSYS;
}
if (copy_to_user(buf, &global_var, sizeof(int)))
{
up(&sem); //释放信号量sem,唤醒等待者
return - EFAULT;
}
up(&sem); //释放信号量sem,唤醒等待者
return sizeof(int);
}
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,
loff_t *off)
{
if (down_interruptible(&sem)) //获取信号量sem,不能被中断打断
{
return - ERESTARTSYS;
}
if (copy_from_user(&global_var, buf, sizeof(int)))
{
up(&sem); //释放信号量sem,唤醒等待者
return - EFAULT;
}
up(&sem); //释放信号量sem,唤醒等待者
return sizeof(int);
}
module_init(globalvar_init);
module_exit(globalvar_exit);
Makefile编写:
ifeq ($(KERNELRELEASE),)
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
KERNELDIR ?= /usr/src/linux-headers-$(shell uname -r)/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
obj-m := globalvar.o
endif
编译完了,要加载内核模块:insmod globalvar.ko
要让应用可以访问还要创建节点:mknod /dev/globalvar c 255 0
应用程序编写:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
main()
{
int fd, num;
//打开“/dev/globalvar”
fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != -1 )
{
//初次读 globalvar
read(fd, &num, sizeof(int));
printf("The globalvar is %d\n", num);
//写 globalvar
printf("Please input the num written to globalvar\n");
scanf("%d", &num);
write(fd, &num, sizeof(int));
//再次读 globalvar
read(fd, &num, sizeof(int));
printf("The globalvar is %d\n", num);
//关闭“/dev/globalvar”
close(fd);
}
else
{
printf("Device open failure\n");
}
}
编译应用: gcc -o globalvar.o globalvartest.c
执行应用程序./globalvartest.o
- 虚拟字符设备的完整制作过程 模块源代码,应用,编译所有步骤均有详细介绍
- 完整的android开发环境搭建和源代码编译过程
- 完整的android开发环境搭建和源代码编译过程 .
- VLC 源代码 在linux下编译的完整过程
- 虚拟打印机开发日志(一):使用x64 WIN7编译环境编译的完整步骤
- OGRE源代码编译超详细步骤
- linux设备驱动程序的hello模块编译过程
- linux设备驱动程序的hello模块编译过程
- JDBC访问所有数据库的完整步骤
- JDBC访问所有数据库的完整步骤
- JDBC访问所有数据库的完整步骤
- JDBC访问所有数据库的完整步骤
- JDBC访问所有数据库的完整步骤
- LINUX内核编译步骤详细介绍
- 【转】LINUX内核编译步骤详细介绍
- LINUX内核编译步骤详细介绍
- 图标字体详细制作过程介绍
- 图标字体详细制作过程介绍
- ubuntu各文件目录作用以及启动流程
- pixel clock
- 使用internal(com.android.internal)和hidden(@hide)APIs – Part 1
- 08主界面布局的实现和点击事件的添加
- [LeetCode] Palindrome Partitioning
- 虚拟字符设备的完整制作过程 模块源代码,应用,编译所有步骤均有详细介绍
- 使用internal(com.android.internal)和hidden(@hide)APIs – Part 2
- onselectstart与onselect—禁止选择或禁止复制
- poj 1338 ugly number
- Zookeeper概述(译)
- 使用internal(com.android.internal)和hidden(@hide)APIs – Part 3
- 使用internal(com.android.internal)和hidden(@hide)APIs – Part 4
- Wireshark的过滤规则
- tomcat 类自动加载