字符设备驱动程序之按键——同步互斥阻塞
生活随笔
收集整理的這篇文章主要介紹了
字符设备驱动程序之按键——同步互斥阻塞
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
我們知道在之前的應(yīng)用程序中,如果我們同時(shí)運(yùn)行兩次應(yīng)用程序的話(huà),則兩次都可以同時(shí)打開(kāi)設(shè)備,這就是說(shuō)我們的按鍵資源同時(shí)被兩個(gè)進(jìn)程使用。顯然這不是我們想要的,那么下面我們就要引入互斥的概念。
關(guān)于互斥其實(shí)其實(shí)現(xiàn)很簡(jiǎn)單,就是采用一些標(biāo)志,當(dāng)文件被一個(gè)進(jìn)程打開(kāi)后,就會(huì)設(shè)置該標(biāo)志,使其他進(jìn)程無(wú)法打開(kāi)設(shè)備文件。下面,我們就完全靠自己去實(shí)現(xiàn)一個(gè)互斥,代碼修改如下:
首先定義一個(gè)全局變量: static int canread=1; 然后在open函數(shù)開(kāi)始處加入如下代碼: ? if(--canread!=0) ? ? ? ? { ? ? ? ? ?? ? ? ? ? ? ? canread++; ? ? ? ? ? ? return -EBUSY; ? ? ? ? } 在close函數(shù)里面加入如下代碼: anread++; 我們可以先分析一下,當(dāng)進(jìn)程A打開(kāi)設(shè)備文件時(shí),因?yàn)槌跏贾礳anread=1,canread減1后正好等于0,不會(huì)出錯(cuò)返回,成功打開(kāi)了。此時(shí)當(dāng)進(jìn)程B再來(lái)打開(kāi)該設(shè)備文件時(shí),因canread=0,canread減1不為0,所以會(huì)出錯(cuò)出錯(cuò)返回,且canread加1,canread=0。當(dāng)進(jìn)程A關(guān)閉后,canread加1,canread=1;這時(shí)如果B進(jìn)程運(yùn)行,會(huì)成功打開(kāi)設(shè)備文件。看上去似乎完美得實(shí)現(xiàn)了互斥,其實(shí)這其中是有問(wèn)題的。因?yàn)槠鋵?shí)對(duì)于: ?--canread! 這行代碼來(lái)說(shuō),它分為3的步驟,包括:讀取canread值,修改canread值,寫(xiě)回canread的值。 但是不要忘記了,我們的linux系統(tǒng)是一個(gè)多任務(wù)的系統(tǒng),假如當(dāng)A讀取了canread的值為1時(shí),還沒(méi)來(lái)得及減1,就已經(jīng)切換到了進(jìn)程B,進(jìn)程B讀出的canread的值也是1,這樣的話(huà),B就成功打開(kāi)了設(shè)備文件。如果這時(shí)再切換回了A,因?yàn)橹癆已經(jīng)讀出了canread為1,這時(shí)會(huì)將canread當(dāng)作1來(lái)進(jìn)行后續(xù)處理,也可以打開(kāi)文件。這可不是我們想要的,那么有沒(méi)有其他辦法呢?答案是肯定的,下面來(lái)介紹兩種方法:
方法一:原子操作 定義:原子操作指的是在執(zhí)行過(guò)程中不會(huì)被別的代碼路徑所中斷的操作。 下面是幾個(gè)常用原子操作函數(shù): atomic_t v = ATOMIC_INIT(0); ? ? //定義原子變量v并初始化為0 atomic_read(atomic_t *v); ? ? ? ?//返回原子變量的值 void atomic_inc(atomic_t *v); ? ?//原子變量增加1 void atomic_dec(atomic_t *v); ? ?//原子變量減少1 int atomic_dec_and_test(atomic_t *v); //自減操作后測(cè)試其是否為0,為0則返回true,否則返回false。 修改代碼:
首先定義全局原子變量: static atomic_t canread= ATOMIC_INIT(1); 然后在open函數(shù)開(kāi)始處加入如下代碼: ?if(!atomic_dec_and_test(&canread)) ? ? ? ? { ? ? ? ? ?? ? ? ? ? ? ? atomic_inc(&canread); ? ? ? ? ? ? return -EBUSY; ? ? ? ? }
在close函數(shù)里面加入如下代碼: atomic_inc(&canread);
方法二:信號(hào)量 定義:信號(hào)量(semaphore)是用于保護(hù)臨界區(qū)的一種常用方法,只有得到信號(hào)量的進(jìn)程才能執(zhí)行臨界區(qū)代碼。當(dāng)獲取不到信號(hào)量時(shí),進(jìn)程進(jìn)入休眠等待狀態(tài)。 下面是幾個(gè)常用的信號(hào)量相關(guān)的函數(shù):
2. 信號(hào)量 信號(hào)量(semaphore)是用于保護(hù)臨界區(qū)的一種常用方法,只有得到信號(hào)量的進(jìn)程才能執(zhí)行臨界區(qū)代碼。 當(dāng)獲取不到信號(hào)量時(shí),進(jìn)程進(jìn)入休眠等待狀態(tài)。 定義信號(hào)量 struct semaphore sem; 初始化信號(hào)量 void sema_init (struct semaphore *sem, int val); void init_MUTEX(struct semaphore *sem);//初始化為0 static DECLARE_MUTEX(button_lock);?? ? //定義互斥鎖 獲得信號(hào)量 void down(struct semaphore * sem); int down_interruptible(struct semaphore * sem);? int down_trylock(struct semaphore * sem); 釋放信號(hào)量 void up(struct semaphore * sem); 修改代碼: 首先定義全局互斥鎖: static DECLARE_MUTEX(button_lock); 在open函數(shù)中獲得信號(hào)量: down(&button_lock); 在close函數(shù)中釋放信號(hào)量:
up(&button_lock); 我們測(cè)試時(shí)第一次運(yùn)行應(yīng)用程序,發(fā)現(xiàn)其狀態(tài)時(shí)S,第二次運(yùn)行,其狀態(tài)時(shí)D,S表示睡眠狀態(tài),是因?yàn)闆](méi)有按鍵按下而產(chǎn)生睡眠,而D表示一種僵死狀態(tài),或者也可以說(shuō)是一種睡眠狀態(tài),不過(guò)它是因?yàn)闆](méi)有獲取信號(hào)量而進(jìn)入僵死。那么第二個(gè)進(jìn)程什么時(shí)候才能從僵死狀 態(tài)喚醒呢?那就要等到第一個(gè)進(jìn)程釋放信號(hào)量。我們將第一個(gè)進(jìn)程殺死,發(fā)現(xiàn)第二個(gè)進(jìn)程的狀態(tài)變成了S,從而驗(yàn)證了我們的說(shuō)法。
下面我們?cè)賮?lái)談一下阻塞和非阻塞的問(wèn)題: 阻塞操作:是指在執(zhí)行設(shè)備操作時(shí)若不能獲得資源則掛起進(jìn)程,直到滿(mǎn)足可操作的條件后再進(jìn)行操作。被掛起的進(jìn)程進(jìn)入休眠狀態(tài),被從調(diào)度器的運(yùn)行隊(duì)列移走,直到等待的條件被滿(mǎn)足。 非阻塞操作:進(jìn)程在不能進(jìn)行設(shè)備操作時(shí)并不掛起,它或者放棄,或者不停地查詢(xún),直至可以進(jìn)行操作為止。 我們?cè)隍?qū)動(dòng)程序中修改代碼: 在open函數(shù)中加入如下代碼: if (file->f_flags & O_NONBLOCK) { if (down_trylock(&button_lock)) return -EBUSY; } else { down(&button_lock); } 我們?cè)趓ead函數(shù)中加入如下代碼: if (filp->f_flags & O_NONBLOCK) { if (!ev_press) ?//如果沒(méi)有按鍵按下返回 return -EAGAIN; } else { wait_event_interruptible(button_waitq, ev_press);//這一句要將本來(lái)的注釋掉 } 將ev_press相關(guān)的定義添加上 添加定義:static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 在中斷中補(bǔ)上這一句:wake_up_interruptible(&button_waitq);
測(cè)試程序如下: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h>
int main(int argc, char **argv) { ? ? ? ? int fd; ? ?? unsigned char key_val; ? ? ? fd = open("/dev/keys", O_RDWR); if (fd < 0) { printf("can't open!\n"); }
while (1) { ?? ?read(fd, &key_val, 1); ?? ?printf("key_val: 0x%x\n", key_val); } return 0; } 默認(rèn)情況下是阻塞方式打開(kāi)的,運(yùn)行測(cè)試程序,當(dāng)我們沒(méi)有按下按鍵時(shí)會(huì)休眠,但不會(huì)返回。 我們將: fd = open("/dev/keys", O_RDWR);改為 fd = open("/dev/keys", O_RDWR |?O_NONBLOCK); 會(huì)變成非阻塞方式打開(kāi),這時(shí)沒(méi)有按鍵按下會(huì)返回。為方便測(cè)試,我們?cè)谘h(huán)里加入:sleep(5);
首先定義一個(gè)全局變量: static int canread=1; 然后在open函數(shù)開(kāi)始處加入如下代碼: ? if(--canread!=0) ? ? ? ? { ? ? ? ? ?? ? ? ? ? ? ? canread++; ? ? ? ? ? ? return -EBUSY; ? ? ? ? } 在close函數(shù)里面加入如下代碼: anread++; 我們可以先分析一下,當(dāng)進(jìn)程A打開(kāi)設(shè)備文件時(shí),因?yàn)槌跏贾礳anread=1,canread減1后正好等于0,不會(huì)出錯(cuò)返回,成功打開(kāi)了。此時(shí)當(dāng)進(jìn)程B再來(lái)打開(kāi)該設(shè)備文件時(shí),因canread=0,canread減1不為0,所以會(huì)出錯(cuò)出錯(cuò)返回,且canread加1,canread=0。當(dāng)進(jìn)程A關(guān)閉后,canread加1,canread=1;這時(shí)如果B進(jìn)程運(yùn)行,會(huì)成功打開(kāi)設(shè)備文件。看上去似乎完美得實(shí)現(xiàn)了互斥,其實(shí)這其中是有問(wèn)題的。因?yàn)槠鋵?shí)對(duì)于: ?--canread! 這行代碼來(lái)說(shuō),它分為3的步驟,包括:讀取canread值,修改canread值,寫(xiě)回canread的值。 但是不要忘記了,我們的linux系統(tǒng)是一個(gè)多任務(wù)的系統(tǒng),假如當(dāng)A讀取了canread的值為1時(shí),還沒(méi)來(lái)得及減1,就已經(jīng)切換到了進(jìn)程B,進(jìn)程B讀出的canread的值也是1,這樣的話(huà),B就成功打開(kāi)了設(shè)備文件。如果這時(shí)再切換回了A,因?yàn)橹癆已經(jīng)讀出了canread為1,這時(shí)會(huì)將canread當(dāng)作1來(lái)進(jìn)行后續(xù)處理,也可以打開(kāi)文件。這可不是我們想要的,那么有沒(méi)有其他辦法呢?答案是肯定的,下面來(lái)介紹兩種方法:
方法一:原子操作 定義:原子操作指的是在執(zhí)行過(guò)程中不會(huì)被別的代碼路徑所中斷的操作。 下面是幾個(gè)常用原子操作函數(shù): atomic_t v = ATOMIC_INIT(0); ? ? //定義原子變量v并初始化為0 atomic_read(atomic_t *v); ? ? ? ?//返回原子變量的值 void atomic_inc(atomic_t *v); ? ?//原子變量增加1 void atomic_dec(atomic_t *v); ? ?//原子變量減少1 int atomic_dec_and_test(atomic_t *v); //自減操作后測(cè)試其是否為0,為0則返回true,否則返回false。 修改代碼:
首先定義全局原子變量: static atomic_t canread= ATOMIC_INIT(1); 然后在open函數(shù)開(kāi)始處加入如下代碼: ?if(!atomic_dec_and_test(&canread)) ? ? ? ? { ? ? ? ? ?? ? ? ? ? ? ? atomic_inc(&canread); ? ? ? ? ? ? return -EBUSY; ? ? ? ? }
在close函數(shù)里面加入如下代碼: atomic_inc(&canread);
方法二:信號(hào)量 定義:信號(hào)量(semaphore)是用于保護(hù)臨界區(qū)的一種常用方法,只有得到信號(hào)量的進(jìn)程才能執(zhí)行臨界區(qū)代碼。當(dāng)獲取不到信號(hào)量時(shí),進(jìn)程進(jìn)入休眠等待狀態(tài)。 下面是幾個(gè)常用的信號(hào)量相關(guān)的函數(shù):
2. 信號(hào)量 信號(hào)量(semaphore)是用于保護(hù)臨界區(qū)的一種常用方法,只有得到信號(hào)量的進(jìn)程才能執(zhí)行臨界區(qū)代碼。 當(dāng)獲取不到信號(hào)量時(shí),進(jìn)程進(jìn)入休眠等待狀態(tài)。 定義信號(hào)量 struct semaphore sem; 初始化信號(hào)量 void sema_init (struct semaphore *sem, int val); void init_MUTEX(struct semaphore *sem);//初始化為0 static DECLARE_MUTEX(button_lock);?? ? //定義互斥鎖 獲得信號(hào)量 void down(struct semaphore * sem); int down_interruptible(struct semaphore * sem);? int down_trylock(struct semaphore * sem); 釋放信號(hào)量 void up(struct semaphore * sem); 修改代碼: 首先定義全局互斥鎖: static DECLARE_MUTEX(button_lock); 在open函數(shù)中獲得信號(hào)量: down(&button_lock); 在close函數(shù)中釋放信號(hào)量:
up(&button_lock); 我們測(cè)試時(shí)第一次運(yùn)行應(yīng)用程序,發(fā)現(xiàn)其狀態(tài)時(shí)S,第二次運(yùn)行,其狀態(tài)時(shí)D,S表示睡眠狀態(tài),是因?yàn)闆](méi)有按鍵按下而產(chǎn)生睡眠,而D表示一種僵死狀態(tài),或者也可以說(shuō)是一種睡眠狀態(tài),不過(guò)它是因?yàn)闆](méi)有獲取信號(hào)量而進(jìn)入僵死。那么第二個(gè)進(jìn)程什么時(shí)候才能從僵死狀 態(tài)喚醒呢?那就要等到第一個(gè)進(jìn)程釋放信號(hào)量。我們將第一個(gè)進(jìn)程殺死,發(fā)現(xiàn)第二個(gè)進(jìn)程的狀態(tài)變成了S,從而驗(yàn)證了我們的說(shuō)法。
下面我們?cè)賮?lái)談一下阻塞和非阻塞的問(wèn)題: 阻塞操作:是指在執(zhí)行設(shè)備操作時(shí)若不能獲得資源則掛起進(jìn)程,直到滿(mǎn)足可操作的條件后再進(jìn)行操作。被掛起的進(jìn)程進(jìn)入休眠狀態(tài),被從調(diào)度器的運(yùn)行隊(duì)列移走,直到等待的條件被滿(mǎn)足。 非阻塞操作:進(jìn)程在不能進(jìn)行設(shè)備操作時(shí)并不掛起,它或者放棄,或者不停地查詢(xún),直至可以進(jìn)行操作為止。 我們?cè)隍?qū)動(dòng)程序中修改代碼: 在open函數(shù)中加入如下代碼: if (file->f_flags & O_NONBLOCK) { if (down_trylock(&button_lock)) return -EBUSY; } else { down(&button_lock); } 我們?cè)趓ead函數(shù)中加入如下代碼: if (filp->f_flags & O_NONBLOCK) { if (!ev_press) ?//如果沒(méi)有按鍵按下返回 return -EAGAIN; } else { wait_event_interruptible(button_waitq, ev_press);//這一句要將本來(lái)的注釋掉 } 將ev_press相關(guān)的定義添加上 添加定義:static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 在中斷中補(bǔ)上這一句:wake_up_interruptible(&button_waitq);
測(cè)試程序如下: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h>
int main(int argc, char **argv) { ? ? ? ? int fd; ? ?? unsigned char key_val; ? ? ? fd = open("/dev/keys", O_RDWR); if (fd < 0) { printf("can't open!\n"); }
while (1) { ?? ?read(fd, &key_val, 1); ?? ?printf("key_val: 0x%x\n", key_val); } return 0; } 默認(rèn)情況下是阻塞方式打開(kāi)的,運(yùn)行測(cè)試程序,當(dāng)我們沒(méi)有按下按鍵時(shí)會(huì)休眠,但不會(huì)返回。 我們將: fd = open("/dev/keys", O_RDWR);改為 fd = open("/dev/keys", O_RDWR |?O_NONBLOCK); 會(huì)變成非阻塞方式打開(kāi),這時(shí)沒(méi)有按鍵按下會(huì)返回。為方便測(cè)試,我們?cè)谘h(huán)里加入:sleep(5);
總結(jié)
以上是生活随笔為你收集整理的字符设备驱动程序之按键——同步互斥阻塞的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 嵌入式linux ext4映像制作工具说
- 下一篇: e2fsprogs制作嵌入式 mkfs.