回调函数执行过程分析

发布时间 2023-04-02 14:54:21作者: 开宝特攻

回调函数执行过程分析

​ 学号:SA*****200

回调的过程

​ 将函数作为参数,传入某一外部函数,以便该外部函数可以调用此函数来完成某些任务,这样的函数就称为回调函数(CallBack,call-then-back)

回调函数

​ 上图是一个示例,应用程序在调用某一库函数时,通过将函数地址作为参数传递的方式,指定了一个回调函数。库函数执行过程中,会调用这一函数,来完成应用程序的请求。一般回调函数都是和应用程序处于同一抽象层,这样就形成了一个:高层调用底层,底层再反过来调用高层的过程。

​ 事实上,回调不仅可以用于在应用程序和库函数之间,把库函数换成别的中间函数也行。

回调的优势

​ 回调函数为应用提供了很大的灵活性、可拓展性。

​ 一方面,同一个接口(即中间函数),根据起始函数传入的参数(即回调函数地址)不同,可以表现出不同的行为,这体现了多态的思想。

​ 另一方面,采用这种方法,也满足了开闭原则,即你可以在不修改中间函数源代码的情况下,仅通过修改或者添加回调函数,便可以变更接口的行为。

一个回调函数执行的例子

​ 下面是一个回调函数执行的例子(meun.c 中执行过程与此相仿)

​ 现有一个接口 getOddNum,可以给定任意整数,它返回一个奇数。至于得到奇数的方式,则由用户自定义,比如是采用 2K+1 还是 4k+1 的形式,由传入的回调函数确定。

​ main 函数以 doubleNum 为参数调用 getOddNum 时,会把其地址传入 rsi 寄存器:movl $_Z9doubleNumi, %esi,然后就调用接口, call _Z9getOddNumiPFiiE,开始接口的处理过程了。

​ 在 getOddNum 函数中,分配栈帧后,会 call *%rsi,也即调用 rsi 寄存器中的地址指向的函数,而 rsi 中存放的,正是在 main 函数中放入的 doubleNum 的入口地址。可见,确实是在 getOddNum 函数中调用的回调函数。

​ 进入 doubleNum 函数后,它会把 rdi 寄存器中的参数加上自身后返回,返回到 getOddNum 函数中,然后 getOddNum 函数对 rax 寄存器中的返回值加一,把 rsp 指向 main 函数的栈顶,然后返回。此时,main 函数便完成了一次对接口 getOddNum 的调用,传入3,得到了奇数 7。

​ 想以 4k+1 的方式得到奇数,也不需要改动接口 getOddNum 的代码,只需要添加一个回调函数 fourTimesNum 即可,调用的方式也不变,简单满足了开闭原则。