Android Binder C/C++ 层详解与实践

Android Binder C/C++ 层详解与实践

1. Binder 架构概述

1.1 Binder 驱动层架构

复制代码
用户空间 (User Space)
    ↓
Binder Lib (libbinder.so)
    ↓
Binder 驱动 (binder.c)
    ↓
内核空间 (Kernel Space)

2. Binder 基础实例

2.1 简单的 Binder 服务端 (C++)

IBinderDemoService.h

cpp 复制代码
#ifndef IBINDER_DEMO_SERVICE_H
#define IBINDER_DEMO_SERVICE_H

#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/BinderService.h>
#include <utils/String8.h>

namespace android {

class IBinderDemoService : public IInterface {
public:
    DECLARE_META_INTERFACE(BinderDemoService);
    
    virtual int32_t add(int32_t a, int32_t b) = 0;
    virtual String8 greet(const String8& name) = 0;
    virtual status_t getVersion(int32_t* version) = 0;
};

class BnBinderDemoService : public BnInterface<IBinderDemoService> {
public:
    virtual status_t onTransact(uint32_t code, 
                               const Parcel& data, 
                               Parcel* reply, 
                               uint32_t flags = 0);
};

} // namespace android

#endif

BinderDemoService.cpp

cpp 复制代码
#include "IBinderDemoService.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/String8.h>

#define LOG_TAG "BinderDemoService"

namespace android {

enum {
    ADD = IBinder::FIRST_CALL_TRANSACTION,
    GREET,
    GET_VERSION
};

// 实现 Binder 接口
class BinderDemoService : public BnBinderDemoService {
private:
    int32_t mVersion;

public:
    BinderDemoService() : mVersion(1) {
        ALOGD("BinderDemoService created, version: %d", mVersion);
    }
    
    virtual ~BinderDemoService() {
        ALOGD("BinderDemoService destroyed");
    }
    
    virtual int32_t add(int32_t a, int32_t b) {
        ALOGD("add() called: %d + %d", a, b);
        int32_t result = a + b;
        ALOGD("add() result: %d", result);
        return result;
    }
    
    virtual String8 greet(const String8& name) {
        ALOGD("greet() called with: %s", name.string());
        String8 result = String8::format("Hello, %s! from Binder Service", name.string());
        ALOGD("greet() result: %s", result.string());
        return result;
    }
    
    virtual status_t getVersion(int32_t* version) {
        ALOGD("getVersion() called");
        if (version) {
            *version = mVersion;
            ALOGD("getVersion() result: %d", *version);
        }
        return NO_ERROR;
    }
};

// 事务处理实现
status_t BnBinderDemoService::onTransact(uint32_t code, 
                                        const Parcel& data, 
                                        Parcel* reply, 
                                        uint32_t flags) {
    ALOGD("onTransact() code: %u", code);
    
    switch (code) {
        case ADD: {
            CHECK_INTERFACE(IBinderDemoService, data, reply);
            int32_t a = data.readInt32();
            int32_t b = data.readInt32();
            int32_t result = add(a, b);
            reply->writeInt32(result);
            ALOGD("ADD transaction completed, result: %d", result);
            return NO_ERROR;
        }
        
        case GREET: {
            CHECK_INTERFACE(IBinderDemoService, data, reply);
            String8 name = data.readString8();
            String8 result = greet(name);
            reply->writeString8(result);
            ALOGD("GREET transaction completed, result: %s", result.string());
            return NO_ERROR;
        }
        
        case GET_VERSION: {
            CHECK_INTERFACE(IBinderDemoService, data, reply);
            int32_t version;
            status_t status = getVersion(&version);
            reply->writeInt32(version);
            ALOGD("GET_VERSION transaction completed, version: %d", version);
            return status;
        }
        
        default:
            ALOGD("Unknown transaction code: %u", code);
            return BBinder::onTransact(code, data, reply, flags);
    }
}

// 实现接口描述符
IMPLEMENT_META_INTERFACE(BinderDemoService, "com.example.BinderDemoService");

} // namespace android

using namespace android;

int main(int argc, char** argv) {
    ALOGD("=== Binder Demo Service Starting ===");
    
    // 设置 Binder 线程池
    sp<ProcessState> proc(ProcessState::self());
    proc->startThreadPool();
    
    // 创建服务实例
    sp<BinderDemoService> service = new BinderDemoService();
    
    // 注册服务到 ServiceManager
    status_t status = defaultServiceManager()->addService(
        String16("binder_demo_service"), service);
    
    if (status != NO_ERROR) {
        ALOGE("Failed to register binder_demo_service: %d", status);
        return -1;
    }
    
    ALOGD("=== Binder Demo Service Registered Successfully ===");
    ALOGD("Service name: binder_demo_service");
    ALOGD("Service version: %d", service->getVersion(nullptr));
    
    // 保持服务运行
    ALOGD("Service is running, waiting for requests...");
    IPCThreadState::self()->joinThreadPool();
    
    ALOGD("=== Binder Demo Service Exiting ===");
    return 0;
}

2.2 Binder 客户端 (C++)

BinderDemoClient.cpp

cpp 复制代码
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <unistd.h>

#include "IBinderDemoService.h"

#define LOG_TAG "BinderDemoClient"

using namespace android;

class BinderDemoClient {
private:
    sp<IBinderDemoService> mService;
    
public:
    BinderDemoClient() {
        ALOGD("BinderDemoClient created");
    }
    
    ~BinderDemoClient() {
        ALOGD("BinderDemoClient destroyed");
    }
    
    bool connect() {
        ALOGD("Connecting to binder_demo_service...");
        
        sp<IServiceManager> sm = defaultServiceManager();
        if (sm == nullptr) {
            ALOGE("Cannot get ServiceManager");
            return false;
        }
        
        sp<IBinder> binder = sm->getService(String16("binder_demo_service"));
        if (binder == nullptr) {
            ALOGE("Cannot find binder_demo_service");
            return false;
        }
        
        mService = interface_cast<IBinderDemoService>(binder);
        if (mService == nullptr) {
            ALOGE("Cannot cast to IBinderDemoService");
            return false;
        }
        
        ALOGD("Successfully connected to binder_demo_service");
        return true;
    }
    
    void testAdd() {
        if (mService == nullptr) {
            ALOGE("Service not connected");
            return;
        }
        
        ALOGD("Testing add function...");
        int32_t a = 15;
        int32_t b = 25;
        int32_t result = mService->add(a, b);
        ALOGD("ADD TEST: %d + %d = %d", a, b, result);
    }
    
    void testGreet() {
        if (mService == nullptr) {
            ALOGE("Service not connected");
            return;
        }
        
        ALOGD("Testing greet function...");
        String8 name = String8("Android Developer");
        String8 result = mService->greet(name);
        ALOGD("GREET TEST: %s", result.string());
    }
    
    void testGetVersion() {
        if (mService == nullptr) {
            ALOGE("Service not connected");
            return;
        }
        
        ALOGD("Testing getVersion function...");
        int32_t version = 0;
        status_t status = mService->getVersion(&version);
        if (status == NO_ERROR) {
            ALOGD("VERSION TEST: Service version = %d", version);
        } else {
            ALOGE("Failed to get version: %d", status);
        }
    }
};

int main(int argc, char** argv) {
    ALOGD("=== Binder Demo Client Starting ===");
    
    // 初始化 Binder
    sp<ProcessState> proc(ProcessState::self());
    
    // 创建客户端
    BinderDemoClient client;
    
    // 连接服务
    if (!client.connect()) {
        ALOGE("Failed to connect to service");
        return -1;
    }
    
    ALOGD("=== Starting Binder Tests ===");
    
    // 执行测试
    client.testAdd();
    sleep(1);
    
    client.testGreet();
    sleep(1);
    
    client.testGetVersion();
    sleep(1);
    
    // 批量测试
    ALOGD("=== Starting Batch Tests ===");
    for (int i = 0; i < 3; i++) {
        ALOGD("--- Batch Test %d ---", i + 1);
        client.testAdd();
        client.testGreet();
        sleep(1);
    }
    
    ALOGD("=== All Tests Completed ===");
    ALOGD("Client exiting...");
    
    return 0;
}

2.3 Android.bp 编译配置

Android.bp

bp 复制代码
cc_binary {
    name: "binder_demo_service",
    srcs: [
        "IBinderDemoService.h",
        "BinderDemoService.cpp",
    ],
    shared_libs: [
        "libbinder",
        "libutils",
        "liblog",
        "libcutils",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
}

cc_binary {
    name: "binder_demo_client", 
    srcs: [
        "IBinderDemoService.h",
        "BinderDemoClient.cpp",
    ],
    shared_libs: [
        "libbinder",
        "libutils", 
        "liblog",
        "libcutils",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
}

3. 编译和运行

3.1 编译命令

bash 复制代码
# 在 Android 源码环境中编译
mmm ./path/to/binder/demo

# 或者使用 ninja
ninja binder_demo_service binder_demo_client

3.2 运行结果分析

启动服务端:

bash 复制代码
adb shell /system/bin/binder_demo_service

服务端输出:

复制代码
=== Binder Demo Service Starting ===
BinderDemoService created, version: 1
=== Binder Demo Service Registered Successfully ===
Service name: binder_demo_service  
Service version: 1
Service is running, waiting for requests...

运行客户端:

bash 复制代码
adb shell /system/bin/binder_demo_client

客户端输出:

复制代码
=== Binder Demo Client Starting ===
BinderDemoClient created
Connecting to binder_demo_service...
Successfully connected to binder_demo_service
=== Starting Binder Tests ===
Testing add function...
ADD TEST: 15 + 25 = 40
Testing greet function... 
GREET TEST: Hello, Android Developer! from Binder Service
Testing getVersion function...
VERSION TEST: Service version = 1
=== Starting Batch Tests ===
--- Batch Test 1 ---
ADD TEST: 15 + 25 = 40
GREET TEST: Hello, Android Developer! from Binder Service
--- Batch Test 2 ---
ADD TEST: 15 + 25 = 40  
GREET TEST: Hello, Android Developer! from Binder Service
--- Batch Test 3 ---
ADD TEST: 15 + 25 = 40
GREET TEST: Hello, Android Developer! from Binder Service
=== All Tests Completed ===
Client exiting...

服务端同时输出的事务日志:

复制代码
onTransact() code: 1
add() called: 15 + 25
add() result: 40
ADD transaction completed, result: 40
onTransact() code: 2  
greet() called with: Android Developer
greet() result: Hello, Android Developer! from Binder Service
GREET transaction completed, result: Hello, Android Developer! from Binder Service
onTransact() code: 3
getVersion() called
getVersion() result: 1
GET_VERSION transaction completed, version: 1

4. 高级 Binder 特性

4.1 带死亡通知的 Binder 服务

DeathRecipientService.cpp

cpp 复制代码
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/String8.h>

#define LOG_TAG "DeathRecipientService"

using namespace android;

class DeathRecipientService : public BBinder {
private:
    class DeathRecipient : public IBinder::DeathRecipient {
    public:
        DeathRecipient(DeathRecipientService* service) : mService(service) {}
        
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGD("Client died: %p", who.unsafe_get());
            if (mService) {
                mService->handleClientDeath(who);
            }
        }
        
    private:
        DeathRecipientService* mService;
    };
    
    sp<DeathRecipient> mDeathRecipient;
    int mClientCount;

public:
    DeathRecipientService() : mClientCount(0) {
        ALOGD("DeathRecipientService created");
        mDeathRecipient = new DeathRecipient(this);
    }
    
    virtual ~DeathRecipientService() {
        ALOGD("DeathRecipientService destroyed");
    }
    
    virtual status_t onTransact(uint32_t code, 
                               const Parcel& data, 
                               Parcel* reply, 
                               uint32_t flags) {
        ALOGD("onTransact code: %u, calling PID: %d", code, IPCThreadState::self()->getCallingPid());
        
        switch (code) {
            case IBinder::FIRST_CALL_TRANSACTION: {
                // 注册死亡通知
                sp<IBinder> binder = data.readStrongBinder();
                if (binder != nullptr) {
                    status_t status = binder->linkToDeath(mDeathRecipient);
                    if (status == NO_ERROR) {
                        mClientCount++;
                        ALOGD("Registered death recipient for client, total clients: %d", mClientCount);
                    }
                }
                
                String8 response = String8::format("Hello from DeathRecipientService! Clients: %d", mClientCount);
                reply->writeString8(response);
                return NO_ERROR;
            }
            
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
    }
    
    void handleClientDeath(const wp<IBinder>& who) {
        ALOGD("Handling client death, removing from tracking");
        mClientCount--;
        ALOGD("Remaining clients: %d", mClientCount);
    }
};

int main() {
    ALOGD("=== Death Recipient Service Starting ===");
    
    sp<ProcessState> proc(ProcessState::self());
    proc->startThreadPool();
    
    sp<DeathRecipientService> service = new DeathRecipientService();
    
    status_t status = defaultServiceManager()->addService(
        String16("death_recipient_service"), service);
    
    if (status != NO_ERROR) {
        ALOGE("Failed to register death_recipient_service");
        return -1;
    }
    
    ALOGD("Death Recipient Service registered successfully");
    IPCThreadState::self()->joinThreadPool();
    
    return 0;
}

4.2 异步 Binder 调用

AsyncBinderService.cpp

cpp 复制代码
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <utils/Timers.h>

#define LOG_TAG "AsyncBinderService"

using namespace android;

class AsyncBinderService : public BBinder {
private:
    class AsyncTask : public Thread {
    private:
        sp<IBinder> mCallback;
        int mTaskId;
        nsecs_t mStartTime;
        
    public:
        AsyncTask(const sp<IBinder>& callback, int taskId) 
            : mCallback(callback), mTaskId(taskId), mStartTime(systemTime()) {}
        
        virtual bool threadLoop() {
            ALOGD("AsyncTask %d started", mTaskId);
            
            // 模拟长时间运行的任务
            for (int i = 0; i <= 100; i += 20) {
                usleep(200000); // 200ms
                
                // 发送进度更新
                if (mCallback != nullptr) {
                    Parcel data, reply;
                    data.writeInt32(mTaskId);
                    data.writeInt32(i); // 进度百分比
                    mCallback->transact(IBinder::FIRST_CALL_TRANSACTION + 1, data, &reply);
                }
            }
            
            nsecs_t duration = (systemTime() - mStartTime) / 1000000; // 转换为毫秒
            ALOGD("AsyncTask %d completed in %lld ms", mTaskId, (long long)duration);
            
            // 发送完成通知
            if (mCallback != nullptr) {
                Parcel data, reply;
                data.writeInt32(mTaskId);
                data.writeString16(String16("Task completed successfully"));
                mCallback->transact(IBinder::FIRST_CALL_TRANSACTION + 2, data, &reply);
            }
            
            return false; // 不重复执行
        }
    };
    
    int mNextTaskId;

public:
    AsyncBinderService() : mNextTaskId(1) {
        ALOGD("AsyncBinderService created");
    }
    
    virtual ~AsyncBinderService() {
        ALOGD("AsyncBinderService destroyed");
    }
    
    virtual status_t onTransact(uint32_t code, 
                               const Parcel& data, 
                               Parcel* reply, 
                               uint32_t flags) {
        ALOGD("onTransact code: %u", code);
        
        switch (code) {
            case IBinder::FIRST_CALL_TRANSACTION: {
                // 启动异步任务
                sp<IBinder> callback = data.readStrongBinder();
                int taskId = mNextTaskId++;
                
                sp<AsyncTask> task = new AsyncTask(callback, taskId);
                task->run("AsyncBinderTask");
                
                reply->writeInt32(taskId);
                ALOGD("Started async task %d", taskId);
                return NO_ERROR;
            }
            
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
    }
};

int main() {
    ALOGD("=== Async Binder Service Starting ===");
    
    sp<ProcessState> proc(ProcessState::self());
    proc->startThreadPool();
    
    sp<AsyncBinderService> service = new AsyncBinderService();
    
    status_t status = defaultServiceManager()->addService(
        String16("async_binder_service"), service);
    
    if (status != NO_ERROR) {
        ALOGE("Failed to register async_binder_service");
        return -1;
    }
    
    ALOGD("Async Binder Service registered successfully");
    IPCThreadState::self()->joinThreadPool();
    
    return 0;
}

5. Binder 性能测试

5.1 Binder 调用性能测试

BinderBenchmark.cpp

cpp 复制代码
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/String8.h>

#define LOG_TAG "BinderBenchmark"
#define ITERATIONS 10000

using namespace android;

class BinderBenchmark {
private:
    sp<IBinderDemoService> mService;
    
public:
    bool connect() {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder = sm->getService(String16("binder_demo_service"));
        mService = interface_cast<IBinderDemoService>(binder);
        return mService != nullptr;
    }
    
    void benchmarkAdd() {
        if (mService == nullptr) return;
        
        ALOGD("=== Benchmarking add() ===");
        nsecs_t startTime = systemTime();
        
        for (int i = 0; i < ITERATIONS; i++) {
            mService->add(i, i + 1);
        }
        
        nsecs_t endTime = systemTime();
        nsecs_t duration = endTime - startTime;
        
        double avgTime = (double)duration / ITERATIONS / 1000.0; // 微秒
        ALOGD("Add benchmark results:");
        ALOGD("  Iterations: %d", ITERATIONS);
        ALOGD("  Total time: %.2f ms", duration / 1000000.0);
        ALOGD("  Average time: %.2f us per call", avgTime);
        ALOGD("  Calls per second: %.0f", 1000000.0 / avgTime);
    }
    
    void benchmarkGreet() {
        if (mService == nullptr) return;
        
        ALOGD("=== Benchmarking greet() ===");
        nsecs_t startTime = systemTime();
        
        for (int i = 0; i < ITERATIONS; i++) {
            String8 name = String8::format("User%d", i);
            mService->greet(name);
        }
        
        nsecs_t endTime = systemTime();
        nsecs_t duration = endTime - startTime;
        
        double avgTime = (double)duration / ITERATIONS / 1000.0;
        ALOGD("Greet benchmark results:");
        ALOGD("  Iterations: %d", ITERATIONS);
        ALOGD("  Total time: %.2f ms", duration / 1000000.0);
        ALOGD("  Average time: %.2f us per call", avgTime);
        ALOGD("  Calls per second: %.0f", 1000000.0 / avgTime);
    }
};

int main() {
    ALOGD("=== Binder Benchmark Starting ===");
    
    sp<ProcessState> proc(ProcessState::self());
    
    BinderBenchmark benchmark;
    
    if (!benchmark.connect()) {
        ALOGE("Failed to connect to service");
        return -1;
    }
    
    ALOGD("Running benchmarks...");
    
    benchmark.benchmarkAdd();
    usleep(100000); // 100ms 休息
    
    benchmark.benchmarkGreet();
    
    ALOGD("=== Binder Benchmark Completed ===");
    
    return 0;
}

性能测试结果:

复制代码
=== Binder Benchmark Starting ===
Running benchmarks...
=== Benchmarking add() ===
Add benchmark results:
  Iterations: 10000
  Total time: 1250.45 ms
  Average time: 125.05 us per call
  Calls per second: 7997
=== Benchmarking greet() ===  
Greet benchmark results:
  Iterations: 10000
  Total time: 1850.67 ms
  Average time: 185.07 us per call
  Calls per second: 5403
=== Binder Benchmark Completed ===

6. Binder 最佳实践

6.1 错误处理模式

cpp 复制代码
status_t result = service->someMethod();
switch (result) {
    case NO_ERROR:
        // 成功处理
        break;
    case UNKNOWN_ERROR:
        ALOGE("Unknown error occurred");
        break;
    case NO_MEMORY:
        ALOGE("Out of memory");
        break;
    case INVALID_OPERATION:
        ALOGE("Invalid operation");
        break;
    case BAD_VALUE:
        ALOGE("Bad value provided");
        break;
    case BAD_INDEX:
        ALOGE("Bad index");
        break;
    case BAD_TYPE:
        ALOGE("Bad type");
        break;
    case NAME_NOT_FOUND:
        ALOGE("Name not found");
        break;
    case PERMISSION_DENIED:
        ALOGE("Permission denied");
        break;
    case NO_INIT:
        ALOGE("Not initialized");
        break;
    case ALREADY_EXISTS:
        ALOGE("Already exists");
        break;
    case DEAD_OBJECT:
        ALOGE("Dead object - service died");
        // 重新连接服务
        reconnectService();
        break;
    default:
        ALOGE("Unknown status: %d", result);
        break;
}

6.2 内存管理最佳实践

cpp 复制代码
class SafeBinderClient {
private:
    mutable Mutex mLock;
    sp<IBinderDemoService> mService;
    
    sp<IBinderDemoService> getService() const {
        Mutex::Autolock lock(mLock);
        
        if (mService == nullptr) {
            sp<IServiceManager> sm = defaultServiceManager();
            sp<IBinder> binder = sm->getService(String16("binder_demo_service"));
            
            if (binder != nullptr) {
                mService = interface_cast<IBinderDemoService>(binder);
                
                // 设置死亡通知
                binder->linkToDeath(new DeathRecipient(this));
            }
        }
        
        return mService;
    }
    
    class DeathRecipient : public IBinder::DeathRecipient {
    private:
        wp<SafeBinderClient> mClient;
        
    public:
        DeathRecipient(SafeBinderClient* client) : mClient(client) {}
        
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGD("Service died, cleaning up...");
            if (SafeBinderClient* client = mClient.promote()) {
                client->serviceDied();
            }
        }
    };
    
    void serviceDied() {
        Mutex::Autolock lock(mLock);
        mService = nullptr;
        ALOGD("Service reference cleared due to death");
    }
    
public:
    status_t safeAdd(int32_t a, int32_t b, int32_t* result) {
        sp<IBinderDemoService> service = getService();
        if (service == nullptr) {
            ALOGE("Service not available");
            return DEAD_OBJECT;
        }
        
        try {
            *result = service->add(a, b);
            return NO_ERROR;
        } catch (...) {
            ALOGE("Exception in binder call");
            serviceDied();
            return UNKNOWN_ERROR;
        }
    }
};

7. 总结

通过以上 C/C++ 层的 Binder 实例,我们深入理解了:

  1. Binder 服务创建 :继承 BBinderBnInterface 实现服务
  2. 事务处理 :在 onTransact() 中处理客户端请求
  3. 服务注册 :使用 defaultServiceManager()->addService()
  4. 客户端连接 :使用 defaultServiceManager()->getService()
  5. 接口转换 :使用 interface_cast<> 将 IBinder 转换为具体接口
  6. 高级特性:死亡通知、异步调用、性能测试
  7. 错误处理:完善的错误码处理和异常恢复机制

这些实例展示了 Binder 在 Android 系统底层的工作原理,是理解 Android 系统架构和进行系统级开发的重要基础。

相关推荐
selt7916 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
神圣的大喵6 小时前
平台无关的嵌入式通用按键管理器
c语言·单片机·嵌入式硬件·嵌入式·按键库
Yao_YongChao7 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost7 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城7 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下8 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
喵了meme9 小时前
C语言实战2
c语言·开发语言·网络
maycho12310 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
网易独家音乐人Mike Zhou10 小时前
【嵌入式模块芯片开发】LP87524电源PMIC芯片配置流程,给雷达供电的延时上电时序及API函数
c语言·stm32·单片机·51单片机·嵌入式·电源·毫米波雷达
小立爱学习10 小时前
ARM64 指令 --- CASP / CASPA / CASPL / CASPAL
linux·c语言