钩子函数
回调函数与钩子函数
简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
2、什么是钩子(hook)函数?
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链,最近安装的钩子放在链的开始,而最先安装的钩子放在最后,也就是后加入的先获得控制权。
3、如何区分?
函数钩取事件:函数主动找事件=>钩子函数
事件派发给函数:事件调用函数=>回调函数
打个形象的比喻:书店、你、你小表弟、书店美女店员
书店暂时没有你要的书,咋办呢?
1)你无耻的派了你小表弟在书店24小时蹲守,有人送书来,你表弟就去看下,一旦来了,你表弟就告诉你,并且帮你买下来
2)你留下你的号码给美女店员,让她有书就通知你
第一种:你就是js,你表弟就是你派出去的监听器,监听函数就是让你表弟买下了这本书的指令,这个过程没有美女店员的事
第二种:你依然是js,美女店员是事件,你的号码就是函数,把号码留给店员的过程就是把函数注入到事件当中的过程,美女打电话通知你的过程就是回调,所以你注入的函数就是回调函数,这个过程没有你表弟的事
dom通过事件通知js的过程即是回调,对应的函数就是回调函数
js通过监听函数得知事件的过程即是钩取,对应的函数就是钩子函数
钩子函数和回调函数都是事件处理函数,根本上,他们都是为了捕获消息而生的,但是钩子函数在捕获消息的第一时间就会执行,而回调函数是在整个捕获过程结束时,最后一个被执行的。
一、beforesleep “钩子函数” -- 统一的应答处理
while 事件循环:
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
beforesleep 对象是一个回调函数,在 redis-server 初始化时已经设置好了。
while 事件循环每一轮循环都会调用这个 beforeSleep 函数。在这个 beforeSleep 函数中有一个 handleClientsWithPendingWrites 调用:
void beforeSleep(struct aeEventLoop *eventLoop) {
//省略无关代码...
/* Handle writes with pending output buffers. */
handleClientsWithPendingWrites();
//省略无关代码...
}
handleClientsWithPendingWrites()集中处理所有客户端的回复。具体地:
首先从全局 server 对象的 clients_pending_write 字段( 存储 client 对象的链表 )挨个取出有数据要发送的 client 对象,然后调用 writeToClient 函数尝试将 client 中存储的应答数据(每个已连接的client都有维护一个输出缓冲区)发出去。
(1)如果数据能够全部发完,发完以后则会移除对应的 fd 上的可写事件( 如果添加了 );如果当前 client 设置了 CLIENT_CLOSE_AFTER_REPLY 标志,则发送完数据立即释放这个 client 对象。
(2)可能由于网络或者客户端的原因,redis-server 某个客户端的数据发送不出去,或者只有部分可以发出去。不管哪种情况,数据这一次发不完。这个时候就需要监听可写事件了。注册可写事件 AE_WRITABLE 的回调函数是 sendReplyToClient 。也就是说,当下一次某个触发可写事件时,调用的就是 sendReplyToClient 函数。
总结:
如果有数据要发送给某个 client ,不需要专门注册可写事件等触发可写事件再发送。通常的做法是在应答数据产生的地方直接发送,如果是因为对端 Tcp 窗口太小引起的发送不完,则将剩余的数据存储至某个缓冲区并注册监听可写事件,等下次触发可写事件后再尝试发送,一直到数据全部发送完毕后移除可写事件。
redis-server 数据的发送逻辑与这个稍微有点差别,就是将数据发送的时机放到了 EventLoop 的某个时间点上( 这里是在 ProcessEvents 之前 ),其他的与上面完全一样。
之所以不注册监听可写事件,等可写事件触发再发送数据,原因是通常情况下,网络通信的两端数据一般都是正常收发的,不会出现某一端由于 Tcp 窗口太小而使另外一端发不出去的情况。如果注册监听可写事件,那么这个事件会频繁触发,而触发时不一定有数据需要发送,这样不仅浪费系统资源,同时也浪费服务器程序宝贵的 cpu 时间片。
二、aftersleep 钩子函数 - 处理其他
通常情形下,在一个 EventLoop 中除了有定时器、IO Multiplexing 和 IO 事件处理逻辑外,可以根据需求自定义一些函数,这类函数我们称之为“钩子函数”。钩子函数可以位于 Loop 的任何位置,前面我们介绍的 beforesleep 函数就是在事件处理之前自定义的钩子函数( 位于定时器时间检测逻辑之前 )。
在 redis-server 中,在 IO Multiplexing 调用与 IO 事件处理逻辑之间也有一个自定义的钩子函数叫 aftersleep :
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
//无关代码省略...
/* IO Multiplexing */
numevents = aeApiPoll(eventLoop, tvp);
/* After sleep callback. */
if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
eventLoop->aftersleep(eventLoop);
/* process IO event. */
for (j = 0; j < numevents; j++) {
//无关代码省略...
}
}
三、redis-server网络通信模块
参考:
https://gitbook.cn/gitchat/column/5c0e149eedba1b683458fd5f/topic/5c0e2c27edba1b683459204d
javaScript:理解事件、事件处理函数、钩子函数、回调函数
钩子(HOOK)函数教程
相关阅读
MATLABSimulink模块库详解(一)Sources篇
MATLAB Simulink模块库详解(一)Sources篇Simulink模块库概述1.Sources模块库,为仿真提供各种信号源2.Sinks模块库,为仿真提供输出设备
现在在公司里大多数时间使用的原型工具是墨客,真的是简单易用,拖拽式的设计简直是良心。但是也有不足的地方,个人对交互要求较高,这一
568B: 白橙 橙 白绿 蓝 白蓝 绿 白棕 棕568A: 白绿 绿 白橙 蓝 白蓝 橙 白棕 棕
关于模块的初始化网上讲解的有很多,但是有些讲的不是特别全面,有些讲的不是特别的深入,所以我在博客上找了一些可以供参考的,给大家同
店铺要装修不知道设置轮播图模块的看过来。关于轮播图无论是旺铺智能版还是旺铺专业版或基础版都后台自带这样的轮播图模块,我们只