熟悉之後可以跳過
Hotplug 是一種內核向用戶態應用通報關於熱插拔設備一些事件發生的機制,桌面系統能夠利用它對設備進行有效的管理,udev 動態地維護 /dev 下的設備文件,inotify 是一種文件系統的變化通知機制,如文件增加、刪除等事件可以立刻讓用戶態得知。
事實上,在 inotify 之前已經存在一種類似的機制叫 dnotify,但是它存在許多缺陷:
1. 對於想監視的每一個目錄,用戶都需要打開一個文件描述符,因此如果需要監視的目錄較多,將導致打開許多文件描述符,特別是,如果被監視目錄在移動介質上(如光盤和 USB 盤),將導致無法 umount 這些文件系統,因為使用 dnotify 的應用打開的文件描述符在使用該文件系統。
2. dnotify 是基於目錄的,它只能得到目錄變化事件,當然在目錄內的文件的變化會影響到其所在目錄從而引發目錄變化事件,但是要想通過目錄事件來得知哪個文件變化,需要緩存許多 stat 結構的數據。
3. Dnotify 的接口非常不友好,它使用 signal。
Inotify 是為替代 dnotify 而設計的,它克服了 dnotify 的缺陷,提供了更好用的,簡潔而強大的文件變化通知機制:
1. Inotify 不需要對被監視的目標打開文件描述符,而且如果被監視目標在可移動介質上,那麼在 umount 該介質上的文件系統後,被監視目標對應的 watch 將被自動刪除,並且會產生一個 umount 事件。
2. Inotify 既可以監視文件,也可以監視目錄。
3. Inotify 使用系統調用而非 SIGIO 來通知文件系統事件。
4. Inotify 使用文件描述符作為接口,因而可以使用通常的文件 I/O 操作select 和 poll 來監視文件系統的變化。(廣泛應用於電源管理, udev中監控/dev下文件事件,tslib中提到通知觸摸屏有觸摸事件也通過此機制)
Inotify 可以監視的文件系統事件包括:
IN_ACCESS,即文件被訪問
IN_MODIFY,文件被 write
IN_ATTRIB,文件屬性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可寫文件被 close
IN_CLOSE_NOWRITE,不可寫文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移來,如 mv、cp
IN_CREATE,創建新文件
IN_DELETE,文件被刪除,如 rm
IN_DELETE_SELF,自刪除,即一個可執行文件在執行時刪除自己
IN_MOVE_SELF,自移動,即一個可執行文件在執行時移動自己
IN_UNMOUNT,宿主文件系統被 umount
IN_CLOSE,文件被關閉,等同於(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移動,等同於(IN_MOVED_FROM | IN_MOVED_TO)
注:上面所說的文件也包括目錄。
用戶態操作函數如下,
fd=Inotify_init()
Wd=Inotify_add_watch(fd,path,mask)//mask=IN_ALL_EVENTS
Inotify_rm_watch(fd,wd);
檢測目錄下或者文件的操作
調用read讀取數據
Read(fd,buffer,MAX_BUF_SIZE)
Struct inotify_event{
__s32 wd;
__u32 mask; //具體位對應不同的文件或者目錄的操作
__u32 cookie;//cookie to synchronize two events
__u32 len; //length (including nulls) of name
Char name[0]; //stub for possible name
}
Struct inotify_event *event;
Event=(struct inotify_event *)buffer;
說起socket,在udev中也提到它的用途,不同的socket用途不一樣,例如有NETLINK_KOBJECT_UEVENT專門用於監控uevent事件的socket,NetLINK_NETFILTER用於Netfilter,以及NETLINK_SELINUX SElinux event notifications等。
而在電源管理中用到的是最簡單的AF_LOCAL族的socket,一般用於linux user app之間進行通信,socket的東東在網絡上也很多,就不多說了,貼一些socket服務端一些簡單代碼,並且說明一個問題。
Struct sockaddr_un addr={AF_LOCAL, SERVER_SOCKET_NAME };
Int len=sizeof(addr.sun_famliy)+strlen(addr.sun_path);
Unlink(SERVR_SOCKET_NAME);
Fd=socket(AF_LOCAL,SOCK_STREAM,0);
Setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))
Bind(fd,&addr,len);
Chmod(addr.sun_path,0666)
Listen(fd,CLIENT_SIZE) //client_size 可以監聽的client數目
Setsockopt SO_REUSEADDR
缺省條件下,一個套接口不能與一個已在使用中的本地地址捆綁(參見bind())。但有時會需要“重用”地址。因為每一個連接都由本地地址和遠端地址的組合唯一確定,所以只要遠端地址不同,兩個套接口與一個地址捆綁並無大礙。為了通知WINDOWS套接口實現不要因為一個地址已被一個套接口使用就不讓它與另一個套接口捆綁,應用程序可在bind()調用前先設置SO_REUSEADDR選項。請注意僅在bind()調用時該選項才被解釋;故此無需(但也無害)將一個不會共用地址的套接口設置該選項,或者在bind()對這個或其他套接口無影響情況下設置或清除這一選項。
#include<poll.h>
Int poll(struct pollfd fds[],nfds_t nfds,int timeout)
對於Poll而言,每當調用Poll時,系統不會清空fds[]這個數組,特別在socket連接比較多的時候,在一定程度上提高處理效率,而select函數在調用之後,會清空它所檢測的socket描述符集合,導致每次調用select之前都必須把socket描述符重新加入到待檢測的集合中,因此,select函數適合於只檢測一個socket描述符的情況,而Poll適用於大量socket描述符的情況。
如果需要反復使用select時,需要注意將需要監控的描述符重新加入到待檢測的集合中(在寫終端通信程序時 出過該問題)
Nfds為fds數組中fd的長
返回值:
>0:數組fds中准備好讀、寫或出錯狀態的那些socket描述符的總數量;
Pollfd結構
Struct pollfd
{
Int fd;//文件描述符
Short events;//等待的事件
Short revents;//實際發生的事件
}
POLLIN 有數據可以讀入,read不會阻塞
我們知道在/sys/文件系統中,提供了進入suspend模式的屬性文件,向該屬性文件寫入“mem”即可進入睡眠模式,然而在用戶層如何確定是否向該接口發送消息呢?
設計一個定時器,如果該定時器超時,則向屬性文件寫入“mem”,當然,我們在寫屬性文件時,應該能廣播所有user app,通知其做好准備,系統resume時同樣能廣播所有的設備,
這樣就需要一個服務和一個client或者多個client,client提供user app所需要的注冊接口,將與電源管理有關的程序注冊到client中來,並且提供相應的操作方法給應用程序調用。
總體的思路如下圖,
而整體的框架也如下圖所示,
接下來根據框架圖分三個部分介紹。
TO BE CONTINUED