Linux的so组件设计框架及逻辑

组件这个名词在百度上的定义:组件(Component)是对数据和方法的简单封装

这里不再赘述概念和定义,也不再过多纠结很多技术问题或者讨论。我们就实际组件应用关注的焦点和思考,做一个回顾,并整理Linux的so组件设计框架及逻辑。

1. 组件关注点

1.1 组件定义关注点

  1. 组件属性
  2. 组件方法

1.2 组件业务关注点

  1. 支持多实例(线程安全)
  2. 无缝升级(无业务中断)
  3. 支持负荷分担(业务动态迁移)
  4. 支持性能监测
  5. 支持安全策略
  6. 支持可配置(所有参数调优)
  7. 支持优雅退出功能

2. so特性

从库的角度,大体分为静态链接库和动态链接库两大类。so就是linux下动态链接库的后缀。其特性可参考度娘

  1. 扩展了应用程序的特性;
  2. 可以用许多种编程语言来编写;
  3. 简化了软件项目的管理;
  4. 有助于节省内存;
  5. 有助于资源共享;
  6. 有助于应用程序的本地化;
  7. 有助于解决平台差异;
  8. 可以用于一些特殊的目的;

3. so组件设计逻辑

这里以Linux系统为例,但是Windows同样适用(可能代码上稍有差异)。

我们为了解决组件的关注点,应用了动态链接库的特性,从逻辑层面可以解决业务需求。

so动态链接库:

  1. 代码段共享,节省内存;
  2. 数据段每个进程拥有各自的私有数据副本;
  3. 组件方法提供了天然的API接口设计理念,反向要求设计者具备更高的业务抽象和解耦;
  4. 便于版本及项目管理;
  5. 多语言开发平台以及平台差异的天然兼容性;
  6. 析构和构造函数有助于组件的自身动态维护;

4. so组件设计框架

通过组件的构造和析构作为组件初始化和去初始化机制,当然为了更好的应对优雅退出及异常场景处理,在构造函数中需要做非常多的业务/异常场景的分析和钩子函数设计。

这里针对构造、析构和接口问题进行框架介绍和范例举例:

4.1 so组件范例

  • 构造函数
  • 析构函数
  • API接口
//so_test.c
#include <stdio.h>

int Myfunc_test(void)
{
	printf("........Myfunc_test in %s........\n", __FILE__);
	return(0);
}

static void __attribute__((constructor)) Myfunc_init(void)
{
	printf("Myfunc_init in %s\n", __FILE__);
	return;
}

static void __attribute__((destructor)) Myfunc_fini(void)
{
	printf("Myfunc_fini in %s\n", __FILE__);
	return;
}
//so_test.h
#ifndef __SO_TEST_HEADER__
#define __SO_TEST_HEADER__

int Myfunc_test(void);

#endif /* __SO_TEST_HEADER__ */

编译方法

$ gcc -fPIC -shared -o libtestsubx.so so_test.c

4.2 so隐式调用范例

#include <stdio.h>
#include "so_test.h"

int main(int argc, char** argv)
{
	printf("so main\n");
	Myfunc_test();
	sleep(5);
	printf("so main exit\n");
}

编译方法

$ gcc -g so_main.c -o so_main -L. -ltestsubx

鉴于隐式调用收到so路径的限制,因此需要更好的管理路径,测试脚本如下

#!/bin/sh
export LD_PRELOAD=${pwd}libtestsubx.so:${LD_PRELOAD}
export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH}
./so_main

4.3 so显式调用范例

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include "so_test.h"

int main(int argc, char** argv)
{
	void(*pTest)();      

	char dir[256];
	
	char *path = getcwd(dir, 256);
	if ( path == NULL ) {
		printf("Failed get current dir\n");
		return -1;
	}
	
	strcat(path, "/");
	strcat(path, "/libtestsubx.so");
	printf("Current so is %s\n", path);

	void*pdlHandle = dlopen(path, RTLD_LAZY);
	if( pdlHandle == NULL ) {
		printf("Failed load library\n");
		return -1;
	}

	char* pszErr = dlerror();
	if(pszErr != NULL) {
		printf("%s\n", pszErr);
		return -1;
	}

	pTest = dlsym(pdlHandle, "Myfunc_test");
	pszErr = dlerror();
	if( pszErr != NULL ) {
		printf("%s\n", pszErr);
		dlclose(pdlHandle);
		return -1;
	}

	(*pTest)();

	dlclose(pdlHandle);

	return 0;  
}

编译方法

$ gcc -g so_dlopen.c -o so_dlopen -L. -ldl

5 so组件设计建议

根据实际应用的要求,选择隐式调用和显式调用。通常来说简单设计,隐式调用更加方便和直接。

而从实际要对组件进行“1.2 组件业务关注点”这种复杂考虑,那么建议采用显式调用方法,并针对API进行管理(这里的API是指函数)。更多业务类解耦可以考虑消息API。

在大型项目开发中,需要建立主体程序框架和基于主体程序框架的业务组件设计框架,以便针对业务的组件能够通过各种API进行解耦,独立进行开发、测试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值