-
FTK的主循环设计
-
批处理程序与GUI程序的不同
- 循环的流程
-
FTK与windows的不同
-
windows的主循环
- while(GetMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
-
FTK作者的看法
- 当前线程的消息是由另一线程传递到当前线程的,GUI需要引入多线程
- 如果把消息看做是一个对象,那WINDOWS里则把消息
单纯看做是一个数据,事件的响应放在wndProc处理函数中
- 看的不多,对以上持保留意见
-
FTK的主循环
- ftk_run();
-
FTK作者的看法
- 单线程处理多个事件源
- 增加新事件源时不会影响事件分发的框架(WINDOWS会影响?)
- FTK主循环方式
- main.c的写法
- Select机制实现(MTK有吗?)
-
ftk_main_loop_run
-
有select方式实现
-
涉及到阻塞与非阻塞的编程
- 阻塞方式block,顾名思义,
就是进程或是线程执行到这些函数时必须等待某个事件的发生,
如果事件没有发生,进程或线程就被阻塞,函数不能立即返回
- 非阻塞方式non-block,
就是进程或线程执行此函数时不必非要等待事件的发生,
一旦执行肯定返回,以返回值的不同来反映函数的执行情况,
如果事件发生则与阻塞方式相同,
若事件没有发生则返回一个代码来告知事件未发生,
而进程或线程继续执行,所以效率较高
-
fd_set
- int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
- struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数
- int maxfdp是一个整数值,是指集合中所有文件描述符的范围
,即所有文件描述符的最大值加1,不能错!
在Windows中这个参数的值无所谓,可以设置不正确
- fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,
我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,
如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,
如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,
select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化
- fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,
我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,
如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,
如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,
select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化
-
struct timeval* timeout是select的超时时间,这个参数至关重要,
它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,
就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化
为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述
符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,
timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时
间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述
- struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
- 函数返回值:负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
- fd_set可理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄
- fd_set集合可以通过一些宏由人为来操作
- 初始化集合FD_ZERO(fd_set *)
- 文件描述符加入集合之中FD_SET(int ,fd_set *)
- 文件描述符从集合中删除FD_CLR(int ,fd_set*)
- 检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )
- 无select方式实现
-
main_loop的执行过程
- 第一步 注册事件源的文件描述符fd到文件描符集中。
每一个整件源都会有自已的文件描述符,定时事件源文件描述符为-1(也就是一无效的文件描述符)
static int ftk_source_timer_get_fd(FtkSource* thiz)
{
return -1;
}
在mainloop里我们会首先将每一个source的fd(要求该文件描述符>= 0)进行注册到一个fd文件描述符集,
if((fd = ftk_source_get_fd(source)) >= 0)
{
FD_SET(fd, &thiz->fdset);
if(mfd < fd) mfd = fd;
n++;
}
第二步 设置select的阻塞时间。
select 的超时时间wait_time,初始值设为3000ms(也就是3s)
我们遍历所有的定时事件源的, 如果该事件源的定时时间小于wait_time,则设置为select 的wait_time为此定时事件源的定时时间,代码如下所示,
source_wait_time = ftk_source_check(source);
if(source_wait_time >= 0 && source_wait_time < wait_time)
{
wait_time = source_wait_time;
}
第三步
调用select函数, 此时select函数是阻塞的,只有两种情况才会跳出select阻塞,执行后面的函数,1 当有fd文件描述符集中的某一个fd可读时,2
select time out超时。
ret = select(mfd + 1, &thiz->fdset, NULL, NULL, &tv);
第四步
接下来遍历每一个事件源,对每一个事件源(source)做如下处理
1 查询事件源的文件描述符fd可读,如果可读,那么调用相应的处理函数,同时直接返回到while循环的第一步.
2 ftk_source_check(source) 是否返回0,
ftk_source_check(source)返回0, ,也就是说事件源为定时事件源,同时该定时时间已到,处理定时事件源的定时处理函数。
对应定时时间为1.5s的定时事件源, 我们设置select的阻塞时间为1.5s,
调用select函数后, 有两种情况会发生
1 假设在1秒时,有一个事件源的fd可读,
此时直接调用该事件源的处理函数,并且进入下一个while循环,在下一次循环里,对于定时时间这1.5s的定时事件源,它的定时时间会更新为0.5s.
2 select超时, 1.5内无任何事件源的fd可读,此时对每一个事件源来说, 首先会校验ftk_source_check(source)
是否返回0,返回0,调用说明该事件源为定时事件源,同时定时时间已到,应该调用事件源的超时处理函数。