1.关于并发控制
Linux 中多个进程对共享资源的并发访问,并发访问会导致竞态。
在单 CPU范围内避免竞态的一种方法是在进入临界区之前屏蔽系统的中断。CPU一般都具备屏蔽中断和打开中断的功能,中断屏蔽将使得中断与进程之间的并发不再发生,由于Linux 内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也就得以避免了。
local_irq_disable() //屏蔽中断
...
critical section //临界区
...
local_irq_enable() //开中断
原子操作
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
原子操作驱动
#include
#include
#include
#include /* copy_to_user,copy_from_user */
#include
#include
#include
#include
#include
#include
#define LED_MAJOR 240
//原子变量
atomic_t atomic_led = ATOMIC_INIT(0);
int led_open (struct inode *inode,struct file *filp)
{
unsigned tmp;
//减1兼测试
if (!atomic_dec_and_test(&atomic_led))
{
//加1
atomic_inc(&atomic_led);
return -1;
}
tmp = readl(S3C64XX_GPMDAT);
tmp &= 0xffff0000;
tmp |= 0x00001111;
writel(tmp, S3C64XX_GPMCON);
writel(0xffffffff, S3C64XX_GPMDAT);
printk('<0>open ledn');
return 0;
}
ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
return count;
}
ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
char wbuf[1];
unsigned tmp;
copy_from_user(wbuf,buf,count);
tmp = readl(S3C64XX_GPMDAT);
tmp &= 0xfffffff0;
tmp |= wbuf[0];
writel(tmp, S3C64XX_GPMDAT);
return count;
}
int led_release (struct inode *inode, struct file *filp)
{
atomic_inc(&atomic_led);
return 0;
}
struct file_operations led_fops ={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
int __init led_init (void)
{
int rc;
printk ('<0>led initn');
rc = register_chrdev(LED_MAJOR,'led',&led_fops);
if (rc <0)
{
printk ('<0>register %s char dev errorn','led');
return -1;
}
//atomic_t atomic_led = ATOMIC_INIT(0);
//原子变量置1
atomic_set(&atomic_led, 1);
printk ('<0>successn');
return 0;
}
void __exit led_exit (void)
{
unregister_chrdev(LED_MAJOR,'led');
printk ('<0>module exitn');
return ;
}
module_init(led_init);
module_exit(led_exit);
测试程序
#include
#include 'stdio.h'
#include 'pthread.h'
#include 'stdlib.h'
void thread_one(void)
{
int fd,count;
char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};
while((fd = open('/dev/my_led',O_RDWR))<0)
{
printf ('pthread_one:Open /dev/my_led file errorn');
//pthread_exit(0);
sleep(1);
}
for(count=0;count<3;count++)
{
write(fd,&buf[0],1);
sleep(1);
write(fd,&buf[5],1);
sleep(1);
}
close (fd);
}
void thread_two(void)
{
int fd,count;
char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};
while ((fd = open('/dev/my_led',O_RDWR)) < 0)
{
printf ('pthread_two:Open /dev/my_led file errorn');
//pthread_exit(0);
sleep(1);
}
for(count=0;count<3;count++)
{
write(fd,&buf[1],1);
sleep(1);
write(fd,&buf[5],1);
sleep(1);
}
close(fd);
}
main()
{
pthread_t thread_one_id,thread_two_id;
int resurt;
pthread_attr_t thread_one_attr,thread_two_attr;
int thread_one_priority,thread_two_priority;
thread_one_priority = 11;
thread_two_priority = 11;
pthread_attr_init(&thread_one_attr);
pthread_attr_init(&thread_two_attr);
//设置为指定调度算法
pthread_attr_setinheritsched(&thread_one_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setinheritsched(&thread_two_attr, PTHREAD_EXPLICIT_SCHED);
//指定调度算法
pthread_attr_setschedpolicy(&thread_one_attr, SCHED_RR);
pthread_attr_setschedpolicy(&thread_two_attr, SCHED_RR);
pthread_attr_setschedparam(&thread_one_attr, (struct sched_param *)&thread_one_priority);
pthread_attr_setschedparam(&thread_two_attr, (struct sched_param *)&thread_two_priority);
pthread_attr_setdetachstate(&thread_one_attr,PTHREAD_CREATE_JOINABLE);
//sleep(1);
if((resurt = pthread_create(&thread_one_id, &thread_one_attr, (void *)thread_one, NULL)) == -1)
{
perror('ss');
printf('thread one creat errorn %d',resurt);
exit(1);
}
if((resurt = pthread_create(&thread_two_id, &thread_two_attr, (void *)thread_two, NULL))== -1)
{
printf('thread two creat errorn');
exit(1);
}
//sleep(1);
pthread_join(thread_one_id, NULL);
pthread_join(thread_two_id, NULL);
}
运行结果
自旋锁
自旋锁是一种对临界资源进行互斥手访问的典型手段。为了获得一个自旋锁,在某CPU 上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于它是原子操作,所以在该操作完成之前其他执行单元不可能访问这个内存变量。
自旋锁保护临界段
spinlock(&lock_led);
if (count)/*已经打开*/
{
spin_unlock(&lock_led);
return -1;
}
spin_unlock(&lock_led);
信号量
//获得信号量
void down(struct semaphore * sem);
//释放信号量
void up(struct semaphore * sem);
2 .关于阻塞/非阻塞IO
阻塞和非阻塞I/O 是设备访问的两种不同模式,驱动程序可以灵活地支持用户空间对设备的这两种访问方式。
阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
阻塞一个线程,直至另一个线程运行完。
include
#include
#include
#include /* copy_to_user,copy_from_user */
#include
#include
#include
#include
#include
#include
#include
#include
#define LED_MAJOR 240
struct my_queue
{
//定义等待列对头
wait_queue_head_t queue_head;
//定义自旋锁
spinlock_t lock;
//文件打开标志
int file_flag;
}led_queue;
int led_open (struct inode *inode,struct file *filp)
{
unsigned tmp;
struct my_queue * dev = &led_queue;
//自旋锁 临界段
spin_lock(&dev->lock);
//定义等待列对
DECLARE_WAITQUEUE(wait,current);
//进入等待列对头
add_wait_queue(&dev->queue_head, &wait);
dev->file_flag--;
spin_unlock(&dev->lock);
while(dev->file_flag < 0)
{
//改变进程状态
__set_current_state(TASK_INTERRUPTIBLE);
//
schedule();
}
spin_lock(&dev->lock);
dev->file_flag = 0;
spin_unlock(&dev->lock);
tmp = readl(S3C64XX_GPMDAT);
tmp &= 0xffff0000;
tmp |= 0x00001111;
writel(tmp, S3C64XX_GPMCON);
writel(0xffffffff, S3C64XX_GPMDAT);
printk('<0>open ledn');
//移除
remove_wait_queue(&dev->queue_head, &wait);
return 0;
}
ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
return count;
}
ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
char wbuf[1];
unsigned tmp;
copy_from_user(wbuf,buf,count);
tmp = readl(S3C64XX_GPMDAT);
tmp &= 0xfffffff0;
tmp |= wbuf[0];
writel(tmp, S3C64XX_GPMDAT);
return count;
}
int led_release (struct inode *inode, struct file *filp)
{
struct my_queue * dev = &led_queue;
spin_lock(&dev->lock);
dev->file_flag = 1;
spin_unlock(&dev->lock);
wake_up_interruptible(&dev->queue_head);
return 0;
}
struct file_operations led_fops ={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
int __init led_init (void)
{
int rc;
struct my_queue * dev = &led_queue;
printk ('<0>led initn');
rc = register_chrdev(LED_MAJOR,'led',&led_fops);
if (rc <0)
{
printk ('<0>register %s char dev errorn','led');
return -1;
}
//初始化列对头
init_waitqueue_head(&dev->queue_head);
//初始化自旋锁
spin_lock_init(&dev->lock);
//文件打开标志
dev->file_flag = 1;
printk ('<0>successn');
return 0;