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))
		{
			.....
		}
	}
}

而在驅動層的使用和原理還有待研究:
()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章