Linux驱动编程--阻塞控制

中断屏蔽:

        disable_irq(unsigned int irq);     --关中断
        enable_irq(unsigned int irq);     
--开中断

        local_irq_disable();                   --关全部中断

        local_irq_enable();                    --使能全部中断

 

        local_bh_disable();            --禁止中断底半部
        local_bh_enable();            
--使能中断底半部

        local_irq_save(flags);       --保存cpu中断位信息
        local_irq_restore(x);         --恢复cpu中断位信息


原子操作:

         (整型原子操作)

         atomic_t                                        --原子变量类型

         atomic_set(v,i);                             --设置原子变量为i
         atomic v = ATOMIC_INIT(0);          --定义并初始化v
         atomic_read(v);                            
--读取原子变量的值
         atomic_add(i,v);                            --加i
         atomic_sub(i,v);                             
--减i

         atomic_inc_and_test(v);                --
         atomic_dec_and_test(v);               --
         atomic_sub_and_test(i,v);             --

         atomic_add_return(i,v);
         atomic_sub_return(i,v);
         atomic_inc_return(v);
         atomic_dec_return(v);

         (位原子操作)

         set_bit(nr,addr);                           --置1
         clear_bit(nr,addr);                        --清0
         change_bit(nr,addr);                    --反转
         test_bit(nr,addr);                          --测试
 
         test_and_set_bit(nr, addr);          
         test_and_clear_bit(nr,addr);
         test_and_change_bit(nr,addr);
         (使用原子变量实现设备只能被一个进程打开,或保护临界资源)

static void open(.....)
{
	//防止进程被多个设备打开
	if( !atomic_dec_and_test(v) )
	{
		atomic_inc(v);
	}
}

static void release(....)
{	
	//释放设备
	atomic_inc(v);
}


自旋锁:

         (关于使用自旋锁时要避免使用引起进程调度的操作,例如copy_from_user(), copy_to_user(), kmalloc(), msleep()等有可能导致进程阻塞,所以不能用。具体原因还为明白?)

         spinlock_t  lock;
         spin_lock_init(lock);
         spin_lock(lock);              --没有获得锁则在原地自旋
         spin_trylock(lock);          --没有获得锁则立刻返回假
         spin_unlock(lock);

         (保护临界区资源)      

void fun()
{
	spinlock_t		lock;
	spin_lock_init(lock);
	.....
	spin_lock(lock);
	//临界区,如果临界区较大不适合用自旋锁
	spin_unlock(lock);
	.....
}

         能阻止抢占进程的打扰,但不能防止中断和底半部。需要结合中断屏蔽使用,下面是完整版

         spin_lock_irq(lock)                          = spin_lock(lock) + local_irq_disable();
         spin_unlock_irq(lock)                      = spin_unlock(lock)+ local_irq_enable();
         spin_lock_irqsave(lock,flags)         = spin_lock(lock) + local_irq_save(flags);
         spin_unlock_irqrestore(lock,flags) = spin_unlock(lock) + local_irq_restore(x);
         spin_lock_bh(lock)                         = spin_lock(lock) + local_bh_disable(void);
         spin_unlock_bh(lock)                     = spin_unlock(lock) + local_bh_enable(void);

static int open(...)
{
	//实现设备只能被一个进程打开
	spin_lock_irq(lock);
	if( cnt > 0 )
	{
		spin_unlock_irq(lock);
		return -EBUSY;
	}
	cnt ++;
	spin_unlock_irq(lock);
	......
	return 0;
}

static int release(...)
{
	//释放资源
	spin_lock_irq(lock);
	cnt--;
	spin_unlock_irq(lock);
	.....
	return 0;
}

  
        一般的自旋锁同时锁定读写操作,实际上读操作是允许多个同时进行的。 读写自旋锁允许读的并发访问,禁止写的并发访问,禁止读与写的共同发生。

        rwlock_t lock;                      (读写自旋锁)
        rwlock_init(&lock);

        /** 读锁定*/
        read_lock(lock);
        read_lock_irqsave(lock,flags);
        read_lock_irq(lock);                         = read_lock(lock) + local_irq_disable;
        read_lock_bh(lock);
        /** 读解锁*/
        read_unlock(lock);
        read_unlock_irq(lock)                      = read_unlock(lock) + local_irq_enable;
        read_unlock_irqrestore(lock,flags);
        read_unlock_bh(lock);
        /** 写锁定*/
        write_lock(lock);
        write_lock_irq(lock);
        write_lock_bh(lock);
        write_lock_irqsave(lock,flags);
        write_trylock(lock);
        /** 写解锁*/
        write_unlock(lock);
        write_unlock_bh(lock);
        write_unlock_irq(lock);
        write_unlock_irqrestore(lock,flags);

void example()
{
	rwlock_t	lock;
	rwlock_init( &lock );

	/** 使用读锁*/
	read_lock(lock);
	//临界区资源
	read_unlock(lock);


         /** 使用写锁*/
         write_lock_irqsave( &lock, flag);
         //临界区资源
         write_unlock_irqrestore( &lock, flag);
}


        还有一种RCU(read copy update)的方式。读操作时不加锁,写操作时先对拷贝数据进行写,然后使用回调机制在共享数据空闲时再将修改的数据写回。这对读操作较多的系统比较有利。

        rcu_read_lock();
        rcu_read_lock_bh();

        rcu_read_unlock();
        rcu_read_unlock_bh();

        synchronize_rcu(void);
        synchronize_sched();
 
        call_rcu(struct rcu_head * head,void(* func)(struct rcu_head * rcu));
        call_rcu_bh();

        RCU还提供了一些为 list_head 和 hlist_head的共享数据访问工作的函数。

信号量:

        //信号量会导致睡眠,所以不能在中断上下文使用
       struct semaphore sem;
       sema_init( &sem, 1);
       DECLARE_MUTEX( sem);  = struct semaphore sem = sema_init( temp, 1);
 
       down( &sem);                         --睡眠后不能被信号打断
       down_interruptible( &sem);    --睡眠后可被信号打断
       down_trylock( &sem);            --不成功则直接返回
       up( &sem);

 

static struct semaphore sem;
sema_init( &sem, 1);

static int open(....)
{
	//使设备只能被打开一次
	if( down_trylock( &sem))
	{
		return -EBUSY;
	}
	...
	return 0;
}

static int release(...)
{
         //释放
	up( &sem);
	return 0;
}

完成量:

        struct completion com;
        init_completion( &com);
        DECLARE_COMPLETION( com);     --declare and init a @completion
 
        wait_for_completion( &com);
        complete( &com);                               --rouse a @completion
        complete_all( &com);                         --rouse all @completion

 

互斥体:

       struct mutex mut;
       mutex_init( &mutex);

       mutex_lock( &mut);
       mutex_trylock( &mut);
       mutex_lock_interruptible( &mut);
       mutex_unlock( &mut);

       使用例子:

	struct mutex mut;
	mutex_init( &mut);

	mutex_lock( &mut);
	//....
	mutex_unlock( &mut);

等待队列:

         //定义等待队列
         wait_queue_head_t wq;
         init_waitqueue_head( &wq);
         DECLARE_WAIT_QUEUE_HEAD( wq);
         DECLARE_WAITQUEUE( wait, current);
         //添加队列
         add_wait_queue( &wq, &wait);
         remove_wait_queue( &wq, &wait);
         //相关操作
         schedule( );
         set_current_state( TASK_INTERRUPTIBLE);
         __set_current_state( TASK_RUNNING);
         wake_up( &wq);
         wait_event( wq, condition);

static wait_queue_head_t wq;
void init(.....)    //用来初始化等待队列
{
	init_waitqueue_head( &wq);
}

void  read(.....)
{
	DECLARE_WAITQUEUE( wait, current);   //用来定义一个实体
	......
	add_wait_queue( &wq, &wait);         //添加进队列
	......
	set_current_state( TASK_INTERRUPTIBLE);
	schedule( );                         // 进程退出点 and 回归点
	......
	remove_wait_queue( &wq, &wait);      //从队列里移除
	set_current_state( TASK_RUNNING);
	......
}

void fun(...)
{
	wake_up( &wq);              //唤醒阻塞的进程
}


 

轮询:

 在应用层的使用例子

void AppFun(....)
{
	fd_set	rfds;
	int	fd_1, fd_2;
	fd_1 = open( "/dev/fifo1", O_RDONLY|O_NONBLOCK);
	fd_2 = open( "/dev/fifo2", O_RDONLY|O_NONBLOCK);

	while(1)
	{
		FD_ZERO( &rfds);
		FD_SET( fd_1, &rfds);
		FD_SET( fd_2, &rfds);

		select( MAX(fd_1, fd_2), &rfds, NULL, NULL, NULL);
		if( FD_ISSET( fd_1, &rfds))
		{
			.....
		}
		if( FD_SET( fd_2, &rfds))
		{
			.....
		}
	}
}

而在驱动层的使用和原理还有待研究:
()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值