天天看点

内核同步机制

linux 内核为了避免共享数据(临界区)访问冲突,提出了一些控制机制:原子量,自旋锁,信号量

原子量:

  • 原子操作:cpu 执行某个操作过程中,不可被外界打断或中断
  • 原子变量:原子量的运算过程不可被中断
  • 如何使用原子变量:

    1.定义原子量 :atomic_t xxx

    2.原子量操作函数:

atomic_set(&v, i)                                   //初始化
atomic_read(v)                                      //读
atomic_add(int i, volatile atomic_t *v)             //加,无返回值
atomic_sub(int i, volatile atomic_t *v)             //减,无返回值
int atomic_add_return(int i, volatile atomic_t *v)  //加
int atomic_sub_return(int i, volatile atomic_t *v)  //减
int atomic_cmpxchg(atomic_t *v, int old, int new)   //交换
atomic_inc(v)                                       //自加
atomic_dec(v)                                       //自减
           

自旋锁

如果一个进程给临界区加锁,后来的进程加速哦是加锁不成功,会发生阻塞,只到临界区被解锁,后来的进程解除阻塞,加锁成功,继续运行

  • 如何使用自旋锁

    1.定义自旋锁 :spinlock_t xxx

    2.初始化自旋锁:spin_lock_init(_lock)

    3.临界区前加锁:static inline void spin_lock(spinlock_t *lock)

    4.临界区后解锁:static inline void spin_unlock(spinlock_t *lock)

信号量

主要用于进程间同步,本质上是一个计数器,每当进程访问共享资源时,信号量做减一操作,如果信号量为0,后来的进程在做减一操纵不成功,会发生阻塞,知道信号量大于零,才解除阻塞,减一成功,继续执行

  • 如何使用信号量

    1.定义信号量:struct semaphore xxx

    2.初始化信号量:sema_init(struct semaphore *sem, int val)

    3.减一操作: down(struct semaphore *sem)

    4.加一操作:up(struct semaphore *sem)

示例代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#define GPM4CON 0x110002E0
volatile unsigned long *baseaddr = ;
#define rGPM4CON (*((volatile unsigned long *)(baseaddr + 0)))
#define rGPM4DAT (*((volatile unsigned long *)(baseaddr + 1)))

#define MYLED_MAJOR 0
#define MYLED_NAME "led"
#define MYLED_DRVNUM 1
#define MYLED_NUM 4

dev_t myled_devt;       //设备号
int myled_major;
struct class *myled_class;      //设备结构体
struct cdev *myled;             //设备驱动核心结构

//atomic_t myled_atomic;  //原子操作
/*
//自旋锁操作
int count;
struct spinlock myled_spinlock;
*/

struct semaphore myled_semaphore;

char led_status[MYLED_NUM]={-,-,-,-};

loff_t led_lseek (struct file *fp, loff_t off, int whence)
{
    loff_t newoff=;
    switch(whence)
    {
        case SEEK_SET:
            newoff=off;
            break;
        case SEEK_CUR:
            newoff = fp->f_pos+off;
            break;
        case SEEK_END:
            newoff =  + off;
        default:
            return -EINVAL;
    }
    if(newoff<)
        newoff = ;
    else if(newoff>)
        newoff = ;
    fp->f_pos=newoff;
    return newoff;
}

ssize_t led_read (struct file *fp, char __user *buf, size_t size, loff_t *off)
{
    int i=;
    unsigned long ret;
    if(*off>)
        return -EINVAL;
    if(*off+size>)
        size = -*off;
    for(i=;i<MYLED_NUM;i++)
        led_status[i]= !(rGPM4DAT&(<<i));

    ret = copy_to_user(buf,&led_status[*off],size);

    *off = *off+size;
    printk("led_read is called\n");
    return ;
}

ssize_t led_write (struct file *fp, const char __user *buf, size_t size, loff_t *off)
{
    int i;
    unsigned long temp;
    if(*off>)
        return -EINVAL;
    if(*off + size >)
        size =  - *off;
    temp = copy_from_user(&led_status[*off],buf,size);
    for(i=;i<MYLED_NUM;i++)
    {
        if(led_status[i] == )
            rGPM4DAT |= (<<i);
        else if(led_status[i] == )
            rGPM4DAT &= ~(<<i);
        else
            return -EINVAL;
    }
    *off = *off + size;
    printk("led_write is called \n");
    return ;
}

int led_open (struct inode *node, struct file *fp)
{
    int i=;

/*  //原子操作
    if(myled_atomic.counter)
    {
        printk("this derive is being used \n");
        return -EINVAL;
    }
    atomic_inc(&myled_atomic);
*/
/*  //自旋锁
    spin_lock(&myled_spinlock);
    if(count)
    {
        printk("this device is being used \n");
        spin_unlock(&myled_spinlock);
        return -EINVAL;
    }
    count++;
    spin_unlock(&myled_spinlock);
*/

    //信号量
     down(&myled_semaphore);

    fp->private_data =(void *) MINOR(node->i_rdev);
    rGPM4CON &= ~();
    rGPM4CON |= ();
    for(i=;i<MYLED_NUM;i++)
    {
        led_status[i] = !(rGPM4DAT&(<<i));
    }
    printk("led_open is called \n");
    return ;
}

int led_close (struct inode *node, struct file *fp)
{
    printk("led_close is called \n");
/*  //原子操作
    if(myled_atomic.counter)
        atomic_dec(&myled_atomic);
*/
/*  //自旋锁
    spin_lock(&myled_spinlock);
    if(count)
        count--;
    spin_unlock(&myled_spinlock);
*/

    //信号量
    up(&myled_semaphore);

    return ;
}
long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
    int i=;
    if(_IOC_TYPE(cmd) != 'x')
        return -EINVAL;
    switch (_IOC_DIR(cmd))
    {
        case _IOC_READ:
            for(i=;i<MYLED_NUM;i++)
            {
                led_status[i] =! (rGPM4DAT&(<<i));
            }
            if(copy_to_user((long*)arg,led_status,))
                return -EINVAL;
            break;
        case  _IOC_WRITE:
            if(_IOC_NR(cmd)==)
                rGPM4DAT |= (<<arg);
            else if(_IOC_NR(cmd) == )
                rGPM4DAT &= ~(<<arg);
            else
                return -EINVAL;
            break;
        case (_IOC_WRITE|_IOC_READ):
        {
            for(i=;i<MYLED_NUM;i++)
                led_status[i]=! (rGPM4DAT&(<<i));
            switch (_IOC_NR(cmd))
            {
                case :
                    rGPM4DAT |= (<<*((int *)arg));
                    break;
                case :
                    rGPM4DAT &= ~(<<*((int *)arg));
                    break;
                default :
                    return -EINVAL;
                    break;
            }
            if(copy_to_user((long*)arg,led_status,MYLED_NUM))
                return -EINVAL;
            break;
        }
        case _IOC_NONE:
            break;
        default:
            break;
    }
    return ;
}



struct file_operations led_fops=
{
    .owner=THIS_MODULE,
    .open =led_open,
    .release = led_close,
    .read=led_read,
    .write = led_write,
    .unlocked_ioctl= led_ioctl,
    .llseek = led_lseek,
};


static int __init myled_init(void)
{
    int i=;
    //原子操作
//  atomic_set(&myled_atomic,0);

/*  //自旋锁
    spin_lock_init(&myled_spinlock);
    count=0;
*/
    //信号量
    sema_init(&myled_semaphore,);

#if MYLED_MAJOR
    myled_major = MYLED_MAJOR;
    myled_devt = MKDEV(myled_major,);
    if(register_chrdev_region(myled_devt,MYLED_DRVNUM,MYLED_NAME))
#else
    if(alloc_chrdev_region(&myled_devt,,MYLED_DRVNUM,MYLED_NAME))
#endif
    {
        printk("注册失败\n");
        return -EBUSY;
    }
    myled = cdev_alloc();
    cdev_init(myled,&led_fops);
    if(cdev_add(myled,myled_devt,MYLED_DRVNUM))
        {
        printk("注册成功\n");
        return -EBUSY;
    }
    printk("注册成功\n");
    myled_class = class_create(THIS_MODULE,MYLED_NAME);
    for(i=;i<MYLED_DRVNUM;i++)
        device_create(myled_class,NULL,myled_devt+i,NULL,"myled%d",i);

    baseaddr = ioremap(GPM4CON, );

    return ;
}

static void __exit myled_exit(void)
{
    iounmap(baseaddr);
    device_destroy(myled_class, myled_devt);
    class_destroy(myled_class);
    cdev_del(myled);
    unregister_chrdev_region(myled_devt,MYLED_DRVNUM);
    printk("注销成功\n");
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("MOMO");