十一過後..
小王也剛好即將大學畢業,現在要開始寫簡曆,投簡曆,找工作了。
到了家工作單位,小王欣喜若狂,可再一看,心都涼了半截..
“咋了,小王,看你找工作,我都來了幫你大氣,怕什麼,不就人多點嗎..”看著排到電梯口的長龍,我說(其實,我心裡也害怕,可也不能說出來不是)。
“不是,你不知道,我並不怕面試上有問題,有你在,技術上還是問題嗎,不相信自己還不相信你啊,我主要是怕連面試的機會都沒有,你看那麼多人,不知道要排到哪裡去了,你說每年都這樣,那些人力主管部門的咋也不想個好辦法來改善一下..”小王抱怨道。
“呵呵,有需求才會有進步”我哈哈大笑道,“要不這樣吧,看這樣子,正好給你說說有關Linux裝置驅動程式中的阻塞/非阻塞IO,說不定,你一會兒面試時剛好遇到,就不用愁了..”
“好好,太好了,找你算沒白找..”
看到小王興緻來了,我也賣個關子,頓口氣說道:”下面請聽----Linux裝置驅動程式之阻塞/非阻塞IO”
阻塞:顧名思義,就是指在執行裝置操作時若不能獲得資源則掛起操作,直到滿足可操作的條件後再進行操作,被掛起的進程進入休眠狀態,被從調度器的運行隊列移
走,直到等待的條件滿足。
非阻塞:就是反過來,進程在不能進行裝置操作時並不掛起,它或者放棄,或者不停的查詢,直到可以進行位置。
“小王,明白了沒這兩個基本的概念,比如就像今天的面試就是一個阻塞的問題”我補充到,“當然,是不是說非阻塞一定要不非阻塞好,答案是否定的,比如如果裝置驅動不阻塞,則使用者想擷取裝置操作就只能不斷的用cpu查詢(當然不可能放棄了),很顯然這又會無謂的消耗CPU資源。在阻塞訪問就不存在這樣的問題了,不能擷取資源的進程進入休眠,它將CPU資源讓給了其他進程”。
“聽你這麼一說我, 再結合今天這架勢,我是明白了,只是你剛才說的休眠什麼的…那是不是如果條件滿足了,再整個休眠喚醒什麼的..”小王打岔道。
“不錯啊, 有點我當年的風範,懂得觸類旁通了,確實這樣,這樣,我給你兩段代碼,親身感受一下吧”
阻塞地都取串口一個字元 |
非阻塞地都取串口一個字元 |
char buf; fd = open("/dev/ttys",O_RDWR); .. .. res = read(fd,&buf,1); //當串口上有輸入時才返回 if(res == 1) { printf("%c\n",buf); } |
char buf; fd = open("/dev/ttys",O_RDWR | O_NONBLOCK); .. .. while( read(fd,&buf,1) !=1); //當串口上無輸入也返回,所 //以要迴圈嘗試讀取串口 printf("%c\n",buf); |
L
現在我們有了阻塞的方式讀取,那麼阻塞的進程因為沒有獲得資源會進入休眠狀態,現在就要聊聊有關喚醒的事了。在Linux裝置驅動中,可以使用等待隊
列(wait queue)來實現阻塞進程的喚醒.等待隊列能夠用於實現核心中的非同步事件通知機制。Linux提供了有關等待隊列的操作:
1) wait_queue_head_t my_queue; //定義等待隊列頭
2) init_waitqueue_head(&my_queue); //初始化隊列頭
如果覺得上邊兩步來的麻煩,可以直接使用DECLARE_WAIT_QUEUE_HEAD(name)
3) DECLARE_WAITQUEUE(name,tsk); //定義等待隊列
4) void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
用於將等待隊列wait添加到等待隊列頭指向的等待隊列鏈表中 。
5) wait_event(queue, conditon);
wait_event_interruptible(queue, condition);//可以被訊號打斷
wait_event_timeout(queue, condition, timeout);
wait_event_interruptible_timeout(queue, condition, timeout);//不能被訊號打斷
queue:作為等待隊列頭的等待隊列被喚醒
conditon:必須滿足,否則阻塞
timeout和conditon相比,有更高優先順序
6) void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
上述操作會喚醒以queue作為等待隊列頭的所有等待隊列中所有屬於該等待隊列頭的等待隊列對應的進程。
7) sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
sleep_on作用是把目前進程的狀態置成TASK_UNINTERRUPTIBLE,並定義一個等待隊列,之後把他附屬到等待隊列頭q,直到資源可用,q引導的等待隊列被喚醒。interruptible_sleep_on作用是一樣的, 只不過它把進程狀態置為TASK_INTERRUPTIBLE.
這兩個函數的流程是首先,定義並初始化等待隊列,把進程的狀態置成TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,並將對待隊列添加到等待隊列頭。
然後通過schedule(放棄CPU,調度其他進程執行。最後,當進程被其他地方喚醒,將等待隊列移除等待隊列頭。
在Linux核心中,使用set_current_state()和__add_wait_queue()函數來實現目前進程狀態的改變,直接使用current->state = TASK_UNINTERRUPTIBLE
類似的語句也是可以的。
因此我們有時也可能在許多驅動中看到,它並不調用sleep_on或interruptible_sleep_on(),而是親自進行進程的狀態改變和切換。
“小王,聽了這麼多你心裡有個大致的底了沒,如果考官問道這方面的問題,應該挺順利的,現在時間來不及了,晚點我給你來個例子,你看看就可以了,瞧,馬上到你了,趕快準備一下,祝你好運哦..”