IO事件的执行流程以Event-test.c中的函数进行分析
-
static void //事件触发后的回调函数
-
fifo_read(int fd, short event, void *arg)
-
{
-
char buf[255];
-
int len;
-
struct event *ev = arg;
-
#ifdef WIN32
-
DWORD dwBytesRead;
-
#endif
-
-
/* Reschedule this event */
-
event_add(ev, NULL); //重新绑定事件
-
-
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n", //在标准错误上打印
-
fd, event, arg);
-
#ifdef WIN32
-
len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL);
-
-
// Check for end of file.
-
if(len && dwBytesRead == 0) {
-
fprintf(stderr, "End Of File");
-
event_del(ev);
-
return;
-
}
-
-
buf[dwBytesRead] = '\0';
-
#else
-
len = read(fd, buf, sizeof(buf) - 1); //从文件描述符fd中读取数据
-
-
if (len == -1) {
-
perror("read");
-
return;
-
} else if (len == 0) {
-
fprintf(stderr, "Connection closed\n");
-
return;
-
}
-
-
buf[len] = '\0'; //增加末尾字符
-
#endif
-
fprintf(stdout, "Read: %s\n", buf); //在标准输出上打印
-
}
-
int
-
main (int argc, char **argv)
-
{
-
struct event evfifo; //定义一个事件
-
#ifdef WIN32
-
HANDLE socket;
-
// Open a file.
-
socket = CreateFileA("test.txt", // open File
-
GENERIC_READ, // open for reading
-
0, // do not share
-
NULL, // no security
-
OPEN_EXISTING, // existing file only
-
FILE_ATTRIBUTE_NORMAL, // normal file
-
NULL); // no attr. template
-
-
if(socket == INVALID_HANDLE_VALUE)
-
return 1;
-
-
#else
-
struct stat st; //linux文件属性结构
-
const char *fifo = "event.fifo"; //文件名
-
int socket;
-
-
if (lstat (fifo, &st) == 0) { //获取文件属性到st结构
-
if ((st.st_mode & S_IFMT) == S_IFREG) {
-
errno = EEXIST;
-
perror("lstat");
-
exit (1);
-
}
-
}
-
-
unlink (fifo);
-
if (mkfifo (fifo, 0600) == -1) { //设置文件读,写权限
-
perror("mkfifo");
-
exit (1);
-
}
-
-
/* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
-
#ifdef __linux
-
socket = open (fifo, O_RDWR | O_NONBLOCK, 0); //打开文件,以读写,非阻塞方式
-
#else
-
socket = open (fifo, O_RDONLY | O_NONBLOCK, 0);
-
#endif
-
-
if (socket == -1) { //打开失败
-
perror("open");
-
exit (1);
-
}
-
-
fprintf(stderr, "Write data to %s\n", fifo); //输出到标准错误
-
#endif
-
/* Initalize the event library */
-
event_init(); //初始化event_base
-
-
/* Initalize one event */
-
#ifdef WIN32
-
event_set(&evfifo, (int)socket, EV_READ, fifo_read, &evfifo);
-
#else
-
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); //设置event
-
#endif
-
-
/* Add it to the active events, without a timeout */
-
event_add(&evfifo, NULL); //绑定event到event_base
-
-
event_dispatch(); //事件调度
-
#ifdef WIN32
-
CloseHandle(socket);
-
#endif
-
return (0);
-
}
IO事件的读写过程可以分为以下几个步骤
(1) 打开文件,并获取指定文件的文件描述符socket
(2) 调用event_set()函数,设置IO事件evfifo,指定事件的文件描述符socket,事件的类型EV_READ,事件的回调函数fifo_read,回调函数的参数evfifo。
(3) 调用event_add()函数,绑定evififo到全局eventbase。在add函数中,evfifo被加入到eventbase的eventqueue链表之中,并且指定了evfifo所处的链表类型是EVLIST_INSERTED
(4) 调用event_dispatch()函数,执行事件分发
(a)在分发函数中,先执行evsel->dispatch,evsel是绑定到IO多路复用技术中的一种的指针,这里假设为EPOLL,则这里将调用epoll_dispatch。
(b)在执行
epoll_dispatch过程中,系统检测出文件描述符socket已经就绪,则会把evfifo加入到active链表中,active链表实际上对应的eventbase中的activequeue[i]链表,i是evfifo在初始化时确定的优先级。
(c)系统继续执行event_process_active()函数,处理active中的事件evfifo,执行evfifo的回调函数。
(d)当所有的active事件都处理完毕之后,系统退出dispatch函数。
在上面的例子中,在执行evfifo的回调函数过程中,再次把evfifo绑定到eventbase,所以程序会一直去读文件描述符socket对应文件中的前254个字符。
阅读(1046) | 评论(0) | 转发(0) |