姓名:李浩然
學號:16030410020
轉自:http://blog.csdn.net/Dreaming_My_Dreams/article/details/8272877(有刪改)
【嵌牛導讀】:為了區(qū)分IO的五種模型蓬抄,下面先來看看同步與異步俐银、阻塞與非阻塞的概念差別。
同步:所謂同步碘橘,就是在發(fā)出一個功能調用時畦贸,在沒有得到結果之前仔涩,該調用就不返回地粪。按照這個定義首量,其實絕大多數函數都是同步調用(例如sin,?isdigit等)荞驴。但是一般而言不皆,我們在說同步、異步的時候熊楼,特指那些需要其他部件協作或者需要一定時間完成的任務霹娄。最常見的例子就是?SendMessage。該函數發(fā)送一個消息給某個窗口鲫骗,在對方處理完消息之前犬耻,這個函數不返回。當對方處理完畢以后执泰,該函數才把消息處理函數所返回的?LRESULT值返回給調用者枕磁。
異步:異步的概念和同步相對。當一個異步功能調用發(fā)出后术吝,調用者不能立刻得到結果计济。當該異步功能完成后,通過狀態(tài)排苍、通知或回調來通知調用者沦寂。
【嵌牛鼻子】:非阻塞IO、阻塞IO淘衙、輪詢传藏、異步通知、同步彤守、異步
【嵌牛提問】:4種IO模型是什么毯侦?都有哪四種IO模型?這些模型各有什么特點具垫?在底層是怎么實現的侈离。
【嵌牛正文】:一、非阻塞IO
當設備沒有要操作的資源時做修,它會立即返回霍狰。這種操作抡草,一般要在應用層循環(huán)檢測,比較占用cpu資源蔗坯。
二康震、阻塞IO
這種實現方法需要借助于等待隊列
對待buf有兩種隊列,讀和寫宾濒,可以在設備的私有資源中定義兩個隊列:wait_queue_head_t ? ?rq腿短,wq;當讀寫時绘梦,沒有相應的資源橘忱,并且是以阻塞方式讀寫設備的,那么卸奉,就可以把當前任務(應用層的進程钝诚,這些任務都有一個task_struct來表示)放到等待隊列中,等待有資源或中斷時喚醒榄棵。
睡眠方式有兩種:手動睡眠凝颇、自動睡眠
手動睡眠:
自動睡眠:wait_event_interruptible(rq/wq,condition)
這種方式是可以被中斷喚醒的睡眠方式,不管何種喚醒疹鳄,醒來首先會檢查condition是否滿足拧略,若不滿足會繼續(xù)睡眠,若condition滿足瘪弓,那么垫蛆,就會再次去申請資源(因為同時喚醒這個等待隊列的所有任務,會產生競態(tài))腺怯,沒有申請到袱饭,會繼續(xù)睡眠,申請到瓢喉,就會去完成讀寫宁赤。
三、輪詢(這種方法的select和poll之間的機制還有點迷糊栓票,稍后補上)
輪詢,需要借助于底層驅動的poll函數和等待隊列來實現
在應用層中愕够,我們把需要監(jiān)控的fd放入fd_set中走贪,然后調用select函數,最終就會調用到驅動中的poll 函數惑芭。
這些系統(tǒng)調用功能相同:允許進程來決定它是否可讀或寫一個或多 個文件而不阻塞坠狡。這些調用也可阻塞進程直到任何一個給定集合的文件描述符可用來讀或寫。(也就是說遂跟,在應用程序中的select中會一直阻塞(還是睡眠)逃沿,直至至少有一個設備可以操作婴渡,所以說,在應用層次中凯亮,其實不是輪詢執(zhí)行select函數边臼,這點是以前的誤解)
四、異步通知
在設備驅動中使用異步通知可以使得對設備的訪問 可進行時假消,由驅動主動通知應用程序進行訪問柠并,這樣,使用無阻塞IO的應用程序無須輪詢設備是否可訪問富拗,而阻塞訪問 也可被類似“中斷”的異步通知所取代臼予。
概念:一旦設備就緒,則主動通知應用程序啃沪,這樣應用程序根本就不需要查詢設備狀態(tài)粘拾,這一點非常類似硬件上 “中斷”的概念,可稱之為“信號驅使的異步IO”创千。
應用程序:
(1)首先應用程序指定進程問文件的屬主缰雇,就是把pid保存在file中
fcntl(fd,F_SETOWN,getpid());
(2) 要在設備中設置FASYNC標志
oflag = fcntl(fd,F_GETFL)
fcntl(fd,F_SETFL,oflag | FASYNC)
設置完上面一句話后,應用程序會自動調用驅動中的fasync函數签餐,
驅動中:
(1) 在設備結構體中定義一個異步結構體指針:struct fasync_struct ?* async_queue寓涨;
(2)實現fasync函數,調用fasync_helper函數氯檐,把file放入或刪除異步通知隊列中
(3)當設備資源就緒時戒良,發(fā)送信號,可以發(fā)一次冠摄,也可以一直發(fā)糯崎,直至資源被消耗;這完全取決于驅動怎么寫
kill_fsaync(&dev->async_queue,SIGIO,POLL_IN)
(4)當進程關閉設備時河泳,要在release函數中把進程從異步通知隊列中刪除.
[cpp]view plaincopy
structkey_device{
structcdev?cdev;
structfasync_struct?*async_queue;//定義一個隊列
}key_device;
intkey_fasync(intfd,structfile?*file,intmode)
{
structkey_device?*device?=?file->private_data;
returnfasync_helper(fd,file,mode,&device->async_queue);//此函數是用來把進程加入或刪除一個隊列沃呢,這是根據參數mode來區(qū)別的,當應用層fcntl(fd,F_SETFL,oflag?|?FASYNC)時拆挥,mode經過VFS層后自動為1薄霜,則此函數是用來加入隊列//在release中,mode傳遞為0纸兔,則是把任務從隊列中刪除
}
structkey_release(structinode?*,structfile?*)
{
key_fasync(-1,file,0)//刪除
}
structfile_operations?fops={
.fasync??=?key_fasync,
};
if(key_device->async_queue)
kill_fasync(&((structkey_device*)dev)->async_queue,SIGIO,POLL_IN);????//向隊列中的所有任務發(fā)送SIGIO信號惰瓜,
}
structkey_device{
structcdev?cdev;
structfasync_struct?*async_queue;//定義一個隊列
}key_device;
intkey_fasync(intfd,structfile?*file,intmode)
{
structkey_device?*device?=?file->private_data;
returnfasync_helper(fd,file,mode,&device->async_queue);//此函數是用來把進程加入或刪除一個隊列,這是根據參數mode來區(qū)別的汉矿,當應用層fcntl(fd,F_SETFL,oflag?|?FASYNC)時崎坊,mode經過VFS層后自動為1,則此函數是用來加入隊列//在release中洲拇,mode傳遞為0奈揍,則是把任務從隊列中刪除
}
structkey_release(structinode?*,structfile?*)
{
key_fasync(-1,file,0)//刪除
}
structfile_operations?fops={
.fasync??=?key_fasync,
};
if(key_device->async_queue)
kill_fasync(&((structkey_device*)dev)->async_queue,SIGIO,POLL_IN);????//向隊列中的所有任務發(fā)送SIGIO信號曲尸,
}
阻塞IO意味著一直等待設備可訪問后再訪問。
非阻塞IO中使用poll意味著查詢設備是否可訪問男翰。
異步通知則意味著設備通知自身可訪問另患,實現了異步IO。