因此,Melon支持了c99,并利用c99提供的宏特性,实现了将函数模板定义的函数的实参以可变参数的形式传递到入口和出口回调函数中。这就意味着,入口和出口回调函数可以访问函数的参数,并对参数的内容作出修改(主要针对指针指向的内存中的数据)。
这样,就给我们在回调函数中提供了更多的可操作空间。我们可以针对不同的函数,修改其参数值,从而来影响后续函数调用中的执行逻辑。我们也可以利用回调函数的处理结果来决定是否应该调用实际函数(也就是过滤功能)。例如前面的权限验证,我们可以将其大致简化为如下形式:
int entry\_callback(char \*file, char \*func, int line, ...)
{
va_list args;
va\_start(args, line);
int \*id = (int \*)va\_arg(args, int \*);
va\_end(args);
return !verify\_identity(id)? -1: 0;
}
void exit\_callback(char \*file, char \*func, int line, ...)
{
va_list args;
va\_start(args, line);
int \*id = (int \*)va\_arg(args, int \*);
va\_end(args);
log("%d\n", \*id);
}
void foo(int \*id)
{
//...
}
int bar(int \*id, int e)
{
//...
return 0;
}
这里的代码只是一个示意,后面会给出一个实际可用的示例。
我们可以随意增加函数,这些函数都会利用同一对入口和出口函数来实现身份验证。
示例
下面就给出一个可用的使用函数模板实现AOP的C语言代码。
#include "mln\_func.h"
#include <stdio.h>
#include <string.h>
#if defined(MLN\_C99)
#include <stdarg.h>
#endif
MLN\_FUNC\_VOID(static, void, foo, (int \*a, int b), (a, b), {
printf("in %s: %d\n", __FUNCTION__, \*a);
\*a += b;
})
MLN\_FUNC(static, int, bar, (void), (), {
printf("%s\n", __FUNCTION__);
return 0;
})
static int my\_entry(const char \*file, const char \*func, int line, ...)
{
if (strcmp(func, "foo")) {
printf("%s won't be executed\n", func);
return -1;
}
#if defined(MLN\_C99)
va_list args;
va\_start(args, line);
int \*a = (int \*)va\_arg(args, int \*);
va\_end(args);
printf("entry %s %s %d %d\n", file, func, line, \*a);
++(\*a);
#else
printf("entry %s %s %d\n", file, func, line);
#endif
return 0;
}
static void my\_exit(const char \*file, const char \*func, int line, ...)
{
if (strcmp(func, "foo"))
return;
#if defined(MLN\_C99)
va_list args;
va\_start(args, line);
int \*a = (int \*)va\_arg(args, int \*);
va\_end(args);
printf("exit %s %s %d %d\n", file, func, line, \*a);
#else
printf("exit %s %s %d\n", file, func, line);
#endif
}
int main(void)
{
int a = 1;
mln\_func\_entry\_callback\_set(my_entry);
mln\_func\_exit\_callback\_set(my_exit);
foo(&a, 2);
return bar();
}
这段函数中,我们使用MLN_FUNC
和MLN_FUNC_VOID
来定义了两个函数,即foo
和bar
。两个函数的逻辑很简单,就是printf输出当前函数名以及参数值(如果有参数的话)。同时,我们也使用了mln_func_entry_callback_set
和mln_func_exit_callback_set
定义了两个全局回调函数,用来在函数调用开始和结束时调用。
我们可以看到,回调函数中使用strcmp
对进入回调的函数做了过滤,仅对foo
函数做额外处理。在入口回调中输出函数信息及第一个参数的值,随后修改参数指针指向的内存中的值。在出口回调中输出函数信息和参数值。
我们来编译一下(我们假定这个代码文件名为a.c
):
cc -o a a.c -I /usr/local/melon/include/ -L /usr/local/melon/lib/ -lmelon -std=c99 -DMLN\_C99 -DMLN\_FUNC\_FLAG
这里:
/usr/local/melon
是Melon库的默认安装路径。-std=c99
是启用c99。-DMLN_C99
是定义一个名为MLN_C99
的宏,这个宏用来启用函数模板组件中C99下才有的特性。-DMLN_FUNC_FLAG
用来定义一个名为MLN_FUNC_FLAG
的宏,这个宏用来启用函数模板功能。是的,如果没有这个宏,上面的那些使用MLN_FUNC
定义的函数就是普通的C语言函数,也不会触发入口和出口回调函数的调用。
执行一下看看效果:
entry a.c foo 8 1
in __mln_func_foo: 2
exit a.c foo 8 4
bar won't be executed
可以看到:
为了做好运维面试路上的助攻手,特整理了上百道 【运维技术栈面试题集锦】 ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。
本份面试集锦涵盖了
- 174 道运维工程师面试题
- 128道k8s面试题
- 108道shell脚本面试题
- 200道Linux面试题
- 51道docker面试题
- 35道Jenkis面试题
- 78道MongoDB面试题
- 17道ansible面试题
- 60道dubbo面试题
- 53道kafka面试
- 18道mysql面试题
- 40道nginx面试题
- 77道redis面试题
- 28道zookeeper
总计 1000+ 道面试题, 内容 又全含金量又高
- 174道运维工程师面试题
1、什么是运维?
2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
3、现在给你三百台服务器,你怎么对他们进行管理?
4、简述raid0 raid1raid5二种工作模式的工作原理及特点
5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
7、Tomcat和Resin有什么区别,工作中你怎么选择?
8、什么是中间件?什么是jdk?
9、讲述一下Tomcat8005、8009、8080三个端口的含义?
10、什么叫CDN?
11、什么叫网站灰度发布?
12、简述DNS进行域名解析的过程?
13、RabbitMQ是什么东西?
14、讲一下Keepalived的工作原理?
15、讲述一下LVS三种模式的工作过程?
16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
17、如何重置mysql root密码?
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!