内核态的竞态与并行
中断屏蔽:
local_irq_save(flags)
local_irq_restore(flags)
Telnet 192.168.x.x登录开发板
#if 0
。。。。。。
#endif
原子操作
原子操作指的是在执行过程中不会被别的代码所中断的操作.
分为 位 和 整型变量 两类原子操作。
void atomic_set(atomic_t *v, int i); //设置原子变量v的值为i
atomic_t v = ATOMIC_INIT(0); //定义原子变量v, 并初始化为0 **************************
atomic_read(atomic_t *v); //获得原子变量的值,返回原子变量的值
void atomic_add(int i, atomic_t *v); //原子变量+i
void atomic_sub(int i, atomic_t *v); //原子变量-i
void atomic_inc(atomic_t *v); //原子变量+1 *******************************
void atomic_dec(atomic_t *v); //原子变量-1
对原子变量执行自增,自减和减操作后 ,测试其是否为0,为 0 则返回 true,否则返回 false :
[cpp] view plain copy
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v); ***********************
int atomic_sub_and_test(int i, atomic_t *v);
对原子变量进行加/减,自增/自减操作,并返回新的值:
[cpp] view plain copy
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_sub_return(atomic_t *v);
在代码中怎么添加:
__init中:
for(i=0;i<DEVICE_MINOR_NUM;i++){
my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
memset(my_devices[i].data,0,REGDEV_SIZE);
/*设备注册到系统*/
reg_init_cdev(&my_devices[i],i);
/*创建设备节点*/
device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
atomic_inc(&(my_devices[i].atc));
}
static int chardevnode_open(struct inode *inode, struct file *file)
{
struct reg_dev *reg_devp =NULL;
reg_devp =container_of(inode->i_cdev,struct reg_dev,cdev);
file->private_data=reg_devp;
printk(KERN_EMERG "chardevnode_open is success!\n");
if(!atomic_dec_and_test(&(reg_devp->atc)))
{
printk(KERN_ERR "atomic:device can open only once!");
atomic_inc(&(reg_devp->atc));
return -EBUSY;
}
return 0;
}
static int chardevnode_release(struct inode *inode, struct file *file)
{
struct reg_dev *reg_devp =NULL;
reg_devp=file->private_data;
printk(KERN_EMERG "chardevnode_release is success!\n");
atomic_inc(&(reg_devp->atc));
return 0;
}
自旋锁
自旋:原地打转。最多只有一个持有者
自旋锁不会使进程睡眠,如果一个执行单元
试图获取一个已经被持有的锁,那么该执行单元就会一直忙等待,在这里看自旋锁是否被持有者释放
include/linux/spinlock_types.h。
可以保护临界资源
自旋操作方法
定义
spinlock_t lock
初始化
spin_lock_init(...)
spin_lock
获取锁:
Spin_lock (...) /*如果不成功,则一直自旋等待*/
Spin_trylock(...)/*如果不成功返回一个错误值
操作临界资源
...
释放锁:
Spin_unlock(...)
注意:spin_lock
Spin_unlock 要成对出现,如果连续两次释放锁,可能会导致内核崩溃。
自旋锁保护的资源尽量的短,尽量少发生竞态的情况
读写锁,顺序锁,RCU(读写更新)
remive_wait_queue(,)
在__init中加:
spin_lock_init(&(my_devices[i].lock));
my_devices[i].open_num=0;
在open中加:
spin_lock(&(reg_devp->lock));
if(OPEN_NUM<=reg_devp->open_num){
spin_unlock(&(reg_devp->lock));
printk(KERN_ERR "atomic:device can open over num!");
return -EBUSY;
}
reg_devp->open_num++;
spin_unlock(&(reg_devp->lock));
在release中加:
spin_lock(&(reg_devp->lock));
reg_devp->open_num--;
spin_unlock(&(reg_devp->lock));
信号量
struct semaphore{
spinlock_t lock; //有自旋锁的机制
unsigned int count;
struct list_head wait_list;
};
1.定义信号量
Struct semaphore sem;
2.初始化信号量
Sema_init (*sem,int val)
init_MUTEX //初始化为1
init_MUTEX _LOCKED//初始化为0
DECLARE_MUTEX(name)定义并初始化为1
3.获取信号量(sem-1)
down(。。。)/* 获取信号量不成功会导致进程睡眠*/
down_interruptible(struct semaphore * sem)/*获取信号量不成功会导致进程进入可中断睡眠睡眠 */
down_killable/*不成功 睡眠 可killable的*/
down_trylock /* 不成功直接返回错误号*/
4.访问临界资源
5.释放信号量(sem+1)
up(..)
struct semaphore_waiter {
struct list_head list;
struct task_struct *task;
int up;
};
/*
* Because this function is inlined, the 'state' parameter will be
* constant, and thus optimised away by the compiler. Likewise the
* 'timeout' parameter for the cases without timeouts.
*/
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
struct task_struct *task = current;
struct semaphore_waiter waiter;
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = task;
waiter.up = 0;
for (;;) {
if (signal_pending_state(state, task))
goto interrupted;
if (timeout <= 0)
goto timed_out;
__set_task_state(task, state);
spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout);
spin_lock_irq(&sem->lock);
if (waiter.up)
return 0;
}
信号量和自旋锁的区别
1.信号量的实现肯定使用了自旋锁机制
2.信号量可以有多个持有者,获取信号量不成功时睡眠
自旋锁最多只有一个持有者获取自旋锁不成功时,原地自旋
3.自旋锁保护的代码比较短
信号量保护的代码可以比较长
在 __init中加:
sema_init(&(my_devices[i].sem_open),2);
sema_init(&(my_devices[i].sem_read),1);
sema_init(&(my_devices[i].sem_write),0);
在write中加:
down_interruptible(&(reg_devp->sem_write));
printk(KERN_INFO"chardevnode_write success");
在read中加:
down_interruptible(&(reg_devp->sem_read));
printk(KERN_INFO"chardevnode_read success");
等待队列
作用:
等待队列的数据结构 include/linux/wait.h
wait_bit_queue{
Struct wait_bit_key;
Wait_queue_t wait;
};
Struct __wait_bit_queue_head{ /*头结点*/
Spinlock_t lock;
Struct list_jead task_lisrt;
};
struct __wait_queue { /*一个节点*/
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
1.定义和初始化等待队列头
wait_queue_head_t wqh;
Init_wait_queue(。。。)//初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD() //定义和初始化头结点
DECLARE_WAITQUEUE//定义和初始化一个节点
2.添加,移除等待队列
add_wait_queue(。。。)
remove_wait_queue(。。。)
3.等待事件
#include<linix/sched.h>
Wait_event(queue,condition)//当condition为真时,立刻返回,否则进入TASK_UNINTERRUTBILE并挂在queue指定的链表中
wait_event_killable(...)
wait_event_interroptbile(。。。)
wait_event_tomeout(...)
wait_event_interroptible_timeout(...)
4。唤醒等待队列
wake_up(...)
一般使用方法
1)定义并初始化等待队列,并添加到队列头指向的链表
2)改变进程的状态 sleep
3)通过schedule()主动放弃CPU,调度其他就绪的线程执行
4)被阻塞的进程会被唤醒,将等待队列从链表中删除
DECLARE_WAITQUEUE(,current) current线程关联 他是一个宏 宏里面是 current_thread_info()->task
/*设置当前线程为睡眠状态*/
set_current_state(TASK_INITERRUPTIBLE);
schedule();
wait_event_interruptible(wq,condition)//可以一套带走上面的定义初始化
/**
* wait_event_interruptible - sleep until a condition gets true
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
* @condition evaluates to true or a signal is received.
* The @condition is checked each time the waitqueue @wq is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*
* The function will return -ERESTARTSYS if it was interrupted by a
* signal and 0 if @condition evaluated to true.
*/
#define wait_event_interruptible(wq, condition)\
({\
int __ret = 0;\
if (!(condition))\
__wait_event_interruptible(wq, condition, __ret);\
__ret;\
})
#define __wait_event_interruptible_timeout(wq, condition, ret)\
do {\
DEFINE_WAIT(__wait);\
\
for (;;) {\
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);\
if (condition)\
break;\
if (!signal_pending(current)) {\
ret = schedule_timeout(ret);\
if (!ret)\
break;\
continue;\
}\
ret = -ERESTARTSYS;\
break;\
}\
finish_wait(&wq, &__wait);\
} while (0)
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
__set_current_state(TASK_RUNNING);
/*
* We can check for list emptiness outside the lock
* IFF:
* - we use the "careful" check that verifies both
* the next and prev pointers, so that there cannot
* be any half-pending updates in progress on other
* CPU's that we haven't seen yet (and that might
* still change the stack area.
* and
* - all other users take the lock (ie we can only
* have _one_ other CPU that looks at or modifies
* the list).
*/
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqrestore(&q->lock, flags);
}
}
演示程序:
/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
#include <linux/init.h>
/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
#include <linux/module.h>
/*定义module_param module_param_array的头文件*/
#include <linux/moduleparam.h>
/*定义module_param module_param_array中perm的头文件*/
#include <linux/stat.h>
/*三个字符设备函数*/
#include <linux/fs.h>
/*MKDEV转换设备号数据类型的宏定义*/
#include <linux/kdev_t.h>
/*定义字符设备的结构体*/
#include <linux/cdev.h>
/*分配内存空间函数头文件*/
#include <linux/slab.h>
/*包含函数device_creatchar_driver_ledse 结构体class等头文件*/
#include <linux/device.h>
/*自定义头文件*/
#include "char_driver_leds.h"
/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>
MODULE_LICENSE("Dual BSD/GPL");
/*声明是开源的,没有内核版本限制*/
MODULE_AUTHOR("songmao");
/*声明作者*/
static int led_gpios[] = {
EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
};
#define LED_NUMARRAY_SIZE(led_gpios)
int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;
/*输入主设备号*/
module_param(numdev_major,int,S_IRUSR);
/*输入次设备号*/
module_param(numdev_minor,int,S_IRUSR);
static struct class *myclass;
struct reg_dev *my_devices;
/*打开操作*/
static int chardevnode_open(struct inode *inode, struct file *file)
{
struct reg_dev *reg_devp =NULL;
reg_devp =container_of(inode->i_cdev,struct reg_dev,cdev);
file->private_data=reg_devp;
printk(KERN_EMERG "chardevnode_open is success!\n");
/*if(!atomic_dec_and_test(&(reg_devp->atc)))
{
printk(KERN_ERR "atomic:device can open only once!");
atomic_inc(&(reg_devp->atc));
return -EBUSY;
}*/
spin_lock(&(reg_devp->lock));
if(OPEN_NUM<=reg_devp->open_num){
spin_unlock(&(reg_devp->lock));
printk(KERN_ERR "atomic:device can open over num!");
return -EBUSY;
}
reg_devp->open_num++;
spin_unlock(&(reg_devp->lock));
return 0;
}
/*关闭操作*/
static int chardevnode_release(struct inode *inode, struct file *file)
{
struct reg_dev *reg_devp =NULL;
reg_devp=file->private_data;
printk(KERN_EMERG "chardevnode_release is success!\n");
//atomic_inc(&(reg_devp->atc));
/*spin_lock(&(reg_devp->lock));
reg_devp->open_num--;
spin_unlock(&(reg_devp->lock));*/
up(&(reg_devp->sem_open));
return 0;
}
/*IO操作*/
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
struct reg_dev *reg_devp =NULL;
reg_devp=file->private_data;
switch(cmd)
{
case 0:
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], cmd);
break;
default:
return -EINVAL;
}
printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);
up(&(reg_devp->sem_read));
up(&(reg_devp->sem_write));
return 0;
}
ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)
{
struct reg_dev *reg_devp =NULL;
reg_devp=file->private_data;
down_interruptible(&(reg_devp->sem_read));
printk(KERN_INFO"chardevnode_read success");
return 0;
}
ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops)
{
struct reg_dev *reg_devp =NULL;
reg_devp=file->private_data;
down_interruptible(&(reg_devp->sem_write));
printk(KERN_INFO"chardevnode_write success");
return 0;
}
loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){
return 0;
}
struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = chardevnode_open,
.release = chardevnode_release,
.unlocked_ioctl = chardevnode_ioctl,
.read = chardevnode_read,
.write = chardevnode_write,
.llseek = chardevnode_llseek,
};
/*设备注册到系统*/
static void reg_init_cdev(struct reg_dev *dev,int index){
int err;
int devno = MKDEV(numdev_major,numdev_minor+index);
/*数据初始化*/
cdev_init(&dev->cdev,&my_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_fops;
/*注册到系统*/
err = cdev_add(&dev->cdev,devno,1);
if(err){
printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);
}
else{
printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);
}
}
static int gpio_init(void){
int i=0,ret;
for(i=0;i<LED_NUM;i++){
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i,ret);
return -1;
}
else{
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 1);
}
}
return 0;
}
static int __init scdev_init(void)
{
int ret = 0,i;
dev_t num_dev;
printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);
printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);
if(numdev_major){
num_dev = MKDEV(numdev_major,numdev_minor);
ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
}
else{
/*动态注册设备号*/
ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
/*获得主设备号*/
numdev_major = MAJOR(num_dev);
printk(KERN_EMERG "adev_region req %d !\n",numdev_major);
}
if(ret<0){
printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);
}
myclass = class_create(THIS_MODULE,DEVICE_NAME);
my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
if(!my_devices){
ret = -ENOMEM;
goto fail;
}
memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
/*设备初始化*/
for(i=0;i<DEVICE_MINOR_NUM;i++){
my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
memset(my_devices[i].data,0,REGDEV_SIZE);
/*设备注册到系统*/
reg_init_cdev(&my_devices[i],i);
/*创建设备节点*/
device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
//atomic_inc(&(my_devices[i].atc));
spin_lock_init(&(my_devices[i].lock));
my_devices[i].open_num=0;
sema_init(&(my_devices[i].sem_open),2);
sema_init(&(my_devices[i].sem_read),1);
sema_init(&(my_devices[i].sem_write),0);
printk(KERN_EMERG "scdev_init!\n");
}
ret = gpio_init();
if(ret){
printk(KERN_EMERG "gpio_init failed!\n");
}
/*打印信息,KERN_EMERG表示紧急信息*/
return 0;
fail:
/*注销设备号*/
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
printk(KERN_EMERG "kmalloc is fail!\n");
return ret;
}
static void __exit scdev_exit(void)
{
int i;
printk(KERN_EMERG "scdev_exit!\n");
/*除去字符设备*/
for(i=0;i<DEVICE_MINOR_NUM;i++){
cdev_del(&(my_devices[i].cdev));
/*摧毁设备节点函数d*/
device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
}
/*释放设备class*/
class_destroy(myclass);
/*释放内存*/
kfree(my_devices);
/*释放GPIO*/
for(i=0;i<LED_NUM;i++){
gpio_free(led_gpios[i]);
}
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
}
module_init(scdev_init);
/*初始化函数*/
module_exit(scdev_exit);
/*卸载函数*/
Chardeviceleds.h
#ifndef _CHAR_DRIVER_LEDS_H_
#define _CHAR_DRIVER_LEDS_H_
#ifndef DEVICE_NAME
#define DEVICE_NAME "chardevnode"
#endif
#ifndef DEVICE_MINOR_NUM
#define DEVICE_MINOR_NUM 2
#endif
#ifndef DEV_MAJOR
#define DEV_MAJOR 0
#endif
#ifndef DEV_MINOR
#define DEV_MINOR 0
#endif
#ifndef REGDEV_SIZE
#define REGDEV_SIZE 3000
#endif
#ifndef OPEN_NUM
#define OPEN_NUM 2
#endif
struct reg_dev
{
char *data;
unsigned long size;
atomic_t atc;
struct cdev cdev;
spinlock_t lock;
int open_num;
struct semaphore sem_open;
struct semaphore sem_read;
struct semaphore sem_write;
wait_queue_head_t wqh;
int led;
};
#endif