Kernel module编程(十三):信号量、互斥锁、读写信号量和完成量
来源:互联网 发布:怎么让吊变大 知乎 编辑:程序博客网 时间:2024/05/01 17:55
本文也即《Linux Device Drivers》,LDD3的第五章Concurrency and Race Conditions的读书笔记之二,但我们不限于此内容。 Kernel提供不同的原语来处理不同的情况,最常用的是采用信号量的方式。如果不能获得资源将进入sleep状态,等待资源释放,也即block的方式。通过加锁的原语使之sleep,例如在scull的write()的例子中,kmalloc很适合,但是不是所有情况都适合sleep的方式。信号量是一种sleep机制。它包含一个整数,以及一对函数P和V。进程如果需要进入将调用P,如果信号量的值大于0,那么该值减一,继续执行,如果信号量等于或者小于0,进程将等待其他人释放型号了。Unlock信号量即调用V,它将信号量的值加1,如果可能唤醒正在等待的进程。 如果信号量用户互斥(mutex:mutual exclusion),将信号量的值初始化的1,这样只允许一个进程或者线程执行。这种情况下,信号量也成为互斥锁。在linux kernel中基本上是由于的信号量都是互斥锁。 在scull中,我们已经以互斥锁的方式使用过信号量,在scull_write()中有竞争导致的内存泄漏的危险: if(!dptr->data[s_pos]){ 这里分配了内存空间kmalloc,然后进行copy内容,如果在执行的过程中,有多个进程同时触发了scull_write,就发生竞争,例如A执行第一句判断,当尚未执行第二句kmalloc时,B执行第一句,因此B也需要执行第二句,因此两个进程对data[s_pos]都需要kmalloc空间,并执行copy内容,导致内容混乱是一个方面,A分配的空间,他的指针消失了,我们无法对A的kmalloc进行释放,它将占据内存的一个空间,直至系统关闭,这就是内存泄漏。 信号量的使用例子见kernel module编程(五):设备读写,相关操作已经重点标出,这里不再重复。使用方式如下: 我们需要注意,一定要在获取信号量之前确保已经初始化。在语句先后执行顺序中必须要优先处理。 在scull的例子中,读和写都通过信号量进行保护,防止一起写,也防止在写和读同时操作,这些都是我们应当避免的,当时它同时也不允许两个读的操作一通进行,而这种情况是不会产生危害的。Linuxkernel提供了一个rwsem的特别的信号量用于处理这种情况,允许多个读同时存在以提高程序处理能力。读写信号量在驱动中一般较少使用,但是有时会非常有效。使用方式如下: 读写信号量允许一个写用户或者无限个读用户来获得,写用户将具备优先具备,当一个写试图进入这段关键操作代码时,其他读者都无法获得信号量,必须等待所有的写完成。因此它适合于写操作比较少,且写的过程比较短的情况,不适合存在大量写,这会阻碍读的处理。 一般信号量的的处理会限制在一个函数内,但是有时会函数A的处理的前提条件是函数B,A必须等待B处理后才能继续,可以用信号量来进行处理,但linux kernel提供complete的方式。使用方式如下: 针对我们的例子Scull,其实completion并不是合适的场景,但我们可以通过它来试验一下。我们希望是在scull的读操作之前都先完成一次写操作。 #include <linux/completion.h> int scull_read(... ...){ int scull_write(... ...){ 对于scull0,我们原来在用户空间有一个读写测试例子,将其分为读测试和写测试。当我们调用读测试是,例子sleep,只有调用写测试时,读测试才能继续进行。但是发现,scull_read的使用发现scull的内核模块发生crash,这是因为在wait_for_completion()之前,对一些变量进行赋值,例如dptr,而这些变量在write的时候是发现改变的,因此出现错误,需要在wait_for_completion后面对这些变量进行赋值。这样解决了crash的问题。但是我们发现在写测试后,读测试可以继续进行,但是很快又陷入了等待状态。下面是读测试的有关代码: file = fopen("dev/scull0","r"); 会调用scull_read直至scull_read返回0或者<0为止。因为已经进行了写操作,所以第一次调用返回内容长度,会出现第二次调用。这样会将程序搞得很混乱,实际上我们写测试,因为写的内容少,可以一次写完,也很可能分为多次写。所以在这里加完成量是不合适的,可以在fopen中进行处理,即scull_open中进行处理,例如: int scull_open(....) 这是更为合理的方式。如果complete的位置仍然放在scull_write中,我们试验三种方式。complete_and_exit(),可以要求写测试中写入大量的内容,我们将发现只写了部分的内容(第一次调用scull_write),测试程序就退出。scull并不是个合适的completion的例子,completion可能会引起这样或者那样的问题,需要仔细规划。 相关链接: 信号量(Semaphore)
信号量和互斥锁
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if(dptr->data[s_pos] == NULL)
goto out;
… …
}读写信号量
completion(不知道中文名字,可能是完成量,^_^)
... ...
DECLARE_COMPLETION(comp); //为了试验方便,就不分每个scull都持有一个完成量,本来应当如此
struct scull_qset * dptr;
... ...
printk("scull_read waiting for completed from write function./n");
wait_for_completion(&comp);
printk("scull_read awake for reading,continue ..../n");
... ....
}
... ...
complete(&comp);
// complete_all(&comp);
// complete_and_exit(&comp);
}
... ...
while((len = fread(read_buf,1,512,file)) > 0){
total_len +=len;
printf("%s",read_buf);
memset(read_buf,0,512);
}
{
... ...
if((filp->f_flags & O_ACCMODE) == O_RDONLY){
printk("waiting for completed from write function./n");
wait_for_completion(&comp);
printk("awake for reading,continue ..../n");
}
... ...
}
我的与kernel module有关的文章
- Kernel module编程(十三):信号量、互斥锁、读写信号量和完成量
- linux信号量与完成量
- 内核同步机制之信号量&读写信号量&完成变量
- 可睡眠锁 互斥量、信号量、读写信号量、完成变量
- 锁(二) 信号量 读写信号量 互斥体
- kernel module编程(五):设备读写
- Linux多线程编程(三)互斥锁和信号量编程例子
- 读写信号量
- (十三) 信号量、消息队列和共享内存
- (十三) 信号量、消息队列和共享内存
- 四、读写信号量(rw_semaphore)
- linux驱动中的互斥途径四:信号量、完成量
- 原子操作、信号量、读写信号量和自旋锁
- 原子操作、信号量、读写信号量和自旋锁
- 信号量和事件---信号量
- 信号量、互斥锁,读写锁和条件变量的区别
- 信号量、互斥锁,读写锁和条件变量的区别
- 多线程编程(1)- 信号量,互斥锁
- 字段有效性规则表达式的示例
- SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据
- 吓死我了
- 开发方法:深入理解敏捷开发的常见误区
- 开始圆规正传
- Kernel module编程(十三):信号量、互斥锁、读写信号量和完成量
- 从大飞机核心网络芯片看科研院所的未来
- 痛快
- 生成Poisson泊松随机序列的代码
- PurgeComm()函数--清空缓冲区
- SetCommMask
- Linux下配置IP、DNS和路由
- IP-MAC扫描监视器早已完成
- oracle复习(二) 之扩展 设置数据库为只读