android底层库libutils之thread研究

本文深入解析 Android 中 Thread 类的工作原理及实现机制,包括线程创建、启动过程、关键方法的作用等,并通过具体示例帮助理解。

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

一.初步认识

        Thread 类其实就是对Linux api做的一个封装,它的代码是在/sysem/core/libutils/thread.cpp。在Android service和camera 流处理中我们都会发现它的影子。就我个人来说,在没看研究thread类时,虽然我知道它是一个线程类,但是用起来还是心里发虚。

例如在Camera service中,各种帧流都会开一个线程来处理,代码在framework/av/services/camera/libcameraservice/api1/camera2client.cpp  initialize方法中。

    mStreamingProcessor = new StreamingProcessor(this); 
    threadName = String8::format("C2-%d-StreamProc",
            mCameraId);
    mStreamingProcessor->run(threadName.string());  //preview,video流处理线程

    mFrameProcessor = new FrameProcessor(mDevice, this);
    threadName = String8::format("C2-%d-FrameProc",
            mCameraId);
    mFrameProcessor->run(threadName.string());

    mCaptureSequencer = new CaptureSequencer(this);  
    threadName = String8::format("C2-%d-CaptureSeq",
            mCameraId);
    mCaptureSequencer->run(threadName.string()); //拍照处理线程

二.源码探索

Android源码中Thread类的定义如下,其实google已经写了很多注释了,大家还是看看原汁原味吧。

class Thread : virtual public RefBase
{
public:
    // Create a Thread object, but doesn't create or start the associated
    // thread. See the run() method.
                        Thread(bool canCallJava = true);  //canCalljava表示这个函数是否使用JNI函数,这里可以看到它默认为true,请继续看下面的代码创建。
    virtual             ~Thread();

    // Start the thread in threadLoop() which needs to be implemented.  
    virtual status_t    run(    const char* name = 0,
                                int32_t priority = PRIORITY_DEFAULT,
                                size_t stack = 0);
    
    // Ask this object's thread to exit. This function is asynchronous, when the
    // function returns the thread might still be running. Of course, this
    // function can be called from a different thread.
    virtual void        requestExit();

    // Good place to do one-time initializations
    virtual status_t    readyToRun();
    
    // Call requestExit() and wait until this object's thread exits.
    // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
    // this function from this object's thread. Will return WOULD_BLOCK in
    // that case.
            status_t    requestExitAndWait();

    // Wait until this object's thread exits. Returns immediately if not yet running.
    // Do not call from this object's thread; will return WOULD_BLOCK in that case.
            status_t    join();

    // Indicates whether this thread is running or not.
            bool        isRunning() const;

#ifdef HAVE_ANDROID_OS
    // Return the thread's kernel ID, same as the thread itself calling gettid() or
    // androidGetTid(), or -1 if the thread is not running.
            pid_t       getTid() const;
#endif

protected:
    // exitPending() returns true if requestExit() has been called.
            bool        exitPending() const;
    
private:
    // Derived class must implement threadLoop(). The thread starts its life
    // here. There are two ways of using the Thread object:
    // 1) loop: if threadLoop() returns true, it will be called again if
    //          requestExit() wasn't called.
    // 2) once: if threadLoop() returns false, the thread will exit upon return.
    virtual bool        threadLoop() = 0;

private:
    Thread& operator=(const Thread&);
    static  int             _threadLoop(void* user);
    const   bool            mCanCallJava;
    // always hold mLock when reading or writing
            thread_id_t     mThread;
    mutable Mutex           mLock;
            Condition       mThreadExitedCondition;
            status_t        mStatus;
    // note that all accesses of mExitPending and mRunning need to hold mLock
    volatile bool           mExitPending;
    volatile bool           mRunning;
            sp<Thread>      mHoldSelf;
#ifdef HAVE_ANDROID_OS
    // legacy for debugging, not used by getTid() as it is set by the child thread
    // and so is not initialized until the child reaches that point
            pid_t           mTid;
#endif
};

上面类定义中看到很多内容,其实真正创建线程的地方是在run方法中,我们来看一下它的run函数。

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    ........
    // reset status and exitPending to their default value, so we can
    // try again after an error happened (either below, or in readyToRun())
    mStatus = NO_ERROR;
    mExitPending = false;
    mThread = thread_id_t(-1);
    
    // hold a strong reference on ourself
    mHoldSelf = this;

    mRunning = true;  //线程运行标志位为true,表示线程运行起来了。

    bool res;
    if (mCanCallJava) { //之前从Thread类中发现这个值为真
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }
    ..........
}
上面由类声明中我们知道mCanCallJava为真,我们可以发现if else中传入的参数都是一样的,我们来跟一下createThreadEtc代码。代码:system/core/include/utils/AndroidThreads.h

// Create thread with lots of parameters
inline bool createThreadEtc(thread_func_t entryFunction,
                            void *userData,
                            const char* threadName = "android:unnamed_thread",
                            int32_t threadPriority = PRIORITY_DEFAULT,
                            size_t threadStackSize = 0,
                            thread_id_t *threadId = 0)
{
    return androidCreateThreadEtc(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId) ? true : false;
}
上面我们看到,它又调用了androidCreateThreadEtc,我们继续来跟一下。

int androidCreateThreadEtc(android_thread_func_t entryFunction,
                            void *userData,
                            const char* threadName,
                            int32_t threadPriority,
                            size_t threadStackSize,
                            android_thread_id_t *threadId)
{
    return gCreateThreadFn(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId);
}

void androidSetCreateThreadFunc(android_create_thread_fn func)
{
    gCreateThreadFn = func; //这里是其它地方注册了一个回调函数。
}
上面androidCreateThreadEtc又调用了一个函数指针,这个函数指针是通过androidSetCreateThreadFunc()注册下来的。我们来看看androidSetCreateThreadFunc()是在什么地方调用的。

在运行时刚开始的时候,就会调用startReg()注册这个回调。

/*
 * Register android native functions with the VM.
 */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
........
}

我们可以看到注册下去的是javaCreateThreadEtc()它的内容如下。

/*
 * This is invoked from androidCreateThreadEtc() via the callback
 * set with androidSetCreateThreadFunc().
 *
 * We need to create the new thread in such a way that it gets hooked
 * into the VM before it really starts executing.
 */
/*static*/ int AndroidRuntime::javaCreateThreadEtc(
                                android_thread_func_t entryFunction, //注意这里entryFunction = _threadLoop
                                void* userData,
                                const char* threadName,
                                int32_t threadPriority,
                                size_t threadStackSize,
                                android_thread_id_t* threadId)
{
    void** args = (void**) malloc(3 * sizeof(void*));   // javaThreadShell must free
    int result;

    if (!threadName)
        threadName = "unnamed thread";

    args[0] = (void*) entryFunction;
    args[1] = userData;
    args[2] = (void*) strdup(threadName);   // javaThreadShell must free

    result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,
        threadName, threadPriority, threadStackSize, threadId);
    return result;
}
上面可以看到又回到了androidCreateRawThreadEtc();我么可以发现它注册的function是AndroidRuntime::javaThreadShell(),其实在后续的处理还是会注册_threadLoop(),接口,而在_threadLoop()接口中又会循环调用用户自己定义的threadLoop().

/*
 * When starting a native thread that will be visible from the VM, we
 * bounce through this to get the right attach/detach action.
 * Note that this function calls free(args)
 */
/*static*/ int AndroidRuntime::javaThreadShell(void* args) {
    void* start = ((void**)args)[0];
    void* userData = ((void **)args)[1]; //这个参数2是我们的thread_loop
    char* name = (char*) ((void **)args)[2];        // we own this storage
    free(args);
    JNIEnv* env;
    int result;

    /* hook us into the VM */
    if (javaAttachThread(name, &env) != JNI_OK)
        return -1;

    /* start the thread running */
    result = (*(android_thread_func_t)start)(userData);

    /* unhook us */
    javaDetachThread();
    free(name);

    return result;
}

上面是android运行时的代码,后面我们分析android运行时的代码时,在分析这里代码。接下来我们来看一下,大家魂牵梦绕的loop函数。其实搞懂这个函数,我们大概也就知道thread类的运作机制了。

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user); //这里user就是继承了thread类的子类对象,其中实现了threadLoop方法。

    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> weak(strong);
    self->mHoldSelf.clear();

#ifdef HAVE_ANDROID_OS
    // this is very useful for debugging with gdb
    self->mTid = gettid();
#endif

    bool first = true; //能到这个地方,肯定是第一次进来。

    do {
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun(); //其实readyTorun直接返回的就是NO_ERROR.
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) { 
                 //第一次刚创建线程时走这个分支
                 //线程还没要退出,exitPending()得到就是0了。
                result = self->threadLoop(); //运行用户自己的LOOP函数。
            }
        } else {
            result = self->threadLoop(); //走到这里已经不是第一次进来了,如果线程没有意愿要退出,那就继续执行用户自己的LOOP函数。
        }

        // establish a scope for mLock
        {
        Mutex::Autolock _l(self->mLock);
        if (result == false || self->mExitPending) { //到这来就是要退出了,在官方源码中注释有在操作mExitPending时一定要加锁。
            self->mExitPending = true;
            self->mRunning = false;
            // clear thread ID so that requestExitAndWait() does not exit if
            // called by a new thread using the same thread ID as this one.
            self->mThread = thread_id_t(-1);
            // note that interested observers blocked in requestExitAndWait are
            // awoken by broadcast, but blocked on mLock until break exits scope
            self->mThreadExitedCondition.broadcast();//这一条广播消息,唤醒那些对该线程"感兴趣"的观察者。后面博文会有介绍
            break;
        }
        }
        
        // Release our strong reference, to let a chance to the thread
        // to die a peaceful death.
        strong.clear();
        // And immediately, re-acquire a strong reference for the next loop
        strong = weak.promote(); //弱生强,为下一次循环做准备。
    } while(strong != 0); 
    
    return 0;
}
关于_thread_Loop,我们就介绍到这里,大家要认清,threadLoop运行在一个循环当中,它的创建是由run方法引发的,所以大家只要在调用的地方看到run方法,就说明创建线程了。而在很多情况下,我们看不到调用run方法的地方,其实如果你找不到run方法地方,可以去对象onFirstRef()方法中看看。

总结:大家在开发中,如果只是看代码了解架构的话,一开始不需要过多扣细节。大概知道怎么用的就行。像这里的Thread类,我们只需要知道调用了run方法,就创建了一个线程,此外一定要保证我们子类中的threadLopp方法要写好。还是看代码吧。

----- pid 15917 at 2025-06-17 16:01:29.796255295+0300 ----- Cmd line: /system/bin/audioserver "binder:15917_4" sysTid=16095 #00 pc 00000000000b7750 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #01 pc 00000000000925e0 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_pi_lock_ex(void volatile*, bool, bool, timespec const*)+332) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #02 pc 00000000000a2944 /apex/com.android.runtime/lib64/bionic/libc.so (PIMutexTimedLock(PIMutex&, bool, timespec const*)+212) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #03 pc 00000000000b1b60 /system/lib64/libc++.so (std::__1::mutex::lock()+12) (BuildId: 53e0091d25a788802d2d3a5324f79b527df4913f) #04 pc 00000000000e57e8 /system/bin/audioserver (android::audio_utils::mutex_impl<android::audio_utils::AudioMutexAttributes>::lock()+140) (BuildId: a5c5911886560f96215f83fa00076034) #05 pc 000000000021b2f4 /system/bin/audioserver (android::AudioPolicyService::getDevicesForAttributes(android::media::audio::common::AudioAttributes const&, bool, std::__1::vector<android::media::audio::common::AudioDevice, std::__1::allocator<android::media::audio::common::AudioDevice>>*)+260) (BuildId: a5c5911886560f96215f83fa00076034) #06 pc 0000000000039dfc /system/lib64/audiopolicy-aidl-cpp.so (android::media::BnAudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+6320) (BuildId: 42f83b19a621bcae5536880c786c936d) #07 pc 00000000001f5570 /system/bin/audioserver (android::AudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+1112) (BuildId: a5c5911886560f96215f83fa00076034) #08 pc 0000000000056524 /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+1148) (BuildId: 217d4a46760a940dfed21538f547262d) #09 pc 0000000000054a08 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+444) (BuildId: 217d4a46760a940dfed21538f547262d) #10 pc 000000000007ec7c /system/lib64/libbinder.so (android::PoolThread::threadLoop()+92) (BuildId: 217d4a46760a940dfed21538f547262d) #11 pc 000000000000e5cc /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+252) (BuildId: d1d3bf8fe6fecd6921ac714b5ef42e28) #12 pc 00000000000a14e8 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+196) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #13 pc 000000000009394c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) 找到当前线程等待调用返回的目标进程为15917,后面的信息怎么理解
最新发布
07-04
<think>我们正在分析一个崩溃日志,特别是关于binder线程的调用栈。用户已经指出目标进程是15917(audioserver),现在需要解释调用栈中的信息。 调用栈中的每一行代表一个函数调用,格式为: #序号 pc 程序计数器地址 库路径 (函数名+偏移) (BuildId) 我们需要解释这些字段的含义,并分析这个调用栈所反映的问题。 关键点: 1. 线程名:"binder:15917_4" 表示这是进程15917的binder线程4号。 2. sysTid=16095 是操作系统级别的线程ID。 3. 调用栈从下往上读,最下面是最后调用的函数(即函数调用链的起点),最上面是当前执行的位置。 分析调用栈: 从最底层开始(#13)直到最顶层(#00): #13 是线程启动的通用入口(__start_thread) #12 是pthread的启动函数(__pthread_start) #11 是libutilsThread类的线程循环(_threadLoop) #10 是libbinder中PoolThread的线程循环(threadLoop),表明这是一个binder线程池中的线程。 #09 和 #08 是IPCThreadState处理binder事务的命令执行(joinThreadPool和executeCommand) #07 和 #06 是AudioPolicyService处理具体的binder事务(onTransact)以及调用了BnAudioPolicyService的onTransact。 #05 是AudioPolicyService的getDevicesForAttributes方法,这里进行了锁操作(mutex的lock) #04 是mutex_impl的lock方法(属于audio_utils命名空间) #03 是libc++中mutex的lock方法 #02 和 #01 是futex相关的锁操作(__futex_pi_lock_ex和syscall) #00 是系统调用(syscall) 从调用栈可以看出,这个线程在等待一个互斥锁(mutex)时被阻塞。具体来说: - 在#05处,AudioPolicyService::getDevicesForAttributes方法试图获取一个锁。 - 这个锁在#04被锁定(mutex_impl::lock) - 然后通过libc++的mutex::lock(#03)进入底层锁操作 - 最终通过futex系统调用(#00)进入内核等待。 因此,当前线程(binder:15917_4)在等待一个锁,而这个锁被其他线程持有,导致它挂起。 现在,用户问的是“后面的信息怎么理解”,即调用栈后面的部分(从#00到#13)如何解读。 我们按字段解释: pc: 程序计数器(Program Counter)地址,表示当前函数调用在内存中的位置。 库路径: 函数所在的共享库或可执行文件路径。 函数名+偏移: 函数名后面跟着的十六进制数字表示从函数入口点的偏移量(以字节为单位)。 BuildId: 库的构建ID,用于唯一标识库的版本。 例如: "#00 pc 00000000000b7750 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32)" 表示: 程序计数器地址:0xb7750 库文件:libc.so 函数:syscall,且当前指令在syscall函数入口点之后32字节处。 这个调用栈的意义:它显示了线程在挂起时的调用链。当前线程在syscall中等待(状态为S,可中断睡眠),因为它在等待一个futex(快速用户态互斥锁)的释放。 另外,注意这个调用栈是在audioserver进程(pid=15917)中的,线程tid=16095。 总结:该binder线程在处理一个来自客户端的请求(getDevicesForAttributes)时,需要获取一个锁,但该锁已被占用,于是线程进入休眠状态,等待锁释放。 根据规则,最后需要提出3-5个相关问题。</think>### Android Native 崩溃日志解析(Binder线程) #### 一、日志关键信息解读 1. **进程标识**: - `pid 15917`:崩溃进程ID(audioserver系统服务) - `Cmd line: /system/bin/audioserver`:进程执行路径 - `"binder:15917_4"`:Binder线程名(进程15917的第4个Binder线程) - `sysTid=16095`:操作系统级线程ID(与之前事务日志中的目标线程匹配) 2. **调用栈结构**: ```plaintext #00 pc 00000000000b7750 /apex/.../libc.so (syscall+32) ... #13 pc 000000000009394c /apex/.../libc.so (__start_thread+68) ``` - `#00`:当前执行位置(最顶层) - `#13`:初始调用位置(最底层) - `pc XXXXXX`:程序计数器地址(代码段偏移量) - 路径:函数所在库文件(如 `libc.so`、`libbinder.so`) - `(函数名+偏移)`:函数签名及指令偏移量 - `BuildId`:库文件构建标识(用于版本匹配) --- #### 二、调用栈关键节点分析 | 层级 | 库文件 | 函数签名 | 关键作用 | |------|--------|----------|----------| | **#00** | libc.so | `syscall+32` | 进入内核态执行系统调用 | | **#01-02** | libc.so | `__futex_pi_lock_ex()` | Futex锁实现(用户态锁) | | **#03** | libc++.so | `std::mutex::lock()` | C++标准互斥锁 | | **#04** | audioserver | `mutex_impl::lock()` | 音频服务自定义锁封装 | | **#05** | audioserver | `AudioPolicyService::getDevicesForAttributes()` | **核心业务逻辑**(音频设备查询) | | **#06** | audiopolicy-aidl-cpp.so | `BnAudioPolicyService::onTransact()` | Binder事务反序列化处理 | | **#07** | audioserver | `AudioPolicyService::onTransact()` | Binder事务分发入口 | | **#08-09** | libbinder.so | `IPCThreadState::executeCommand()` | Binder命令执行引擎 | | **#10** | libbinder.so | `PoolThread::threadLoop()` | Binder线程池运行循环 | | **#11** | libutils.so | `Thread::_threadLoop()` | 线程基础框架 | | **#12-13** | libc.so | `__pthread_start()` | 线程启动入口 | --- #### 三、死锁场景还原 ```mermaid sequenceDiagram participant Client as 客户端进程 participant Binder as Binder驱动 participant Server as audioserver(15917) Client->>Binder: 发送事务请求(getDevicesForAttributes) Binder->>Server: 分配至binder:15917_4(tid=16095) Server->>Server: 请求锁(mutex_impl::lock) Server->>Binder: 通过syscall进入内核等待 Note right of Server: 锁被其他线程持有<br/>线程阻塞在#00 ``` #### 四、问题诊断要点 1. **阻塞位置**: - 线程停止在 `syscall+32`(内核态等待) - 阻塞源:`mutex_impl::lock()`(用户态锁) 2. **锁竞争分析**: - 锁由 `AudioPolicyService::getDevicesForAttributes` 请求 - 该函数通过Binder事务触发(`code=0x1c`) 3. **调用链特征**: ```python # 关键调用深度统计 binder_layer = 4 # #06-#09 (Binder框架层) service_layer = 3 # #05,#07 (服务逻辑层) syscall_layer = 4 # #00-#03 (系统调用层) ``` 4. **死锁风险点**: - 音频服务在持有锁时处理新Binder请求 - 锁等待超时未设置(`pri 0:120`表示120ms超时) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值