多个进程都链接同一个so动态库,代码段共享,数据段不共享

本文通过实例演示了在不同进程和线程中使用共享库时数据段的行为差异。揭示了即使多个程序链接同一动态库,它们的数据段并不共享,而同一进程内的不同线程则共享数据段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

纸上得来终觉浅,这句话一点不假,动态库的知识在看《程序员的自我修养》这本书已经了解了。但是到实际项目遇到问题

却没往这方面去考虑。浪费了半天时间。

问题是这样的:我有个模块编译成一个so库供其他程序使用。比如A,B都需要链接lib.so。lib.so里面有个全局指针p,当A程序调用lib.so的函数启动lib的功能后这个p指针就被赋值了。然后B程序启动,但奇怪的是在B程序里面调用lib.so库函数去get p指针的时得到的却是未初始化的值。导致B程序无法运行,然后就郁闷了半天。明明A已经正常启动把p赋值了,为什么p还是NULL呢?原来是A和B只是共有Lib.so的代码段,但是数据段A和B都有各自的一份是不共享的。为此我做了一个实验。

(1)lib.c和lib.h

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

static int a = 0;

void set(int v)
{
	a = v;
	printf("a = %d\n", a);
	return;
}

void get()
{
	printf("a = %d\n", a);
}
void set(int v);
void get();

将Lib.c编译成一个so库:

gcc -fPIC -shared -o lib.so lib.c

(2)应用程序1: program1.c

#include "lib.h"
#include <time.h>

void main()
{
	int cnt = 0;
	while(1){
		set(cnt++);
		usleep(1000000);
	}
	return;
}

功能:每隔1秒钟去修改Lib.so的a的值

(3)应用程序2: program2.c

#include "lib.h"
#include <time.h>

void main()
{
	while(1){
		get();
		usleep(500000);
	}
	return;
}

功能:每隔500ms去取lib.so里a的值

编译:

gcc -o program1 program1.c ./lib.so
gcc -o program2 program2.c ./lib.so

运行:

./program1 &

./program2

结果:

a = 1
a = 2
./a = 3
programa = 4
2
a = 0
a = 5
a = 0
a = 0
a = 6
a = 0
a = 0
a = 7
a = 0
a = 0
a = 8
a = 0
a = 0
a = 9
a = 0
a = 0
a = 10
a = 0
a = 0
a = 11
a = 0
a = 0
a = 12

从结果可以看出:program1一直在修改a的值,但是program2取得的值一直是0。

这是不同进程的情况。

又做了一个实验,对于同一个进程中的不同的两个线程结果又是怎样呢?

代码如下:

#include "lib.h"
#include <time.h>
#include <pthread.h>

void* thread1(void* data)
{
	int cnt = 0;
	while(1){
		printf("thread1 set...\n");
		set(cnt++);
		usleep(1000000);
	}
	return NULL;
}

void* thread2(void* data)
{
	while(1){
		printf("thread2 get...\n");
		get();
		usleep(500000);
	}
}

void main()
{
	pthread_t thread_id;
	pthread_create(&thread_id, NULL, &thread1, NULL);
	pthread_create(&thread_id, NULL, &thread2, NULL);
	while(1){
		usleep(10000000);
	}
	return;
}

就是把program1和2变为两个线程。

运行结果如下:

thread1 set...
a = 0
thread2 get...
a = 0
thread2 get...
a = 0
thread1 set...
a = 1
thread2 get...
a = 1
thread2 get...
a = 1
thread1 set...
a = 2
thread2 get...
a = 2
thread2 get...
a = 2
thread1 set...
a = 3
thread2 get...
a = 3
thread2 get...
a = 3
thread1 set...
a = 4
thread2 get...
a = 4
thread2 get...
a = 4

可以看出对于两个线程,so的数据段是共享的。

特此记录下。

<think>嗯,用户的问题是关于在Android中多个APK加载同一个SO库时,该库中的方法是否支持并发调用,以及多进程访问的并发安全性。我需要先理解这个问题的背景和相关知识点,然后逐步分析可能的答案。 首先,用户提到的多个APK共享同一个SO库的情况。根据引用[2],重复的SO库可能会导致包体积增加和运行时风险,比如多个SO静态链接的问题。但这里用户关注的是并发调用的问题。接下来,我需要考虑SO库在内存中的加载情况。每个APK在安装时会将SO文件解压到自己的私有目录,如/data/app/包名/lib下。过,如果同APK加载的是同一个物理文件(比如系统库),系统可能会共享内存映射,但如果是同路径下的同名SO,可能共享。 然后,关于并发调用,需要考虑SO库本身的线程安全性。如果SO库内部没有使用全局变量或静态变量,或者正确使用了同步机制(如互斥锁),那么并发调用是安全的。但如果库内部有共享资源且未加锁,多线程同时访问可能导致竞态条件。此外,多进程的情况更复杂,因为每个进程有独立的地址空间,同一SO库在进程中的实例是隔离的,全局变量共享。这时候如果涉及跨进程共享资源(如文件、共享内存),需要额外的同步机制。 用户还提到多进程访问的并发安全性。这里需要考虑SO库是否设计为支持多进程环境。如果库依赖于进程内的静态变量,那么多进程同时调用可能导致各自进程内的数据一致,但会跨进程影响。但如果库使用了跨进程共享资源,比如操作同一个文件,就需要使用进程间同步机制,如文件锁。 另外,引用[3]提到系统在加载SO时根据ABI选择目录,如果同APK中的SO路径同,可能实际加载的是同的文件,这时候并发调用实际上是在同实例中,互影响。但如果多个APK加载的是同一个物理文件(比如系统库),则可能存在共享代码段,但数据段各自独立。 总结来说,是否支持并发调用取决于SO库的实现和加载方式。需要检查库的线程安全性和进程隔离情况。如果库是线程安全且无共享资源依赖,那么多线程和多进程调用是可行的;否则需要额外处理同步问题。 可能用户的实际场景是多个应用需要调用同一个SO库的功能,担心并发时的稳定性。需要建议他们检查SO库的设计,确保关键部分有同步机制,并测试多进程环境下的行为。</think>### 多APK共享SO库的并发调用分析 #### 1. **SO库的内存加载机制** 在Android系统中,每个APK加载的SO库文件虽然可能**物理路径同**(如`/data/app/包名A/lib`和`/data/app/包名B/lib`),但若这些SO库文件**内容完全一致**,系统可能会通过内存映射(mmap)机制共享代码段,但数据段(如全局变量)会独立分配给每个进程[^2][^3]。 例如,若两个APK均包含`libfoo.so`且MD5值相同,但安装后位于同路径,则系统会为每个APK的进程独立加载该库的实例。 #### 2. **并发调用的线程安全性** - **线程安全条件**: SO库中方法的并发调用是否安全,取决于其内部实现: - 若方法**无共享状态**(如仅使用局部变量或线程安全的数据结构),则支持多线程并发调用。 - 若方法依赖**全局变量/静态变量**,则需通过锁(如`pthread_mutex`)或原子操作保证同步。否则可能导致数据竞争(Data Race)或逻辑错误。 - **示例代码(非线程安全)**: ```c static int counter = 0; // 全局变量 void unsafe_increment() { counter++; // 非原子操作,多线程调用会导致竞争 } ``` #### 3. **多进程访问的隔离性** - **进程间内存隔离**: 同APK进程加载的SO库实例**数据段完全独立**,即使代码段共享。例如,APK A和APK B调用`libfoo.so`中的全局变量`int x`,实际访问的是各自进程内的独立内存区域。 - **跨进程共享资源**: 若SO库操作**外部共享资源**(如文件、共享内存),需通过进程间同步机制(如文件锁、Binder)协调访问。 #### 4. **并发场景下的建议** 1. **验证SO库设计**: - 使用`nm`工具检查符号表,确认是否存在全局变量(如`BSS`或`DATA`)。 - 若库提供方声明为“线程安全”,需通过压力测试验证。 2. **优化同步机制**: - 对关键代码段添加互斥锁(参考`pthread_mutex_lock`)。 - 避免跨进程共享非线程安全资源。 3. **动态加载验证**: 通过`dlopen`和`dlsym`动态加载SO库,结合日志监控并发调用行为。 #### 5. **系统限制与风险** - **重复SO的风险**: 若同APK包含同名但同版本的SO库,可能导致符号冲突或内存泄漏。 - **ABI兼容性**: 若APK未正确声明支持的ABI(如armeabi-v7a与arm64-v8a混合),可能触发兼容性异常。 ---
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值