linux并行与竞态

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux并行与竞态相关的知识,希望对你有一定的参考价值。

内核态的竞态与并行

中断屏蔽:

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 *semint 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_initmodule_exit在此文件中*/

#include <linux/init.h>

/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/

#include <linux/module.h>

/*定义module_param module_param_array的头文件*/

#include <linux/moduleparam.h>

/*定义module_param module_param_arrayperm的头文件*/

#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


本文出自 “毛散人” 博客,请务必保留此出处http://songmao.blog.51cto.com/11700139/1878568

以上是关于linux并行与竞态的主要内容,如果未能解决你的问题,请参考以下文章

Linux设备驱动基础01之并发与竞态

Linux设备驱动基础01之并发与竞态

临界区与竞态条件

并发与竞态(原子操作)

:并发与竞态

内核的并发和竞态(信号量completion自旋锁)