熟悉之后可以跳过
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