AIDL Binder机制和原理

news2024/12/22 20:15:18

新的Android 通信规范中,C++层使用AIDL替代HIDL实现Framework和Vendor层之间调用解耦。

我们知道,Binder之间通信,需要一个服务端和一个客户端.

【Binder】

Binder架构分成四层,应用层,Framework层,Native层和内核层

应用层:Java应用层通过调用IActivityManager.bindService,经过层层调用到AMS.bindService;

Framework层:Jave IPC Binder通信采用C/S架构,在Framework层实现BinderProxy和Binder;

Native层:Native IPC,在Native层的C/S架构,实现了BpBinder和BBinder(JavaBBinder);

Kernel层:Binder驱动,运行在内核空间,可共享。其它三层是在用户空间,不可共享。

Binder  IPC原理:

 Binder Service流程:

在这里插入图片描述

 创建Proxy:

   private static class Proxy implements android.app.IActivityManager {
        private android.os.IBinder mRemote;
         Proxy(android.os.IBinder remote) {
                mRemote = remote;
         } 
         ...
           @Override
     public int bindService(android.app.IApplicationThread caller, android.os.IBinder token, android.content.Intent service, String resolvedType, android.app.IServiceConnection connection, int flags, String callingPackage, int userId) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain(); //创建数据Parcel实例
                android.os.Parcel _reply = android.os.Parcel.obtain();//创建返回Parcel实例
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //将ApplicationThread对象传递给systemserver,caller在binder前初始化创建?
                    _data.writeStrongBinder((((caller != null)) ? (caller.asBinder()) : (null)));
                    _data.writeStrongBinder(token);
                    if ((service != null)) {
                        _data.writeInt(1);
                        service.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeString(resolvedType);
                    //将InnerConnection对象传递给systemserver
                    _data.writeStrongBinder((((connection != null)) ? (connection.asBinder()) : (null)));
                    _data.writeInt(flags);
                    _data.writeString(callingPackage);
                    _data.writeInt(userId);
                    //通过bind调用,进入到systemserver
                    mRemote.transact(Stub.TRANSACTION_bindService, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
        }
        ...
    }

【AIDL】

是一个基于Binder通信的接口封装工具(直通式例外,另说),通过定义AIDL接口文件确定Binder客户端和服务通信接口。在软件编译时,AIDL工具根据AIDL接口文件自动生成Interface,客户端Proxy和服务端Stub代码, 模块间调用关系如图:

 

Interface: 服务端接口 + binder功能接口(asBinder)

Proxy: 服务端对应用端提供的客户端代理类(= Binder 客户端 + Inteface transact实现)。服务对外提供接口不直接提供创建的服务实例本身,而是会通过底层Binder服务创建的一个代理服务(伪服务)实例返回给应用端调用。因此伪服务除了继承Interface外,还实现了如何远程调用真服务的接口(基于binder的transact)

private static class Proxy implements com.test.IAIService
    {
 @Override public java.lang.String getServerState() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getServerState, _data, _reply, 0);
          if (!_status) {
            if (getDefaultImpl() != null) {
              return getDefaultImpl().getServerState();
            }
          }
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

Stub:服务端真正继承实现接口的类 + Binder服务端。(onTransact统一接受来自Proxy端的数据,并读取数据调度到真正的服务实现接口)。

而C++层的Binder,Bn端为服务端,Bp端为客户端。所谓Bn、Bp端,实际就是以Bn、Bp为前缀的C++类, 在aidl自动生成工具的接口产物中可以看到(补充产物截图)。以cameraservice为例,cameraservice作为服务端,其继承于BnCameraService

// av/services/camera/libcameraservice/CameraService.h
class CameraService :
    public BinderService<CameraService>,
    public virtual ::android::hardware::BnCameraService,
    public virtual IBinder::DeathRecipient,
    public virtual CameraProviderManager::StatusListener
{
    ……

【接口类】

ICameraService.h有一个ICameraService类,该类是一个接口类,其中所有定义的接口、枚举等都是aidl中所定义的内容,显然它用于定义是Binder机制中的业务接口的,它属于的Binder机制中的接口属性。

 // out/soong/.intermediates/frameworks/av/camera/libcamera_client/android_x86_64_shared/gen/aidl/android/hardware/ICameraService.h
class ICameraService : public ::android::IInterface {
public:
  DECLARE_META_INTERFACE(CameraService) //这是一个宏,里面定义了实例化Bp客户端的方法asInterface
  enum  : int32_t {
    ERROR_PERMISSION_DENIED = 1,
    ERROR_ALREADY_EXISTS = 2,
    ERROR_ILLEGAL_ARGUMENT = 3,
    ERROR_DISCONNECTED = 4,
    ERROR_TIMED_OUT = 5,
    ERROR_DISABLED = 6,
    ……
  virtual ::android::binder::Status getNumberOfCameras(int32_t type, int32_t* _aidl_return) = 0;
  virtual ::android::binder::Status getCameraInfo(int32_t cameraId, ::android::hardware::CameraInfo* _aidl_return) = 0;
  virtual ::android::binder::Status connect(const ::android::sp<::android::hardware::ICameraClient>& client, int32_t cameraId, const ::android::String16& opPackageName, int32_t clientUid, int32_t clientPid, ::android::sp<::android::hardware::ICamera>* _aidl_return) = 0;
 
    ……

  virtual ::android::binder::Status setTorchMode(const ::android::String16& cameraId, bool enabled, const ::android::sp<::android::IBinder>& clientBinder) = 0;
  virtual ::android::binder::Status notifySystemEvent(int32_t eventId, const ::std::vector<int32_t>& args) = 0;
  virtual ::android::binder::Status notifyDeviceStateChange(int64_t newState) = 0;
};  // class ICameraService

ICameraService接口类继承于IInterface类,而IInterface又继承于RefBase,是的,每个C++层Binder必须继承于RefBase,以保证智能指针的使用

class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);
protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};

sp<IBinder> IInterface::asBinder(const IInterface* iface)
{
    if (iface == nullptr) return nullptr;
    return const_cast<IInterface*>(iface)->onAsBinder();
}

// static
sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface)
{
    if (iface == nullptr) return nullptr;
    return iface->onAsBinder();
}

可以看到,IInterface主要提供两个静态接口asBinder。在java层的Binder中,也有asBinder接口,这里也体现了Binder的设计思想,一个Binder客户端/服务端,必须带有接口属性与Binder通信属性,且这两者的类型可以互相转换。asbinder显然是接口属性向Binder通信属性的转换,这也是可以理解的,毕竟看类名就知道,IInterface显然是Binder机制中的带有接口属性的类。
 

【Bn端】

在C++层面的Binder相关类中,所谓的Bn端就是Binder的服务端,在此例中,Cameraservice的Bn端,显然就是之前AIDL自动生成的BnCameraService

 // out/soong/.intermediates/frameworks/av/camera/libcamera_client/android_x86_64_shared/gen/aidl/android/hardware/BnCameraService.h
class BnCameraService : public ::android::BnInterface<ICameraService> {
public:
  explicit BnCameraService();
  ::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
};  // class BnCameraService

BnCameraService继承于BnInterface,这是一个模板类,它的模板参数是Binder中的接口类,此例中当然就是ICameraService了。不过从功能上来看,BnInterface显然是作为Bn端的统一父类而存在的。类似于AIDL java生成的Stub.

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;

protected:
    typedef INTERFACE           BaseInterface;
    virtual IBinder*            onAsBinder();
};

 // Bn端的具体实现则在out/soong/.intermediates/frameworks/av/camera/libcamera_client/android_x86_64_shared/gen/aidl/frameworks/av/camera/aidl/android/hardware/ICameraService.cppz中可以看到

【Bp端】

Bp端是C++层Binder机制中的客户端,如果明白了Bn端的大致结构,那么Bp端的也很理解了,它们大同小异。
在此例中,BpCameraService显然就是CameraService服务的客户端了

 // /home/zfm/work/aosp/out/soong/.intermediates/frameworks/av/camera/libcamera_client/android_x86_64_shared/gen/aidl/android/hardware/BpCameraService.h
class BpCameraService : public ::android::BpInterface<ICameraService> {
public:
  explicit BpCameraService(const ::android::sp<::android::IBinder>& _aidl_impl);
  virtual ~BpCameraService() = default;
  ::android::binder::Status getNumberOfCameras(int32_t type, int32_t* _aidl_return) override;
  ::android::binder::Status getCameraInfo(int32_t cameraId, ::android::hardware::CameraInfo* _aidl_return) override;
  ……
  ::android::binder::Status supportsCameraApi(const ::android::String16& cameraId, int32_t apiVersion, bool* _aidl_return) override;
  ::android::binder::Status isHiddenPhysicalCamera(const ::android::String16& cameraId, bool* _aidl_return) override;
  ::android::binder::Status setTorchMode(const ::android::String16& cameraId, bool enabled, const ::android::sp<::android::IBinder>& clientBinder) override;
  ::android::binder::Status notifySystemEvent(int32_t eventId, const ::std::vector<int32_t>& args) override;
  ::android::binder::Status notifyDeviceStateChange(int64_t newState) override;
};  // class BpCameraService
 // Bp端的具体实现则在out/soong/.intermediates/frameworks/av/camera/libcamera_client/android_x86_64_shared/gen/aidl/frameworks/av/camera/aidl/android/hardware/ICameraService.cppz中可以看到

BpCameraService继承自BpInterface,而BpInterface又继承自它的模板类(接口类)和BpRefBase。

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
    explicit                    BpInterface(const sp<IBinder>& remote);

protected:
    typedef INTERFACE           BaseInterface;
    virtual IBinder*            onAsBinder();
};
// frameworks/native/libs/binder/include/binder/Binder.h
class BpRefBase : public virtual RefBase
{
protected:
    explicit                BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);

    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }

private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);

    IBinder* const          mRemote;
    RefBase::weakref_type*  mRefs;
    std::atomic<int32_t>    mState;
};

BpRefbase对应于AIDL Java端生成的Proxy。这个BpRefBase的成员函数mRemote,其所指向的究竟是个什么对象呢?这要从Bp端的获取说起。

我们知道,在Binder客户端的获取,本质上是从驱动层获取所需Binder的cookie值。

然后在应用层会对将该cookie值封装成一个Bp端,而获取cookie值的地方正是在Parcel类中。通过调用Parcel类中的readStrongBinder方法来获取到cookie值,同时也会在该方法中将cookie值封装成Bp端。
 

status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{
    status_t status = readNullableStrongBinder(val);
    if (status == OK && !val->get()) {
        status = UNEXPECTED_NULL;
    }
    return status;
}

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject<flat_binder_object>(); //从Parcel包中读取出Binder的flat_binder_object结构体,该结构体中会包含Binder对应的Binder索引值。

    if (flat) {
        switch (flat->hdr.type) {
            case BINDER_TYPE_BINDER: //服务端
                *out = reinterpret_cast<IBinder*>(flat->cookie); //将cookie值封装成Bp端
                return finish_unflatten_binder(nullptr, *flat, in);
            case BINDER_TYPE_HANDLE: //客户端
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpHwBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

当switch进入BINDER_TYPE_BINDER时,表示要获取本地已经存在的一个IBinder类,这个IBinder一般是Bn服务端。

而当进入BINDER_TYPE_HANDLE时,则意味着要通过句柄来创建一个新的IBinder,而这个新的IBinder就是Bp客户端了。

Bp客户端是在getStrongProxyForHandle方法中创建的。
 

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  The
        // attemptIncWeak() is safe because we know the BpBinder destructor will always
        // call expungeHandle(), which acquires the same lock we are holding now.
        // We need to do this because there is a race condition between someone
        // releasing a reference on this BpBinder, and a new reference on its handle
        // arriving from the driver.
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, nullptr, 0);
                if (status == DEAD_OBJECT)
                   return nullptr;
            }

            b = BpBinder::create(handle); //该方法会实例化一个BpBinder
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

BpBinder* BpBinder::create(int32_t handle) {
    int32_t trackedUid = -1;
    if (sCountByUidEnabled) {
        trackedUid = IPCThreadState::self()->getCallingUid();
        AutoMutex _l(sTrackingLock);
        uint32_t trackedValue = sTrackingMap[trackedUid];
        if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
            if (sBinderProxyThrottleCreate) {
                return nullptr;
            }
        } else {
            if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
                ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
                      getuid(), trackedUid, trackedValue);
                sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
                if (sLimitCallback) sLimitCallback(trackedUid);
                if (sBinderProxyThrottleCreate) {
                    ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
                          " count drops below %d",
                          trackedUid, getuid(), sBinderProxyCountLowWatermark);
                    return nullptr;
                }
            }
        }
        sTrackingMap[trackedUid]++;
    }
    return new BpBinder(handle, trackedUid);
}

  总结起来,当本地服务端接受到客户端传过来的一个Binder请求时,会在本地创建一个对应的Bp客户端(此时针对这个传递的Binder而言,客户端会保留它的Bn服务端,而服务端则会创建一个Bp客户端,有点绕。Bp客户端进程保留服务端缓存,Bp服务端保留客户端缓存?)。

        而该Bp客户端的实例化流程,首先会先通过Parcel的readStrongBinder实例化一个BpBinder,然后一般会通过调用对应接口类中的asInterface方法将BpBinder转化为一个真正的Bp接口客户端,其实这个转化就是实例化一个Bp客户端,实例化时,会将BpBinder作为构造传参传入Bp客户端中。

        目前还有一个盲点,就是将BpBinder转化为Bp客户端的方法在哪里还没有讲过。之前说该方法是asInterface是被定义在接口类中,但单纯的看代码,是找不到asInterface方法的,这是因为这个方法被统一封装到了一个宏中DECLARE_META_INTERFACE,在ICameraService中则是:

class ICameraService : public ::android::IInterface {
public:
  DECLARE_META_INTERFACE(CameraService)

DECLARE_META_INTERFACE宏的内容如下:

#define DECLARE_META_INTERFACE(INTERFACE)                               \
public:                                                                 \
    static const ::android::String16 descriptor;                        \
    static ::android::sp<I##INTERFACE> asInterface(                     \
            const ::android::sp<::android::IBinder>& obj);              \
    virtual const ::android::String16& getInterfaceDescriptor() const;  \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
    static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl);     \
    static const std::unique_ptr<I##INTERFACE>& getDefaultImpl();       \
private:                                                                \
    static std::unique_ptr<I##INTERFACE> default_impl;                  \
public:          

    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp<I##INTERFACE> intr;                               \
        if (obj != nullptr) {                                           \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == nullptr) {                                      \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                             \
参考阅读
BpBinder.cppNative层BP客户端源码
Binder.cpp
android_os_Parcel.cppParcel对javal层提供的接口注册,Binder数据传输被包在Parcel中


 

 【参考转载】

C++层Binder——Bn、Bp_bn bp_真的不懂呀的博客-CSDN博客

深入理解Binder机制4-bindService过程分析_bindserviceasuser_skytoby的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/715901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4. 注册machine

数字mic系列&#xff0c;注册machine dts配置如下 digital_mic: digital-mic {status "okay";compatible "simple-audio-card";simple-audio-card,format "i2s";simple-audio-card,mclk-fs <256>;simple-audio-card,name "digit…

你知道2个KT6368A的蓝牙芯片模块如何配对_以及数据传输吗

目录 一、简介你知道2个KT6368A的蓝牙芯片模块如何配对_以及数据传输吗&#xff1f;KT6368A的蓝牙模块之间是可以建立连接&#xff0c;并且全双工数据透传的但是有几点注意事项&#xff0c;一定要选择KT6368A的主机版本&#xff0c;和从机版本&#xff0c;如下图&#xff1a; …

Spring Boot 中的分布式锁

Spring Boot 中的分布式锁 在分布式系统中&#xff0c;多个应用程序可能需要同时对同一个资源进行修改。为了避免数据的不一致性和冲突等问题&#xff0c;我们需要对这些资源进行加锁操作。在本文中&#xff0c;我们将介绍 Spring Boot 中的分布式锁是什么&#xff0c;原理是什…

OpenCV键盘监听函数 cv2::waitKey( delay )

1、函数原型&#xff1a;cv::waitKey( delay ) delay&#xff1a;等待时间(毫秒) 作用&#xff1a;通过 waitKey() 可以获取键盘输入2、示例&#xff1a; 实现键盘输入1&#xff0c;将图像转为灰度图输出&#xff1b; 键盘输入2&#xff0c;将图像转为HSV图输出&#xff1b; …

element ui - el-button 重新渲染后disabled属性失效

element ui - el-button重新渲染后disabled属性失效 场景解决方法 场景 有两组不同的按钮&#xff1a; 不在灰度发布状态下的 disabled 的灰色按钮&#xff1b;灰度发布状态下的 可点击按钮&#xff1b; 发现当再次渲染 灰色按钮 时&#xff0c;disabled 属性失效。 解决方…

7.6 【Linux】文件系统的特殊观察与操作

7.6.1 磁盘空间之浪费问题 从上面的特殊字体部分&#xff0c;那就是每个文件所使用掉 block 的容量&#xff01;举例来说&#xff0c;那个 crontab 虽然仅有 451Bytes &#xff0c; 不过他却占用了整个 block &#xff08;每个 block 为 4K&#xff09;&#xff0c;所以将所有的…

电子企业MES管理系统如何加强生产现场管控

随着数字化时代的到来&#xff0c;中小型电子企业面临着提升生产现场管控的挑战。为了应对这一挑战&#xff0c;许多企业开始采用MES生产管理系统。本文将探讨MES生产管理系统如何帮助电子企业加强生产现场管控&#xff0c;提高生产效率和质量。 在电子企业的生产现场&#xf…

MCU是否会从ADAS域控制器消失

摘要&#xff1a; ADAS架构及MCU功能概览、未来ADAS架构的两种方案、MCU是否从ADAS域消失的一点思考 ADAS的装机量和渗透率再提升&#xff0c;尤其L2及L2级&#xff1b;那么随着ADAS域控制器主控芯片的增强&#xff0c;未来&#xff0c;MCU是否还会存在&#xff1f; 转自佐思汽…

SSM之MyBatis

MyBatis学习笔记 一、入门二、XML配置1、configuration&#xff08;配置&#xff09;2、properties&#xff08;属性&#xff09;3、settings&#xff08;设置&#xff09;4、typeAliases&#xff08;类型别名&#xff09;5、typeHandlers&#xff08;类型处理器&#xff09;6、…

CSS的学习3

什么是CSS CSS是层叠样式表的简称&#xff0c;又称为CSS样式表或级联样式表。CSS是一种标记语言。 主要用于设置HTML页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式、边距等&#xff09;、版面的布局和外观显示样式…

【算法】树形DP ② 打家劫舍Ⅲ(树上最大独立集)

文章目录 前期知识例题337. 打家劫舍 III 相关练习题目没有上司的舞会 https://www.luogu.com.cn/problem/P13521377. T 秒后青蛙的位置 https://leetcode.cn/problems/frog-position-after-t-seconds/⭐⭐⭐解法1&#xff1a;BFS优化代码 解法2——自顶向下dfs解法3——自底向…

Buildroot 系统设置开机密码登录-迅为RK3588开发板

首先对开发板进行上电&#xff0c;开发板正常启动后&#xff0c;使用命令“vi /etc/inittab”对文件进行修改&#xff0c;如 下图所示&#xff1a; 设置为密码登陆时配置如下图&#xff08;注意将 ttyS0 修改为 ttyFIQ0&#xff09;&#xff1a; 修改完&#xff0c;保存退出&a…

GLM: General Language Model Pretrainingwith Autoregressive Blank Infilling翻译理解

GPT&#xff08;autoregressive&#xff09;模型是一个自回归模型&#xff0c;利用left-to-right语言模型&#xff0c;由于不是双向attention 机制&#xff0c;因此不能再NLU任务中&#xff0c;获取充分的上下文信息&#xff0c;BERT类似自编码&#xff08;autoencoding&#x…

SQL 优化(四):合理使用 join

在工作的时候经常听到的一句话就是&#xff0c;“这条 SQL 因为 join 了很多表&#xff0c;导致查询速度比较慢”&#xff0c;可以从侧面反映出&#xff0c;join语句对性能的影响是比较大的&#xff0c;而且大部分人不知道如何进行优化。这篇文章我们来讲讲join的执行过程&…

PC C++ SDK 全局函数、防录制功能、下载器、播放器

本文档提供了使用 CSDK 的操作步骤及代码示例&#xff0c;通过本文您可以快速了解如何使用 SDK 提供的功能。您也可以通过 Demo 中的示例进行了解和自有业务开发。 SDK 名词含义及功能说明 参见 plv-player-def.h SDK 全局函数 设置日志&#xff0c;日志过滤项。设置观众信息…

ENVI遥感影像处理—水体提取

2 .水体提取 &#xff08;1&#xff09;导入经过大气校正后的影像FLAASH_result.dat。 &#xff08;2&#xff09;选择工具箱中ToolBox——Band Ratio——Band Math&#xff0c;输入(float(b1)-float(b2))/(float(b1)float(b2))&#xff0c;点击Add to List&#xff0c;选中公…

R3LIVE环境搭建

一、安装ros、livox sdk、livox_ros_driver 安装方法[参考] 二、CGAL和pcl_viewer sudo apt-get install libcgal-dev pcl-tools 三、opencv&#xff08;≥3.3&#xff09; 3.1 命令检查 OpenCV 版本&#xff0c;如果 openCV 版本低于 OpenCV-3.3, 更新openCV版本为3.3.1、3…

Kubernetes 多集群管理工具 - Kuboard

Kuboard 是Kubernetes 多集群管理工具&#xff0c;是一个界面化的web网站&#xff0c;使用起来非常方便。在Kuboard中可以导入集群&#xff0c;在kuboard上可以完成很多的运维工作&#xff0c;比如创建命名空间、创建标签、运行服务、修改pod数量等等。 一&#xff1a;kuboard…

【JavaEE初阶】CSS

摄影分享~ 文章目录 一.CSS基本规范1. CSS基本语法规范2.CSS选择器 二.CSS常用属性1. 字体属性2.文本属性3.背景属性4.圆角矩形5.元素的显示模式块级元素行内元素 6.盒子模型边框内边距外边距 7.弹性布局 一.CSS基本规范 层叠样式表。(Cascading Style Sheets) CSS 能够对网页…

【零基础入门学习Python---Python中Web开发基础之快速入门实践】

&#x1f680; 零基础入门学习Python&#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜…