01情况说明
当共享的资源同时被多个需要这个资源的主体访问时(并发),会产生竞态。这里的主体可能有:中断、另一个进程、另一个cpu。需要用互斥的方式来解决竞态。通俗来说,情况是并发出现竞态(多个主题竞争资源),互斥机制是解决办法,最后是实现了同步(程序按程序员想要的顺序执行)。这篇的互斥机制的所有函数均使用Linux的系统调用(即Linux内核源代码中自带的函数),和多线程中的互斥机制相似,但后者调用的是phread库。
02互斥类型
1.中断屏蔽:仅仅只针对本地cpu
2.原子操作:atomic a++;正常的自增会翻译成3条汇编,如果在中途遇到中断就会出错。
3.自旋锁:spin_lock 自旋:死循环,占死cpu; 在加锁和解锁中间的代码:简单用时少,不允许调度。
4.信号量:sem 二值信号量
5.互斥锁:mutex
02_01使用方式
1.中断屏蔽:
local_irq_disable() //屏蔽中断
local_irq_enable() //开中断
2.原子操作
void atomic_set(atomic_t *v, int i); /*设置原子变量的值为i*/
atomic_t v = ATOMIC_INIT(0); /*定义原子变量v并初始化为0*/
获取原子变量的值
atomic_read(atomic_t *v);/*返回原子变量的值*/原子变量加/减
void atomic_add(int il, atomic_t*v); /*原子变量增加i*/
void atomic sub( int i, atomic_t *v);/*原子变量减少i*/
3.自旋锁
spinlock_t lock; //定义自旋锁
spin_lock_init(&lock) //初始化自旋锁
获得自旋锁
spin_lock(&lock); //如果能够立刻获得锁,它就马上返回,否则它将自旋在那里(阻塞)
spin_trylock(&lock);//非阻塞,如果能立刻获得锁则返回真,否则返回假
释放自旋锁
spin_unlock(&lock);
4.信号量
定义信号量
struct semaphore sem;
初始化信号量
void sema_init(struct semaphore *sem, int val);
信号量的获取
void down(struct semaphore * sem);
int down_interruptible( struct semaphore * sem);
int down_trylock(struct semaphore* sem);
信号量的释放
void up(struct semaphore * sem);
5.互斥锁
初始化
struct mutex my_mutex;mutex_init(&my_mutex);
获取互斥体
void inline _sched mutex_lock(struct mutex *lock);释放互斥体
void_sched mutex_unlock(struct mutex *lock);
使用最多的是自旋锁和互斥锁
03_实例代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
static int fd = -1;
static char wbuf0[16]="1234567890";
static char wbuf1[16]="abcdefghij";
void* fun0(void* arg)
{
while(1)
{
write(fd,wbuf0,16);
}
}
void* fun1(void* arg)
{
while(1)
{
write(fd,wbuf1,16);
}
}
int main()
{
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("fd is %d\n",fd);
return -1;
}
pthread_t t0,t1;
pthread_create(&t0,NULL,fun0,NULL);
pthread_create(&t1,NULL,fun1,NULL);
pthread_detach(t0);
pthread_detach(t1);
while(1)
{
char rbuf[16]={0};
read(fd,rbuf,16);
if(0!=strcmp(rbuf,wbuf0) && 0!=strcmp(rbuf,wbuf1))
printf("rbuf is %s\n",rbuf);
}
close(fd);
return 0;
}
//-lpthread
驱动:在myread和mywrite的copy_to/from_user(本质时memcpy)前后加解自旋锁即可
#include <linux/init.h>
#include <linux/module.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mydev;
static char kbuf[16]={0};
static int ret = -1;
static spinlock_t lock;
int myopen(struct inode *pi,struct file *pf)
{
printk("myopen success\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
printk("myrelease success\n");
return 0;
}
ssize_t myread(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
spin_lock(&lock);
ret = copy_to_user(ubuf,kbuf,len);
spin_unlock(&lock);
if(ret!=0)
return -1;
return len;
}
ssize_t mywrite(struct file pf,char __user *ubuf, size_t len, loff_t *poff)
{
int ret = -1;
spin_lock(&lock);
ret = copy_from_user(kbuf,ubuf,len);
spin_unlock(&lock);
if(ret!=0)
return -1;
return len;
}
static const struct file_operations fops={
.open = myopen,
.release = myrelease,
.read = myread,
.open = myopen,
};
//函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
spin_lock_init(&lock);
printk("init ok\n");
return 0;
}
static void myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("exit ok\n");
return;
}
module_init(myinit);
module_exit(myexit);
MODULE_LICENSE("GPL");
03_02实例2
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
static int fd = -1;
void* fun0(void* arg)
{
int fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("open0 failed");
return -1;
}
printf("open0 ok\n");
sleep(2);
close(fd);
printf("open0 close ok\n");
}
void* fun1(void* arg)
{
sleep(1);
int fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("open1 failed");
return NULL;
}
else
{
printf("open1 ok\n");
close(fd);
}
sleep(2);
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("open1 2 failed");
return NULL;
}
printf("open1 2 ok\n");
}
int main()
{
fd = open("/dev/mydev",O_RDWR);
if(fd<0)
{
printf("fd is %d\n",fd);
return -1;
}
pthread_t t0,t1;
pthread_create(&t0,NULL,fun0,NULL);
pthread_create(&t1,NULL,fun1,NULL);
pthread_detach(t0);
pthread_detach(t1);
while(1);
close(fd);
return 0;
}
//头文件
#include <linux/init.h>
#include <linux/module.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/mutex.h>
#include"cmd.h"
#include<asm/uaccess.h>
#define MA 300
#define MI 0
static dev_t no;
static unsigned count = 1;
static const char * name = "mydev";
static struct cdev mydev;
static int ret = -1;
static struct mutex my_mutex;
int myopen(struct inode *pi,struct file *pf)
{
if(1!=mutex_trylock(&my_mutex))
return -1;
//printk("myopen success\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
mutex_unlock(&my_mutex);
//printk("myrelease success\n");
return 0;
}
static const struct file_operations fops={
.open = myopen,
.release = myrelease,
};
//函数init exit
static int myinit(void)
{
//向上: 内核相关
//1.注册
no = MKDEV(MA,MI);
ret = register_chrdev_region(no,count,name);
if(0!=ret)
return ret;
//2.初始化
cdev_init(&mydev,&fops);
//3.添加
ret = cdev_add(&mydev,no,count);
if(0!=ret)
{
unregister_chrdev_region(no,count);
return ret;
}
mutex_init(&my_mutex);
printk("init ok\n");
return 0;
}
static void myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("exit ok\n");
return;
}
//注册入口函数
module_init(myinit)
module_exit(myexit);
//模块信息描述
MODULE_LICENSE("GPL");
这里由于应用程序有sleep,不能用自旋锁,因为自旋锁中不能有调度。