实验五设备驱动中的并发控制.docx

上传人:b****1 文档编号:2650774 上传时间:2023-05-04 格式:DOCX 页数:18 大小:106.54KB
下载 相关 举报
实验五设备驱动中的并发控制.docx_第1页
第1页 / 共18页
实验五设备驱动中的并发控制.docx_第2页
第2页 / 共18页
实验五设备驱动中的并发控制.docx_第3页
第3页 / 共18页
实验五设备驱动中的并发控制.docx_第4页
第4页 / 共18页
实验五设备驱动中的并发控制.docx_第5页
第5页 / 共18页
实验五设备驱动中的并发控制.docx_第6页
第6页 / 共18页
实验五设备驱动中的并发控制.docx_第7页
第7页 / 共18页
实验五设备驱动中的并发控制.docx_第8页
第8页 / 共18页
实验五设备驱动中的并发控制.docx_第9页
第9页 / 共18页
实验五设备驱动中的并发控制.docx_第10页
第10页 / 共18页
实验五设备驱动中的并发控制.docx_第11页
第11页 / 共18页
实验五设备驱动中的并发控制.docx_第12页
第12页 / 共18页
实验五设备驱动中的并发控制.docx_第13页
第13页 / 共18页
实验五设备驱动中的并发控制.docx_第14页
第14页 / 共18页
实验五设备驱动中的并发控制.docx_第15页
第15页 / 共18页
实验五设备驱动中的并发控制.docx_第16页
第16页 / 共18页
实验五设备驱动中的并发控制.docx_第17页
第17页 / 共18页
实验五设备驱动中的并发控制.docx_第18页
第18页 / 共18页
亲,该文档总共18页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

实验五设备驱动中的并发控制.docx

《实验五设备驱动中的并发控制.docx》由会员分享,可在线阅读,更多相关《实验五设备驱动中的并发控制.docx(18页珍藏版)》请在冰点文库上搜索。

实验五设备驱动中的并发控制.docx

实验五设备驱动中的并发控制

实验五:

设备驱动中的并发控制

1.并发与竞态

并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(raceconditions)。

例如,对于globalmem设备,假设一个执行单元A对其写入3000个字符“a”,而另一个执行单元B对其写入4000个字符“b”,第三个执行单元C读取globalmem的所有字符。

如果执行单元A、B的写操作如图1所示的顺序执行,执行单元C的读操作不会有问题。

但是,如果执行单元A、B如图2所示的顺序执行,而执行单元C又“不合时宜”地读,则会读出3000个“b”。

比图2更复杂、更混乱的并发大量地存在于设备驱动中,只要并发的多个执行单元存在对共享资源的访问,竞态就可能发生。

在Linux内核中,主要的竞态发生于如下几种情况。

1.对称多处理器(SMP)的多个CPU

SMP是一种紧耦合、共享存储的系统模型,它的特点是多个CPU使用共同的系统总线,因此可访问共同的外设和储存器。

2.单CPU内进程与抢占它的进程

Linux2.6内核支持抢占调度,一个进程在内核执行的时候可能被另一高优先级进程打断,进程与抢占它的进程访问共享资源的情况类似于SMP的多个CPU。

3.中断(硬中断、软中断、Tasklet、底半部)与进程之间

中断可以打断正在执行的进程,如果中断处理程序访问进程正在访问的资源,则竞态也会发生。

此外,中断也有可能被新的更高优先级的中断打断,因此,多个中断之间本身也可能引起并发而导致竞态。

上述并发的发生情况除了SMP是真正的并行以外,其他的都是“宏观并行、微观串行”的,但其引发的实质问题和SMP相似。

解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问是指一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问。

访问共享资源的代码区域称为临界区(criticalsections),临界区需要以某种互斥机制加以保护。

中断屏蔽、原子操作、自旋锁和信号量等是Linux设备驱动中可采用的互斥途径,下节将讲解信号量是如何实现对资源的互斥访问的。

2.信号量

信号量(semaphore)是用于保护临界区的一种常用方法。

只有得到信号量的进程才能执行临界区代码,当获取不到信号量时,进程进入休眠等待状态。

Linux系统中与信号量相关的操作主要有如下4种。

1.定义信号量

下列代码定义名称为sem的信号量。

structsemaphoresem;

其中结构体semaphore的定义为:

structsemaphore

{

spinlock_tlock;

unsignedintcount;

structlist_headwait_list;

};

2.初始化信号量

voidsema_init(structsemaphore*sem,intval);

该函数初始化信号量,并设置信号量sem的值为val。

尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。

voidinit_MUTEX(structsemaphore*sem);

该函数用于初始化一个用于互斥的信号量,它把信号量sem的值设置为1,等同于sema_init(structsemaphore*sem,1)。

voidinit_MUTEX_LOCKED(structsemaphore*sem);

该函数也用于初始化一个信号量,但它把信号量sem的值设置为0,等同于sema_init(structsemaphore*sem,0)。

此外,下面两个宏是定义并初始化信号量的“快捷方式”。

DECLARE_MUTEX(name)

DECLARE_MUTEX_LOCKED(name)

前者定义一个名为name的信号量并初始化为1,后者定义一个名为name的信号量并初始化为0。

3.获得信号量

voiddown(structsemaphore*sem);

该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用。

intdown_interruptible(structsemaphore*sem);

该函数功能与down()类似,不同之处为,因为down()而进入睡眠状态的进程不能被信号打断,而因为down_interruptible()而进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这时候函数的返回值非0。

intdown_trylock(structsemaphore*sem);

该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。

它不会导致调用者睡眠,可以在中断上下文使用。

在使用down_interruptible()获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS,如:

if(down_interruptible(&sem))

{

return-ERESTARTSYS;

}

4.释放信号量

voidup(structsemaphore*sem);

该函数释放信号量sem,唤醒等待者。

信号量一般这样被使用,如下所示:

//定义信号量

DECLARE_MUTEX(mount_sem);

down(&mount_sem);//获取信号量,保护临界区

...

criticalsection//临界区

...

up(&mount_sem);//释放信号量

以下代码给出了使用信号量实现设备只能被一个进程打开的例子。

staticDECLARE_MUTEX(xxx_lock);//定义互斥锁

staticintxxx_open(structinode*inode,structfile*filp)

{

...

if(down_trylock(&xxx_lock))//获得打开锁

return-EBUSY;//设备忙

...

return0;/*成功*/

}

staticintxxx_release(structinode*inode,structfile*filp)

{

up(&xxx_lock);//释放打开锁

return0;

}

3.增加并发控制后的globalmem驱动

在globalmem()的读写函数中,由于要调用copy_from_user()、copy_to_user()这些可能导致阻塞的函数,因此使用信号量。

驱动工程师习惯将某设备所使用的自旋锁、信号量等辅助手段也放在设备结构中,因此,可如代码清单1那样修改globalmem_dev结构体的定义,并在模块初始化函数中初始化这个信号量,如代码清单2所示。

1.增加并发控制后的globalmem设备结构体

structglobalmem_dev

{

structcdevcdev;/*cdev结构体*/

unsignedcharmem[GLOBALMEM_SIZE];/*全局内存*/

structsemaphoresem;/*并发控制用的信号量*/

};

2.增加并发控制后的globalmem设备驱动模块加载函数

intglobalmem_init(void)

{

intresult;

dev_tdevno=MKDEV(globalmem_major,0);

/*申请设备号*/

if(globalmem_major)

result=register_chrdev_region(devno,1,"globalmem");

else/*动态申请设备号*/

{

result=alloc_chrdev_region(&devno,0,1,"globalmem");

globalmem_major=MAJOR(devno);

}

if(result<0)

returnresult;

/*动态申请设备结构体的内存*/

globalmem_devp=kmalloc(sizeof(structglobalmem_dev),GFP_KERNEL);

if(!

globalmem_devp)/*申请失败*/

{

result=-ENOMEM;

gotofail_malloc;

}

memset(globalmem_devp,0,sizeof(structglobalmem_dev));

globalmem_setup_cdev(globalmem_devp,0);

init_MUTEX(&globalmem_devp->sem);

return0;

fail_malloc:

unregister_chrdev_region(devno,1);

returnresult;

}

在访问globalmem_dev中的共享资源时,需先获取这个信号量,访问完成后,随即释放这个信号量。

驱动中新的globalmem读、写操作如代码清单3所示。

staticssize_tglobalmem_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos)

{

unsignedlongp=*ppos;

unsignedintcount=size;

intret=0;

structglobalmem_dev*dev=filp->private_data;/*获得设备结构体指针*/

/*分析和获取有效的写长度*/

if(p>=GLOBALMEM_SIZE)

returncount?

-ENXIO:

0;

if(count>GLOBALMEM_SIZE-p)

count=GLOBALMEM_SIZE-p;

if(down_interruptible(&dev->sem))

{

return-ERESTARTSYS;

}

/*内核空间->用户空间*/

if(copy_to_user(buf,(void*)(dev->mem+p),count))

{

ret=-EFAULT;

}

else

{

*ppos+=count;

ret=count;

printk(KERN_INFO"read%dbytes(s)from%d\n",count,p);

}

up(&dev->sem);//释放信号量

returnret;

}

staticssize_tglobalmem_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos)

{

unsignedlongp=*ppos;

unsignedintcount=size;

intret=0;

structglobalmem_dev*dev=filp->private_data;/*获得设备结构体指针*/

/*分析和获取有效的写长度*/

if(p>=GLOBALMEM_SIZE)

returncount?

-ENXIO:

0;

if(count>GLOBALMEM_SIZE-p)

count=GLOBALMEM_SIZE-p;

if(down_interruptible(&dev->sem))//获得信号量

{

return-ERESTARTSYS;

}

/*用户空间->内核空间*/

if(copy_from_user(dev->mem+p,buf,count))

ret=-EFAULT;

else

{

*ppos+=count;

ret=count;

printk(KERN_INFO"written%dbytes(s)from%d\n",count,p);

}

up(&dev->sem);//释放信号量

returnret;

}

代码第16~19和第53~56行用于获取信号量,如果down_interruptible()返回值非0,则意味着其在获得信号量之前已被打断,这时写函数返回-ERESTARTSYS。

代码第33和第67行用于在对临界资源访问结束后释放信号量。

除了globalmem的读写操作之外,如果在读写的同时,另一执行单元执行MEM_CLEARIO控制命令,也会导致全局内存的混乱,因此,globalmem_ioctl()函数也需被重写,如代码清单4所示。

/*ioctl设备控制函数*/

staticintglobalmem_ioctl(structinode*inodep,structfile*filp,unsigned

intcmd,unsignedlongarg)

{

structglobalmem_dev*dev=filp->private_data;/*获得设备结构体指针*/

switch(cmd)

{

caseMEM_CLEAR:

if(down_interruptible(&dev->sem))

{

return-ERESTARTSYS;

}

memset(dev->mem,0,GLOBALMEM_SIZE);

up(&dev->sem);//释放信号量

printk(KERN_INFO"globalmemissettozero\n");

break;

default:

return-EINVAL;

}

return0;

}

实验

1.在实验四中同时打开两个终端,分别执行test程序,观察实验现象,注意命令的执行顺序(以红色数字标注)。

可发现执行的结果跟预期不一致。

2.在/dev/book/globalmem中新建文件globalmen.c。

代码如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#defineGLOBALMEM_SIZE0x1000/*全局内存最大4K字节*/

#defineMEM_CLEAR0x1/*清0全局内存*/

#defineGLOBALMEM_MAJOR150/*预设的globalmem的主设备号*/

staticintglobalmem_major=GLOBALMEM_MAJOR;

/*globalmem设备结构体*/

structglobalmem_dev

{

structcdevcdev;/*cdev结构体*/

unsignedcharmem[GLOBALMEM_SIZE];/*全局内存*/

structsemaphoresem1;/*并发控制用的信号量*/

structsemaphoresem2;/*并发控制用的信号量*/

};

structglobalmem_dev*globalmem_devp;

intglobalmem_open(structinode*inode,structfile*filp)

{

filp->private_data=globalmem_devp;

structglobalmem_dev*dev=filp->private_data;

if(down_interruptible(&dev->sem2))//down_trylock(&dev->sem2)

{

return-EBUSY;

}

return0;

}

intglobalmem_release(structinode*inode,structfile*filp)

{

structglobalmem_dev*dev=filp->private_data;

up(&dev->sem2);

return0;

}

staticintglobalmem_ioctl(structinode*inodep,structfile*filp,unsigned

intcmd,unsignedlongarg)

{

structglobalmem_dev*dev=filp->private_data;

switch(cmd)

{

caseMEM_CLEAR:

if(down_interruptible(&dev->sem1))

{

return-ERESTARTSYS;

}

memset(dev->mem,0,GLOBALMEM_SIZE);

up(&dev->sem1);

printk(KERN_INFO"globalmemissettozero\n");

break;

default:

return-EINVAL;

}

return0;

}

staticssize_tglobalmem_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos)

{

unsignedlongp=*ppos;

unsignedintcount=size;

intret=0;

structglobalmem_dev*dev=filp->private_data;

if(p>=GLOBALMEM_SIZE)

returncount?

-ENXIO:

0;

if(count>GLOBALMEM_SIZE-p)

count=GLOBALMEM_SIZE-p;

if(down_interruptible(&dev->sem1))

{

return-ERESTARTSYS;

}

if(copy_to_user(buf,(void*)(dev->mem+p),count))

{

ret=-EFAULT;

}

else

{

*ppos+=count;

ret=count;

printk(KERN_INFO"read%dbytes(s)from%d\n",count,p);

}

up(&dev->sem1);

returnret;

}

staticssize_tglobalmem_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos)

{

unsignedlongp=*ppos;

unsignedintcount=size;

intret=0;

structglobalmem_dev*dev=filp->private_data;

if(p>=GLOBALMEM_SIZE)

returncount?

-ENXIO:

0;

if(count>GLOBALMEM_SIZE-p)

count=GLOBALMEM_SIZE-p;

if(down_interruptible(&dev->sem1))

{

return-ERESTARTSYS;

}

if(copy_from_user(dev->mem+p,buf,count))

ret=-EFAULT;

else

{

*ppos+=count;

ret=count;

printk(KERN_INFO"written%dbytes(s)from%d\n",count,p);

}

up(&dev->sem1);

returnret;

}

staticconststructfile_operationsglobalmem_fops=

{

.owner=THIS_MODULE,

.read=globalmem_read,

.write=globalmem_write,

.ioctl=globalmem_ioctl,

.open=globalmem_open,

.release=globalmem_release,

};

staticvoidglobalmem_setup_cdev(structglobalmem_dev*dev,intindex)

{

interr,devno=MKDEV(globalmem_major,index);

cdev_init(&dev->cdev,&globalmem_fops);

dev->cdev.owner=THIS_MODULE;

dev->cdev.ops=&globalmem_fops;

err=cdev_add(&dev->cdev,devno,1);

if(err)

printk(KERN_NOTICE"Error%daddingLED%d",err,index);

}

intglobalmem_init(void)

{

intresult;

dev_tdevno=MKDEV(globalmem_major,0);

if(globalmem_major)

result=register_chrdev_region(devno,1,"globalmem");

else

{

result=alloc_chrdev_region(&devno,0,1,"globalmem");

globalmem_major=MAJOR(devno);

}

if(result<0)

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2