当前位置: 首页 > news >正文

Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法

InputReader.cpp
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}

当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程。

InputReader的一次线程循环的工作思路非常清晰,一共三步:

□首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。

□通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中。

□所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;// Copy some state so that we can access it outside the lock later.bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;std::list<NotifyArgs> notifyArgs;{ // acquire lockstd::scoped_lock _l(mLock);oldGeneration = mGeneration;timeoutMillis = -1;auto changes = mConfigurationChangesToRefresh;if (changes.any()) {mConfigurationChangesToRefresh.clear();timeoutMillis = 0;refreshConfigurationLocked(changes);} else if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);}} // release lockstd::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();
// 如果有事件信息,调用processEventsLocked()函数对事件进行加工处理if (!events.empty()) {notifyArgs += processEventsLocked(events.data(), events.size());}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {if (debugRawEvents()) {ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);}mNextTimeout = LLONG_MAX;notifyArgs += timeoutExpiredLocked(now);}}if (oldGeneration != mGeneration) {inputDevicesChanged = true;inputDevices = getInputDevicesLocked();notifyArgs.emplace_back(NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});}} // release lock// Send out a message that the describes the changed input devices.if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}// Notify the policy of the start of every new stylus gesture outside the lock.for (const auto& args : notifyArgs) {const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);}}notifyAll(std::move(notifyArgs));// Flush queued events out to the listener.// This must happen outside of the lock because the listener could potentially call// back into the InputReader's methods, such as getScanCodeState, or become blocked// on another thread similarly waiting to acquire the InputReader lock thereby// resulting in a deadlock.  This situation is actually quite plausible because the// listener is actually the input dispatcher, which calls into the window manager,// which occasionally calls into the input reader.mQueuedListener.flush();
}

processEventsLocked()会分别处理原始输入事件与设备增删事件。

对于原始输入事件,由于EventHub会将属于同一输入设备的原始输入事件放在一起,因此processEventsLocked()可以使processEventsForDeviceLocked()同时处理来自同一输入设备的一批事件。

std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {std::list<NotifyArgs> out;for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}if (debugRawEvents()) {ALOGD("BatchSize: %zu Count: %zu", batchSize, count);}out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}return out;
}
std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);if (deviceIt == mDevices.end()) {ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);return {};}std::shared_ptr<InputDevice>& device = deviceIt->second;if (device->isIgnored()) {// ALOGD("Discarding event for ignored deviceId %d.", deviceId);return {};}return device->process(rawEvents, count);
}
InputDevice.cpp
std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved.  For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.std::list<NotifyArgs> out;for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {if (debugRawEvents()) {const auto [type, code, value] =InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,rawEvent->value);ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);}if (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");} else {ALOGD_IF(debugRawEvents(),"Dropped input event while waiting for next input sync.");}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());mDropUntilNextSync = true;out += reset(rawEvent->when);} else {for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {out += mapper.process(rawEvent);});}--count;}return out;
}

InputMapper是InputReader中实际进行原始输入事件加工的场所,它有一系列的子类,分别用于加工不同类型的原始输入事件。而InputDevice的process()函数使用InputMapper的方式是一个简化了的职责链(chain of responsibility)设计模式。InputDevice不需要知道哪一个InputMapper可以处理一个原始输入事件,只须将一个事件逐个交给每一个InputMapper尝试处理,如果InputMapper可以接受这个事件则处理之,否则什么都不做。

根据设备的类型,将设备做如下分配

InputMapper的种类实在很多,对所有类型都进行详细分析并不现实。选择KeyboardInputMapper和MultiTouchInputMapper两个常用并且具有代表性的InputMapper进行探讨

键盘事件的处理

KeyboardInputMapper的process()函数比较简单

□通过processKey()按键事件做进一步处理。

mQueuedListener.flush();

根据扫描码获取虚拟键值以及功能值后,KeyboardInputMapper::process()调用了processKey()函数对按键事件做进一步处理。

将键盘信息转化为NotifyKeyArgs,返回到loopOnce的loopOnce中,通知事件分发

触摸事件的处理

触摸事件的处理

std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);mMultiTouchMotionAccumulator.process(rawEvent);return out;
}

事件处理完成后回到最初的

mQueuedListener.flush(); // 将事件交给inputDispatcher的过程

 

InputDispatcher继承了InputDispatcherInterface接口
class InputDispatcher : public android::InputDispatcherInterface {
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {Visitor v{[&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },[&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },[&](const NotifyKeyArgs& args) { notifyKey(args); },[&](const NotifyMotionArgs& args) { notifyMotion(args); },[&](const NotifySwitchArgs& args) { notifySwitch(args); },[&](const NotifySensorArgs& args) { notifySensor(args); },[&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },[&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },[&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },};std::visit(v, generalArgs);
}

以motion事件为例

void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {if (debugInboundEventDetails()) {ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, ""displayId=%" PRId32 ", policyFlags=0x%x, ""action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, ""edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, ""yCursorPosition=%f, downTime=%" PRId64,args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,args.downTime);for (uint32_t i = 0; i < args.pointerCount; i++) {ALOGD("  Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, ""touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",i, args.pointerProperties[i].id,ftl::enum_string(args.pointerProperties[i].toolType).c_str(),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));}}Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,args.pointerCount, args.pointerProperties);if (!motionCheck.ok()) {LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();return;}uint32_t policyFlags = args.policyFlags;policyFlags |= POLICY_FLAG_TRUSTED;android::base::Timer t;mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",std::to_string(t.duration().count()).c_str());}bool needWake = false;{ // acquire lockmLock.lock();if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {// Set the flag anyway if we already have an ongoing gesture. That would allow us to// complete the processing of the current stroke.const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);if (touchStateIt != mTouchStatesByDisplay.end()) {const TouchState& touchState = touchStateIt->second;if (touchState.deviceId == args.deviceId && touchState.isDown()) {policyFlags |= POLICY_FLAG_PASS_TO_USER;}}}if (shouldSendMotionToInputFilterLocked(args)) {ui::Transform displayTransform;if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {displayTransform = it->second.transform;}mLock.unlock();MotionEvent event;event.initialize(args.id, args.deviceId, args.source, args.displayId, INVALID_HMAC,args.action, args.actionButton, args.flags, args.edgeFlags,args.metaState, args.buttonState, args.classification,displayTransform, args.xPrecision, args.yPrecision,args.xCursorPosition, args.yCursorPosition, displayTransform,args.downTime, args.eventTime, args.pointerCount,args.pointerProperties, args.pointerCoords);policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy.filterInputEvent(event, policyFlags)) {return; // event was consumed by the filter}mLock.lock();}// Just enqueue a new motion event.std::unique_ptr<MotionEntry> newEntry =std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,args.displayId, policyFlags, args.action,args.actionButton, args.flags, args.metaState,args.buttonState, args.classification, args.edgeFlags,args.xPrecision, args.yPrecision,args.xCursorPosition, args.yCursorPosition,args.downTime, args.pointerCount,args.pointerProperties, args.pointerCoords);if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&!mInputFilterEnabled) {const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);}needWake = enqueueInboundEventLocked(std::move(newEntry));mLock.unlock();} // release lockif (needWake) {mLooper->wake();}
}

到达InputDispatcher的Motion事件被保存在MotionEntry类中

bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {bool needWake = mInboundQueue.empty();mInboundQueue.push_back(std::move(newEntry));EventEntry& entry = *(mInboundQueue.back());traceInboundQueueLengthLocked();switch (entry.type) {case EventEntry::Type::KEY: {LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,"Unexpected untrusted event.");// Optimize app switch latency.// If the application takes too long to catch up then we drop all events preceding// the app switch key.const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);if (isAppSwitchKeyEvent(keyEntry)) {if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {mAppSwitchSawKeyDown = true;} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {if (mAppSwitchSawKeyDown) {if (DEBUG_APP_SWITCH) {ALOGD("App switch is pending!");}mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;mAppSwitchSawKeyDown = false;needWake = true;}}}// If a new up event comes in, and the pending event with same key code has been asked// to try again later because of the policy. We have to reset the intercept key wake up// time for it may have been handled in the policy and could be dropped.if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&mPendingEvent->type == EventEntry::Type::KEY) {KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);if (pendingKey.keyCode == keyEntry.keyCode &&pendingKey.interceptKeyResult ==KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;pendingKey.interceptKeyWakeupTime = 0;needWake = true;}}break;}case EventEntry::Type::MOTION: {LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,"Unexpected untrusted event.");if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {mNextUnblockedEvent = mInboundQueue.back();needWake = true;}break;}case EventEntry::Type::FOCUS: {LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");break;}case EventEntry::Type::TOUCH_MODE_CHANGED:case EventEntry::Type::CONFIGURATION_CHANGED:case EventEntry::Type::DEVICE_RESET:case EventEntry::Type::SENSOR:case EventEntry::Type::POINTER_CAPTURE_CHANGED:case EventEntry::Type::DRAG: {// nothing to dobreak;}}return needWake;
}

通过这两个函数可以看出,到达InputDispatcher的Motion事件被保存在MotionEntry类中,然后排在mInboundQueue列表的队尾,这个mInboundQueue就是InputDispatcher的派发队列。MotionEntry是EventEntry的一个子类,保存了Motion事件的信息。Key事件也有一个KeyEntry与之对应。EventEntry是输入事件在InputDispatcher中的存在形式。另外,由于InputDispatcher在没有事件可以派发时(mInboundQueue为空),将会进入休眠状态,因此在将事件放入派发队列时,需要将派发线程唤醒。

上述过程运行在inputReader线程中,至此inputReader的处理完成

相关文章:

Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法 InputReader.cpp status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });…...

web前端安全性——JSONP劫持

1、JSONP概念 JSONP(JSON with Padding)是JSON的一种“使用模式”&#xff0c;可用于解决主流浏览器的跨域数据访问的问题。由于同源策略&#xff0c;协议IP端口有任意不同都会导致请求跨域&#xff0c;而HTML的script元素是一个例外。利用script元素的这个开放策略&#xff0…...

从零开始学HCIA之广域网技术03

1、LCP中包含的报文类型 &#xff08;1&#xff09;Configure-Request&#xff08;配置请求&#xff09;&#xff0c;链路层协商过程中发送的第一个报文&#xff0c;该报文表明点对点双方开始进行链路层参数的协商。 &#xff08;2&#xff09; Configure-Ack&#xff08;配置…...

AI推介-大语言模型LLMs论文速览(arXiv方向):2024.01.01-2024.01.10

1.Pre-trained Large Language Models for Financial Sentiment Analysis 标题:用于金融情感分析的预训练大型语言模型 author:Wei Luo, Dihong Gong date Time:2024-01-10 paper pdf:http://arxiv.org/pdf/2401.05215v1 摘要&#xff1a; 金融情感分析是指将金融文本内容划分…...

Redis降低内存占用(二)分片结构

一、分区方法&#xff1a; 分片&#xff0c;也称为分区。Redis提供了多种分区实现方案: 1、哈希分区 2、区间分区 3、一致性哈希分区 4、虚拟分区 5、LUA脚本实现分片 二、...

vue大文件读取部分内容,避免重复加载大文件,造成流量浪费

使用场景&#xff1a;项目点云地图是pcd文件&#xff0c;但是文件可能上百兆&#xff0c;我需要获取到文件中的版本信息&#xff0c;跟本地的缓存文件做比较&#xff0c;如果不一致&#xff0c;才会加载整个文件。从而节省流量。 避免重复加载整个“.pcd文件&#xff0c;以最大…...

5G网络RedCap

RedCap&#xff1a;RedCap&#xff08;Reduced Capability&#xff09;&#xff0c;即“降低能力”。它是3GPP在5G R17阶段&#xff0c;针对速率、时延要求不高的5G应用场景&#xff0c;专门推出的一种新技术标准协议&#xff0c;旨在全面提升5G网络质量和覆盖率&#xff0c;也…...

vue+springboot登录与注册功能的实现

①首先写一个登录页面 <template> <div style"background-color: #42b983;display: flex;align-items: center;justify-content: center;height: 100vh"><div style"background-color: white;display: flex;width: 50%;height: 50%;overflow: h…...

数据结构D3作业

1. 2. 按位插入 void insert_pos(seq_p L,datatype num,int pos) { if(LNULL) { printf("入参为空&#xff0c;请检查\n"); return; } if(seq_full(L)1) { printf("表已满&#xff0c;不能插入\n"); …...

Spring框架@Autowired注解进行字段时,使用父类类型接收子类变量,可以注入成功吗?(@Autowired源码跟踪)

一、 前言 平常我们在使用spring框架开发项目过程中&#xff0c;会使用Autowired注解进行属性依赖注入&#xff0c;一般我们都是声明接口类型来接收接口实现变量&#xff0c;那么使用父类类型接收子类变量&#xff0c;可以注入成功吗&#xff1f;答案是肯定可以的&#xff01;…...

【springblade】springblade(bladeX) 数据权限失效原因分析

文章目录 数据权限接口权限 前言&#xff1a;最近博主在按照bladeX官方文档 配置数据权限 结果发现失效了&#xff0c;网上搜了一下没找到合适的答案&#xff0c;本着求人不如求己的精神&#xff0c;自己调试了一下发现了问题所在&#xff0c;也大致看了一下bladeX的权限逻辑。…...

单例模式的几种实现方式

在Java中&#xff0c;实现单例模式主要有几种方式&#xff1a;懒汉式、饿汉式、双重检查锁定、静态内部类和枚举。每种方式都有其特点和适用场景。 1. 饿汉式&#xff08;线程安全&#xff09; 饿汉式是最简单的一种实现方式&#xff0c;通过静态初始化实例&#xff0c;保证了…...

鸿蒙OS运行报错 ‘ToDoListItem({ item })‘ does not meet UI component syntax.

在学习harmonyOS时&#xff0c;原本是好好运行的。但是突然报错 ToDoListItem({ item }) does not meet UI component syntax. 一脸懵逼&#xff0c;以为是自己语法问题检查了半天也没问题。 网上搜索了一下&#xff0c;说把多余的js\map文件删除就行 才发现我的 鸿蒙的开…...

React18源码: reconciler执行流程

reconciler执行流程 1 &#xff09;概述 此处先归纳一下react-reconciler包的主要作用&#xff0c;将主要功能分为4个方面&#xff1a; 输入&#xff1a;暴露api函数&#xff08;如&#xff1a;scheduleUpdateOnFiber&#xff09;, 供给其他包&#xff08;如react包&#xff0…...

mapbox面图层标注

mapbox并没有一个属性类似于’text-field’的symbol图层的直接可以标注的办法&#xff0c;这里笔者提供两种其他的面图层标注的办法用来大家参考 效果图 方案一 把面图层当做点图层直接展示 在mapbox里面&#xff0c;面图层是可以直接渲染成线图层和点图层的&#xff0c;这里…...

MySQL|MySQL基础(求知讲堂-学习笔记【详】)

MySQL基础 目录 MySQL基础一、 MySQL的结构二、 管理数据库1&#xff09;查询所有的数据库2&#xff09;创建数据库3&#xff09;修改数据库的字符编码4&#xff09;删除数据库5&#xff09;切换操作的数据库 三、表的概念四、字段的数据类型4.1 整型4.2 浮点型(float和double)…...

10.docker exec -it /bin/bash报错解决、sh与bash区别

报错 进入容器时&#xff0c;报如下错误 dockeruserdell-PowerEdge-R740:~$ docker exec -it daf2 /bin/bash OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown…...

查询数据库的编码集Oracle,MySQL

1、查询数据库的编码集Oracle,MySQL 1.1、oracle select * from v$nls_parameters where parameterNLS_CHARACTERSET; 查询版本&#xff1a;SELECT * FROM v$version 2、MySQL编码集 SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SC…...

电商数据采集+跨境电商|API电商数据采集接口洞悉数字新零售发展

随着全球经济一体化和电子商务的快速发展&#xff0c;网络购物的需求日益增加。不断涌现的电商企业使得行业的竞争情况愈演愈烈。在这种情况下&#xff0c;企业不仅要加大经营力度&#xff0c;还要在自己的基础设施和技术上持续投入&#xff0c;才能更好的适应市场和消费习惯。…...

linux之用户和用户组

文章目录 一、简介1.1 用户1.2 用户组1.3 UID和GID1.4 用户账户分类 二、用户2.1 添加新的用户账号&#xff1a;useradd2.2 删除账号&#xff1a;userdel2.3 修改账号&#xff1a;usermod(modmodify)2.4 用户口令的管理:passwd2.5 切换用户&#xff1a;su 三、用户组3.1 增加一…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...