Fatal signal 11 (SIGSEGV) at 0x0000130f (code=1), thread xxx (Thread-xx)

本文介绍了在Android应用中遇到的一个因调用SO库导致的程序崩溃问题。通过日志分析,发现.so库在赋值过程中超过了传入数组的长度,从而引发崩溃。尽管整个过程在try...catch中,但错误发生在单独的线程中,使得异常无法被捕获。解决方法包括修改.so库或确保传递足够大的数组。此外,建议使用`Thread.setDefaultUncaughtExceptionHandler`来捕获此类线程外异常。

导致应用程序崩溃问题分析与解决:

先展示与问题相关的代码片

09-04 13:26:32.826 F/libc    (  572): Fatal signal 11 (SIGSEGV) at 0x0000130f (code=1), xxxx 844 (Thread-46)

09-04 13:26:32.936 I/DEBUG   (  103): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

09-04 13:26:32.936 I/DEBUG   (  103): Build fingerprint: 'rockchip/rk3188/rk3188:4.4.4/KTU84Q/eng.root.20151128.163335:eng/test-keys'

09-04 13:26:32.936 I/DEBUG   (  103): Revision: '0'

09-04 13:26:32.936 I/DEBUG   (  103): pid: 572, tid: 844, name: Thread-46  >>> com.xxx.xxxxxx <<<

09-04 13:26:32.936 I/DEBUG   (  103): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0000130f

09-04 13:26:33.016 I/DEBUG   (  103):     r0 000012ef  r1 41e8e2a8  r2 fffffea0  r3 415d7c74

09-04 13:26:33.016 I/DEBUG   (  103):     r4 42741d98  r5 415dc1f0  r6 00000000  r7 41e8e2a8

09-04 13:26:33.016 I/DEBUG   (  103):     r8 80000000  r9 415644f0  sl 41e8e2a8  fp 42741e00

09-04 13:26:33.016 I/DEBUG   (  103):     ip 41e8f1e8  sp 6ccb2b28  lr 41555efc  pc 415642d8  cpsr 800f0010

09-04 13:26:33.016 I/DEBUG   (  103):     d0  0000000000000000  d1  0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d2  0000000000000000  d3  0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d4  3ff0000000000000  d5  529292949610636a

09-04 13:26:33.016 I/DEBUG   (  103):     d6  0000001900000000  d7  4040000000000003

09-04 13:26:33.016 I/DEBUG   (  103):     d8  4040000000000003  d9  0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d10 0000000000000000  d11 0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d12 0000000000000000  d13 0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d14 0000000000000000  d15 0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d16 000000000000001c  d17 090a0d3e31bc80e5

09-04 13:26:33.016 I/DEBUG   (  103):     d18 3ff0000000000000  d19 0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d20 0000000000000000  d21 000c000c000c000c

09-04 13:26:33.016 I/DEBUG   (  103):     d22 0010001000100010  d23 0000000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d24 0000000000000000  d25 3980000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d26 0707070703030303  d27 e03b3b3bd74e4e4e

09-04 13:26:33.016 I/DEBUG   (  103):     d28 7a14141410000000  d29 2ca8000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     d30 3ff0000000000000  d31 4024000000000000

09-04 13:26:33.016 I/DEBUG   (  103):     scr 80000010

09-04 13:26:33.026 I/DEBUG   (  103): 

09-04 13:26:33.026 I/DEBUG   (  103): backtrace:

09-04 13:26:33.026 I/DEBUG   (  103):     #00  pc 000372d8  /system/lib/libdvm.so

09-04 13:26:33.026 I/DEBUG   (  103):     #01  pc 00028ef8  /system/lib/libdvm.so (dvmHeapBitmapScanWalk(HeapBitmap*, void (*)(Object*, void*, void*), void*)+100)

–复现

多的不说直入主题,在项目中有个需要调用so库其中的一些方法获取返回值的操作,就是把固定大小的byte数组当参数传进去,.so里的方法负责给传过来的数组赋值,我这边再拿到该数组进行下一步处理。

关键是整个过程都是在try…catch中,按道理说就算有错也不至于程序崩溃,打印logcat除了上面的代码外找不到其他有用信息,最后经过多次复现观察发现,.so是将原数组整个赋值给传进去的数组的,所以传进去的时候数组固定在合理范围内的固定长度,而在调用.so里的方法时某一时刻赋值的数组长度远远超过传进去的数组长度,程序立马崩溃,于是就确定找到了问题的根源。

–分析

虽然整个过程在try…catch中,但要知道不是所有代码在其之中就不会产生严重问题导致程序崩溃,比如try…catch中new一个Thread,该Thread中发生错误catch也是捕捉不到的。
因为程序调用.so的方法,所以该方法也是运行在你的程序之中的,里面的方法产生错误也就代表你的程序产生了错误,崩溃理所当然。

至于更对这种问题更深层的理解,建议多百度找找这方面的知识,限于本人能力有限,就分析到此。

–解决

找公司写.so的同事,商量解决或你的程序在传数组的时候传入长度更大的数组,当然多数情况还是.so的问题。

最后

这可能是产生该问题的一种情况,不是可能是一定!
很多时候是无从下手的,推荐使用java里的
Thread.setDefaultUncaughtExceptionHandler方法进行捕捉线程之外的问题,多数情况下还是有用的,至于怎么使用请自行百度或者留言探讨。

### 修复 Android JNI 中 `while` 循环刷新 Surface 导致的 `SIGSEGV` 错误 在 Android 的 JNI 开发中,使用 `while` 徢环不断刷新数据并更新到 `Surface` 是常见的需求,例如在实时渲染或图像处理中。然而,不当的实现可能导致严重的运行时错误,如 `Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR)`,即段错误,通常表示访问了未映射的内存地址。 #### 1. 检查 `ANativeWindow` 是否为空或已被释放 在 `while` 循环中访问 `ANativeWindow` 时,必须确保其不为空且未被释放。如果在 `Surface` 销毁后仍然尝试绘制,将导致非法内存访问。 ```cpp while (isRunning) { if (window == nullptr) continue; ANativeWindow_Buffer buffer; if (ANativeWindow_lock(window, &buffer, nullptr) == 0) { // 更新帧数据 ANativeWindow_unlockAndPost(window); } } ``` 在 `Surface` 被销毁时(如调用 `surfaceDestroyed`),应立即释放 `ANativeWindow` 并终止线程。 #### 2. 确保线程安全和同步机制 JNI 中的渲染线程与 Java 层的 UI 线程是分离的,必须确保线程间的数据访问是同步的。例如,当 `Surface` 被销毁时,应通过标志位 `isRunning` 通知渲染线程退出循环,避免访问已释放的资源[^2]。 ```cpp bool isRunning = false; extern "C" JNIEXPORT void JNICALL Java_edu_example_NativeRenderer_stopRendering(JNIEnv* env, jobject /* this */) { isRunning = false; if (window) { ANativeWindow_release(window); window = nullptr; } } ``` #### 3. 使用 `Handler` 或主线程调用 JNI 方法 如果某些 JNI 方法需要操作 UI 或 `Surface`,应通过 `Handler` 在主线程中调用,避免在子线程中直接操作 `Surface`,否则可能因线程调度不一致导致内存访问异常。 ```java new Handler(Looper.getMainLooper()).post(() -> { startRendering(holder); }); ``` #### 4. 检查权限问题 某些操作(如从 SD 卡读取图像数据)可能需要动态权限申请。如果应用未请求 `READ_EXTERNAL_STORAGE` 或 `WRITE_EXTERNAL_STORAGE`,可能导致访问非法内存地址而崩溃[^1]。 ```java if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } ``` #### 5. 使用 `addr2line` 定位崩溃地址 当发生 `SIGSEGV` 错误时,可通过 `addr2line` 工具定位具体出错的代码位置,帮助排查非法内存访问问题[^4]。 ```bash arm-linux-androideabi-addr2line -C -f -e libnative-lib.so 00026877 ``` #### 6. 避免资源竞争和重复释放 确保 `ANativeWindow_fromSurface` 和 `ANativeWindow_release` 成对调用,防止重复释放或多次绑定同一个 `Surface`,这可能导致内存访问冲突。 --- ### 示例代码:修复后的 JNI 渲染线程逻辑 ```cpp #include <jni.h> #include <android/native_window.h> #include <android/native_window_jni.h> #include <pthread.h> #include <unistd.h> ANativeWindow* window = nullptr; bool isRunning = false; void* renderThread(void*) { while (isRunning) { if (window == nullptr) { usleep(10000); // 等待Surface可用 continue; } ANativeWindow_Buffer buffer; if (ANativeWindow_lock(window, &buffer, nullptr) == 0) { // 填充像素数据 uint32_t* pixels = static_cast<uint32_t*>(buffer.bits); int pixelCount = buffer.width * buffer.height; for (int i = 0; i < pixelCount; ++i) { pixels[i] = 0xFF00FF00; // 绿色 } ANativeWindow_unlockAndPost(window); } else { // 锁定失败,短暂休眠避免CPU占用过高 usleep(10000); } usleep(33000); // 控制帧率 } return nullptr; } extern "C" JNIEXPORT void JNICALL Java_edu_example_NativeRenderer_startRendering(JNIEnv* env, jobject /* this */, jobject surface) { if (window != nullptr) { ANativeWindow_release(window); } window = ANativeWindow_fromSurface(env, surface); isRunning = true; pthread_t thread; pthread_create(&thread, nullptr, renderThread, nullptr); } extern "C" JNIEXPORT void JNICALL Java_edu_example_NativeRenderer_stopRendering(JNIEnv* /* env */, jobject /* this */) { isRunning = false; if (window) { ANativeWindow_release(window); window = nullptr; } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值