Linux字符设备驱动程序之并发控制

1.字符设备与块设备

Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得设备操作犹如文件一般。在应用程序看来,硬件设备就是一个普通的文件,应用程序可以像操作普通文件一样操作设备文件。

设备可分为字符设备与块设备,字符设备是按照字符流顺序访问,如键盘。而块设备是能够随机访问数据片。字符设备驱动程序的访问方式最接近普通文件,而块设备驱动程序是支持文件系统的结构,以文件系统实现与应用程序的连接。应用程序不能直接使用块设备驱动程序。

 

2. 字符设备的接口函数

 

注册字符设备:

int register_chrdev(unsigned int major,const char* name,struct file_operations *fops)

解释:

major是主设备号,当该值为0时,自动运行分配。

name是驱动的名字,出现/proc/devices

fops是file_operations结构

返回值:

如果major为0,正常注册后,返回分配的主设备号。如果分配失败,即有注册的设备,返回-EBUSY. 如果major值大于255,返回-EINVAL. 注册成功后返回0.

 

注销设备

int unregister_chrdev(unsigned int major,const char* name)

解释:

major是主设备号

name是驱动的名字

返回值:若major大于255,返回-EINVAL,正常注销后返回0。

 

file_operations结构体中的一些函数,主要有open(),release,read(),write().ioctl(),llseek,poll().

 

int (*open)(struct inode*,struct file*filp):

解释:

inode为索引节点结构的指针,参数file是指向这一设备文件结构的指针。

返回值:正常返回值为0,否则返回小于0的值。

 

open函数通常的功能是:

检查设备特定的错误(如设备没准备好,或者类似的硬件错误)

如果它第一次打开,初始化设备

如果需要,更新f_op指针

分配并填充要放进filp->private_data的任何数据结构

 

 

int(*release)(struct inode*inode,struct file*filp);

解释:

应用程序关闭设备文件时,终止使用设备驱动程序

inode为索引节点指针,参数file是指向这一设备文件结构的指针。

返回值:正常返回0,与应用程序调用close()函数是相对应的。

 

release函数的功能是:

释放open分配在filp->private_data中的任何东西

在最后的close时关闭设备

 

ssize_t read(struct file*filp, char* buf,size_t count,loff_t *f_pos);

ssize_t write(struct file* filp,const char* buf,size_t count,loff_t * f_pos);

 

 

read函数是应用程序读取设备文件时调用的函数,而write函数是应用程序向设备文件写入数据时调用的函数

 

解释:

filp是文件指针,count是请求数据的大小,buf是用户空间的缓存指针,f_pos是文件位置变量。

 

返回值:函数正常运行时,返回已经处理的数据量,否则返回小于0的值。

 

unsigned long copy_to_user(void__user* to,const void* from,unsigned long count);

unsigned long copy_from_user(void* to,const void__user* from,unsigned long count);

 

copy_to_user是把内核空间的数据写入到用户空间。

copy_from_user是从用户空间把数据写入到内核空间。

 

copy_to_user参数:

to 是用户内存空间的地址。

from是内核内存空间的地址。

count是写入用户空间数据的数量

 

copy_from_user参数:

to是内核空间的地址

from是用户空间的地址

count是写入到内核空间的数据数量

 

返回值: 如果复制成功,则返回0,否则返回没有复制成功的字节数。 

宏get_user(x,ptr)与put_user(x,ptr).

get_user是从ptr指向的用户空间读取x变量大小的数据。

put_user(x,ptr)是将x变量大小的数据写入到ptr所指向的用户空间中去。

 

 

3.并发控制的例子

字符设备驱动文件:

globalsem.c

 #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 1000     //定义主设备号
static int globalvar_open(struct inode*inode,struct file*filp);
static int globalvar_release(struct inode*inode,struct file*filp);
static ssize_t globalvar_read(struct file*,char*,size_t,loff_t *off);
static ssize_t globalvar_write(struct file*,const char*,size_t,loff_t*);
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; //定义信号量
spinlock_t spin=SPIN_LOCK_UNLOCKED; //定义自旋锁,并且初始化自旋锁,也可以用spin_lock_init(&lock)动态初始化自旋锁
static int __init globalvar_init(void){
int ret;
ret=register_chrdev(MAJOR_NUM,"global_var",&globalvar_fops); //如果注册成功则返回0
if(ret){

printk("globalvar register failure");
}

else {
printk("globalvar register success");
 sema_init(&sem,1); //初始化信号量
//spin_lock_init(&spin);
//init_MUTEX(&sem);
}

return 0;
}

static void __exit globalvar_exit(void){
int ret;
ret=unregister_chrdev(MAJOR_NUM,"globalvar"); //注销设备
if(ret){ //如果注销成功则返回0

printk("globalvar unregister failure");

}
else{
printk("globalvar unregister success");

}
}

 

//从设备中读取数据,filp是文件指针,buf是用户空间缓冲区,len是要读取数据的长度,off是文件位置偏移量

static ssize_t globalvar_read(struct file *filp,char*buf,size_t len,loff_t *off){ 
if(down_interruptible(&sem)){ //申请信号量,以任务可被信号中断的方式申请信号量,成功时返回0
 return -ERESTARTSYS;
}

if(copy_to_user(buf,&global_var,sizeof(int))){ //将数据复制到用户空间
 up(&sem);
 return -EFAULT;

}

up(&sem);
return sizeof(int);


}

static ssize_t globalvar_write(struct file* filp,char*buf,size_t len,loff_t *off){
if(down_interruptible(&sem)){
  return -ERESTARTSYS;
}

if(copy_from_user(&global_var,buf,sizeof(int))){
  up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(int);
}

//应用程序中调用open函数就会调用驱动程序中的open函数

static int globalvar_open(struct inode*inode,struct file*filp){
spin_lock(&spin);//获得自旋锁
if(globalvar_count){//通过globalvar_count对打开的设备进行控制,某一时刻,只能有一个进程打开设备

 spin_unlock(&spin);//释放自旋锁
return -EBUSY;

}
globalvar_count++;
spin_unlock(&spin);
return 0;
}

//应用程序中调用close函数就会调用此release函数

static int globalvar_release(struct inode*inode,struct file*filp){
globalvar_count--;
return 0;

}

 

 

测试文件:

 

 globalvartest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main(){
int fd,num;
fd=open("/dev/globalvar",O_RDWR,S_IRUSR|S_IWUSR);
if(fd!=-1){
read(fd,&num,sizeof(int));
printf("the globalvar is%d/n",num);
printf("please input the num written to globalvar/n");
scanf("%d",&num);
write(fd,&num,sizeof(int));
read(fd,&num,sizeof(int));
printf("the globalvar is %d/n",num);
close(fd);

}
else{
printf("device open failure/n");

}

 

}

 

 

测试方法:

(1)将globalsem.c与globalvaltest.c文件放到/usr/src/kernels/linux-2.6.20/drivers/char目录下。

并在Makefile文件中添加obj-m  +=globalsem.o

(2)返回到/usr/src/kernels/linux-2.6.20下make.

(3)插入模块insmod globalsem.ko,然后 mknod /dev/globalvar c 200 0

200是major值。

(4)编译globalvartest, gcc -o globalvartest globalvartest.c

(5)运行两个globalvartest.c 进行测试,发现会出现device open failure错误。

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值