Android binder死亡通知机制

在Andorid 的binder系统中,当Bn端由于种种原因死亡时,需要通知Bp端,Bp端感知Bn端死亡后,做相应的处理。

使用
Bp需要先注册一个死亡通知,当Bn端死亡时,回调到Bp端。

1,java代码注册死亡通知

try {binder.asBinder().linkToDeath(new IBinder.DeathRecipient() {@Overridepublic void binderDied() {//处理}},0);
} catch (RemoteException e) {e.printStackTrace();
}

2,c++代码注册死亡通知

	// Create the death listener.class DeathObserver : public IBinder::DeathRecipient {void binderDied(const wp<IBinder>& who) {//处理ALOGD("service is died\n");}};sp<IBinder::DeathRecipient> mDeathObserver = new DeathObserver();sp<IBinder> binder = sm->getService(String16("xxx"));//获取一个BpBinder对象if(binder->linkToDeath(mDeathObserver) != NO_ERROR){ALOGE("link to death failed");}else{ALOGE("link to death sucess");}

当Bn端死亡时,回调binderDied方法

注册死亡通知分析
从上面可以看出来,Bp端通过linkToDeath方法注册死亡通知。我们从java端的linkToDeath开始分析。
binder.asBinder返回的是一个BinderProxy对象

//frameworks\base\core\java\android\os\Binder.java
public native void linkToDeath(DeathRecipient recipient, int flags)throws RemoteException;

这是一个native方法,对应android_os_BinderProxy_linkToDeath方法

//frameworks\base\core\jni\android_util_Binder.cpp
static const JNINativeMethod gBinderProxyMethods[] = {/* name, signature, funcPtr *///省略{"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},//省略
};

继续来看一下android_os_BinderProxy_linkToDeath方法

//frameworks\base\core\jni\android_util_Binder.cpp
static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,jobject recipient, jint flags) // throws RemoteException
{//省略IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);//取出native层的 BpBinder对象//省略if (!target->localBinder()) {//只有Bp端才可以注册死亡通知DeathRecipientList* list = (DeathRecipientList*)env->GetLongField(obj, gBinderProxyOffsets.mOrgue);//1sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);//2status_t err = target->linkToDeath(jdr, NULL, flags);//3//省略}
}

注释1处取出DeathRecipientList对象,DeathRecipientList对象中有一个集合

//frameworks\base\core\jni\android_util_Binder.cpp
class DeathRecipientList : public RefBase {List< sp<JavaDeathRecipient> > mList;Mutex mLock;

注释2处新建一个JavaDeathRecipient对象,并将其加入到上面的集合中

//frameworks\base\core\jni\android_util_Binder.cpp
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list): mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),mObjectWeak(NULL), mList(list){// These objects manage their own lifetimes so are responsible for final bookkeeping.// The list holds a strong reference to this object.LOGDEATH("Adding JDR %p to DRL %p", this, list.get());list->add(this);//加到集合中android_atomic_inc(&gNumDeathRefs);incRefsCreated(env);}

注释3处target为BpBinder对象,继续来看BpBinder的linkToDeath方法

//frameworks\native\libs\binder\BpBinder.cpp
status_t BpBinder::linkToDeath(const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{/*构造Obituary 对象*/Obituary ob;ob.recipient = recipient;ob.cookie = cookie;ob.flags = flags;LOG_ALWAYS_FATAL_IF(recipient == NULL,"linkToDeath(): recipient must be non-NULL");{AutoMutex _l(mLock);if (!mObitsSent) {//mObitsSent默认为0,可以看出只会发送一次死亡通知if (!mObituaries) {mObituaries = new Vector<Obituary>;if (!mObituaries) {return NO_MEMORY;}ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);getWeakRefs()->incWeak(this);IPCThreadState* self = IPCThreadState::self();self->requestDeathNotification(mHandle, this);//1self->flushCommands();//2}ssize_t res = mObituaries->add(ob);//3return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;}}return DEAD_OBJECT;
}

注释1处封装数据

//frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);//注意:cmd为BC_REQUEST_DEATH_NOTIFICATIONmOut.writeInt32((int32_t)handle);mOut.writePointer((uintptr_t)proxy);//这个proxy是前面的BpBinder对象return NO_ERROR;
}

注释2处将数据写给binder驱动

//frameworks\native\libs\binder\IPCThreadState.cpp
void IPCThreadState::flushCommands()
{if (mProcess->mDriverFD <= 0)return;talkWithDriver(false);//通过ioctl写给驱动
}

注释3处将前面构造好的Obituary 添加到集合中,该Obituary对象的recipient 成员指向我们前面传入的JavaDeathRecipient对象。
binder驱动开始处理,注意cmd为BC_REQUEST_DEATH_NOTIFICATION

//kernel\drivers\android\binder.c
case BC_REQUEST_DEATH_NOTIFICATION:
case BC_CLEAR_DEATH_NOTIFICATION: {uint32_t target;binder_uintptr_t cookie;struct binder_ref *ref;struct binder_ref_death *death = NULL;if (get_user(target, (uint32_t __user *)ptr))//从用户空间取出handlereturn -EFAULT;ptr += sizeof(uint32_t);if (get_user(cookie, (binder_uintptr_t __user *)ptr))//从用户空间取出BpBinder对象地址return -EFAULT;ptr += sizeof(binder_uintptr_t);if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {death = kzalloc(sizeof(*death), GFP_KERNEL);//申请binder_ref_death空间//省略			}binder_proc_lock(proc);ref = binder_get_ref_olocked(proc, target, false);//根据handle,找到binder_ref//省略if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {binder_stats_created(BINDER_STAT_DEATH);INIT_LIST_HEAD(&death->work.entry);death->cookie = cookie;//将BpBinder对象地址保存在death的cookie 中ref->death = death;//将death保存在binder_ref的death 成员中if (ref->node->proc == NULL) {//注册的时候,Bn端刚好死亡,一般不太可能ref->death->work.type = BINDER_WORK_DEAD_BINDER;binder_inner_proc_lock(proc);binder_enqueue_work_ilocked(&ref->death->work, &proc->todo);binder_wakeup_proc_ilocked(proc);binder_inner_proc_unlock(proc);}}//省略

对于注册死亡通知时驱动的处理上面的注释已经说的很清楚。主要是将BpBinder对象地址保存在binder_ref的binder_ref_death 结构体中,这里只是做了保存,我们还没有看到死亡通知到底是如何触发的呢,即binderDied是如何被调用到的?接下来我们就来看一下死亡通知的触发
死亡通知触发分析
当Bn端死亡时,就要开始释放资源,调用binder_release,从这个方法开始分析

//kernel\drivers\android\binder.c
static int binder_release(struct inode *nodp, struct file *filp)
{struct binder_proc *proc = filp->private_data;debugfs_remove(proc->debugfs_entry);binder_defer_work(proc, BINDER_DEFERRED_RELEASE);return 0;
}

调用binder_defer_work,注意这个proc还是当前进程即Bn端所处的进程,第二个参数为BINDER_DEFERRED_RELEASE

//kernel\drivers\android\binder.c
static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{mutex_lock(&binder_deferred_lock);proc->deferred_work |= defer;if (hlist_unhashed(&proc->deferred_work_node)) {hlist_add_head(&proc->deferred_work_node,&binder_deferred_list);queue_work(binder_deferred_workqueue, &binder_deferred_work);//1}mutex_unlock(&binder_deferred_lock);
}

注释1处开始执行工作队列,对于binder_deferred_work,则是执行binder_deferred_func函数

//kernel\drivers\android\binder.c
static void binder_deferred_func(struct work_struct *work)
{struct binder_proc *proc;struct files_struct *files;int defer;do {//省略if (defer & BINDER_DEFERRED_RELEASE)binder_deferred_release(proc); /* frees proc */if (files)put_files_struct(files);} while (proc);
}

对于BINDER_DEFERRED_RELEASE,调用binder_deferred_release继续处理

//kernel\drivers\android\binder.c
static void binder_deferred_release(struct binder_proc *proc)
{struct binder_context *context = proc->context;struct rb_node *n;int threads, nodes, incoming_refs, outgoing_refs, active_transactions;BUG_ON(proc->files);mutex_lock(&binder_procs_lock);hlist_del(&proc->proc_node);//删除proc_node节点mutex_unlock(&binder_procs_lock);mutex_lock(&context->context_mgr_node_lock);/*如果是servicemanager死亡,则删除context->binder_context_mgr_node*/if (context->binder_context_mgr_node &&context->binder_context_mgr_node->proc == proc) {//省略}mutex_unlock(&context->context_mgr_node_lock);binder_inner_proc_lock(proc);proc->tmp_ref++;proc->is_dead = true;threads = 0;active_transactions = 0;/*删除binder_thread*/while ((n = rb_first(&proc->threads))) {//省略}nodes = 0;incoming_refs = 0;/*删除binder_node*/while ((n = rb_first(&proc->nodes))) {struct binder_node *node;node = rb_entry(n, struct binder_node, rb_node);nodes++;/** take a temporary ref on the node before* calling binder_node_release() which will either* kfree() the node or call binder_put_node()*/binder_inc_node_tmpref_ilocked(node);rb_erase(&node->rb_node, &proc->nodes);binder_inner_proc_unlock(proc);incoming_refs = binder_node_release(node, incoming_refs);//1binder_inner_proc_lock(proc);}binder_inner_proc_unlock(proc);outgoing_refs = 0;binder_proc_lock(proc);/*删除binder_ref*/while ((n = rb_first(&proc->refs_by_desc))) {//省略}binder_proc_unlock(proc);binder_release_work(proc, &proc->todo);binder_release_work(proc, &proc->delivered_death);binder_debug(BINDER_DEBUG_OPEN_CLOSE,"%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n",__func__, proc->pid, threads, nodes, incoming_refs,outgoing_refs, active_transactions);binder_proc_dec_tmpref(proc);

binder_deferred_release方法里面还是做了很多事情的,上面注释也已经说的很清楚。我们继续来看一下注释1处的binder_node_release方法

//kernel\drivers\android\binder.c
static int binder_node_release(struct binder_node *node, int refs)
{//省略hlist_for_each_entry(ref, &node->refs, node_entry) {refs++;binder_inner_proc_lock(ref->proc);if (!ref->death) {binder_inner_proc_unlock(ref->proc);continue;}death++;BUG_ON(!list_empty(&ref->death->work.entry));ref->death->work.type = BINDER_WORK_DEAD_BINDER;binder_enqueue_work_ilocked(&ref->death->work,&ref->proc->todo);binder_wakeup_proc_ilocked(ref->proc);binder_inner_proc_unlock(ref->proc);}//省略}

取出node中的binder_ref,如果binder_ref中有注册过死亡通知,则添加到Bp端的进程的todo两边,唤醒Bp端进程。注意work的type为BINDER_WORK_DEAD_BINDER。Bp端进程被唤醒,Bp端进程开始处理BINDER_WORK_DEAD_BINDER这个type。注意现在是运行在Bp端所在的进程

//kernel\drivers\android\binder.c
static int binder_thread_read(struct binder_proc *proc,struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,binder_size_t *consumed, int non_block)
{//省略case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {struct binder_ref_death *death;uint32_t cmd;binder_uintptr_t cookie;death = container_of(w, struct binder_ref_death, work);//取出binder_ref中的binder_ref_deathif (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;elsecmd = BR_DEAD_BINDER;//传给用户空间的cmd为BR_DEAD_BINDERcookie = death->cookie;//取出cookie,这个cookie就是之前注册时的BpBinder对象if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {//省略} else {binder_enqueue_work_ilocked(w, &proc->delivered_death);binder_inner_proc_unlock(proc);}if (put_user(cmd, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);if (put_user(cookie,(binder_uintptr_t __user *)ptr))return -EFAULT;ptr += sizeof(binder_uintptr_t);binder_stat_br(proc, thread, cmd);if (cmd == BR_DEAD_BINDER)goto done; /* DEAD_BINDER notifications can cause transactions */} break;
}

经过上面的处理,Bp端进程的用户空间就会得到cmd为BR_DEAD_BINDER的命令。Bp端进程是在executeCommand方法中处理命令的

//frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::executeCommand(int32_t cmd)
{//省略case BR_DEAD_BINDER:{BpBinder *proxy = (BpBinder*)mIn.readPointer();//1proxy->sendObituary();//2mOut.writeInt32(BC_DEAD_BINDER_DONE);mOut.writePointer((uintptr_t)proxy);} break;//省略    }

注释1处取出驱动传过来的BpBinder对象,注释2处调用BpBinder的sendObituary方法

//frameworks\native\libs\binder\BpBinder.cpp
void BpBinder::sendObituary()
{mAlive = 0;if (mObitsSent) return;mLock.lock();Vector<Obituary>* obits = mObituaries;/*首先先向驱动发送清楚这个死亡通知的事件*/if(obits != NULL) {ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);IPCThreadState* self = IPCThreadState::self();self->clearDeathNotification(mHandle, this);self->flushCommands();mObituaries = NULL;}mObitsSent = 1;mLock.unlock();if (obits != NULL) {const size_t N = obits->size();for (size_t i=0; i<N; i++) {reportOneDeath(obits->itemAt(i));//1}delete obits;}
}

注释1处,从mObituaries取出一个个的Obituary对象,然后执行reportOneDeath方法。还记得之前在注册死亡通知时,将我们的recipient封装在了Obituary对象中了。继续来看reportOneDeath方法

void BpBinder::reportOneDeath(const Obituary& obit)
{sp<DeathRecipient> recipient = obit.recipient.promote();ALOGV("Reporting death to recipient: %p\n", recipient.get());if (recipient == NULL) return;recipient->binderDied(this);
}

这里取出我们的recipient对象,调用其binderDied方法。如果是C++注册的死亡通知,那C++层的binderDied就得到执行了。我们接下来看看是如何调用到java端的binderDied方法。对于java端,我们之前传入的是JavaDeathRecipient对象,所以接在看JavaDeathRecipient的binderDied方法

//frameworks\base\core\jni\android_util_Binder.cpp
void binderDied(const wp<IBinder>& who){LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);if (mObject != NULL) {JNIEnv* env = javavm_to_jnienv(mVM);env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,gBinderProxyOffsets.mSendDeathNotice, mObject);//调用BinderProxy的sendDeathNotice方法//省略}}

调用BinderProxy的sendDeathNotice方法,传入的mObject为之前我们注册时传入的DeathRecipient对象

//frameworks\base\core\java\android\os\Binder.java
private static final void sendDeathNotice(DeathRecipient recipient) {if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);try {recipient.binderDied();}catch (RuntimeException exc) {Log.w("BinderNative", "Uncaught exception from death notification",exc);}}

可以看出,调用binderDied,之前注册的死亡通知得以执行。Bp端就感知到了Bn端的死亡

总结
死亡通知机制包含Bp端注册死亡通知以及Bn端死亡时触发死亡通知,用一张图来总结下其流程

在这里插入图片描述

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

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

相关文章

【webrtc】MessageHandler 9: 基于线程的消息处理:执行Port销毁自己

Port::Port 构造的时候,就触发了一个异步操作,但是这个操作是要在 thread 里执行的,因此要通过post 消息 MSG_DESTROY_IF_DEAD 到thread跑:port的创建并米有要求在thread中 但是port的析构却在thread里 这是为啥呢?

豆瓣9.7,这部Java神作第3版重磅上市!

Java 程序员们开年就有重磅好消息&#xff0c;《Effective Java 中文版&#xff08;原书第 3 版&#xff09;》要上市啦&#xff01; 该书的第1版出版于 2001 年&#xff0c;当时就在业界流传开来&#xff0c;受到广泛赞誉。时至今日&#xff0c;已热销近20年&#xff0c;本书第…

三维SDMTSP:GWO灰狼优化算法求解三维单仓库多旅行商问题,可以更改数据集和起点(MATLAB代码)

一、单仓库多旅行商问题 多旅行商问题&#xff08;Multiple Traveling Salesman Problem, MTSP&#xff09;是著名的旅行商问题&#xff08;Traveling Salesman Problem, TSP&#xff09;的延伸&#xff0c;多旅行商问题定义为&#xff1a;给定一个&#x1d45b;座城市的城市集…

CST Studio初级教程 五

本课程将实例讲解CST 3D建模。CST 3D 建模有三个途径&#xff1a;一种方法是用Brick、Sphere、Cone、Torus、Cylinder、Bond Wire指令绘制实体。第二种方法是用Extrude Face、Rotate Face、loft在已有模型基础上生成实体。第三种方法是&#xff0c;先用2D绘图指令绘制Curves&am…

Apache POI 在java中处理excel

介绍: Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下&#xff0c;POI 都是用于操作 Excel 文件。 如何使用: 1.maven坐标引入 <depend…

Aker(安碁科技)晶振产品应用和选型

一、石英晶体振荡器简介 在电子电路系统中&#xff0c;特定的动作需要严格按照一定的顺序进行&#xff0c;以确保数据被正确处理和操作&#xff0c;时钟信号就成了系统工作的重要引导者。而且在多模块复杂电路系统中&#xff0c;为了确保不同功能模块能协调一致地工作&#xf…

你用过最好用的AI工具有哪些

一&#xff1a;介绍 随着科技的飞速发展&#xff0c;AI技术已经深入到我们生活的每一个角落&#xff0c;为我们提供了前所未有的便利和可能性。在众多AI工具中&#xff0c;有几种特别受到人们的喜爱&#xff0c;并且在各自的领域中产生了深远的影响。 1、AI绘画工具 改图鸭AI绘…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(七)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 14 节&#xff09; P14《13.ArkUI组件-自定义组件》 将可变部分封装成组件的成员变量&#xff1a; 1、首先给标题添加两个图标&am…

用Docker 创建并运行一个MySQL容器

可以在DockerHub官网上荡:mysql - Official Image | Docker Hub 指令是:docker pull mysql; 因为文件比较大可能时间比较长&#xff0c;我是跟着黑马的课走的 课程提供的有文件&#xff0c;我就用已有的资源了。 在tmp目录里放入mysql.tar包 然后cd进去 输入指令:docker lo…

上位机开发PyQt(五)【Qt Designer】

PyQt5提供了一个可视化图形工具Qt Designer&#xff0c;文件名为designer.exe。如果在电脑上找不到&#xff0c;可以用如下命令进行安装&#xff1a; pip install PyQt5-tools 安装完毕后&#xff0c;可在如下目录找到此工具软件&#xff1a; %LOCALAPPDATA%\Programs\Python\…

设计模式之监听器模式ListenerPattern(三)

一、介绍 监听器模式是一种软件设计模式&#xff0c;在对象的状态发生改变时&#xff0c;允许依赖它的其他对象获得通知。在Java中&#xff0c;可以使用接口和回调机制来实现监听器模式。 二、代码实例 1、事件Event类 package com.xu.demo.listener;// 事件类 public class…

前端开发框架uni-app

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl uni-app概述 uni-app起源于跨平台应用开发的需求增长。在过去&#xff0c;为不同的平台&#xff08;如iOS、Android、各种小程序等&#xff09;开发应用需要分别编写和维护…

C++必修:类与对象(二)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 构造函数 1.1. 定义 构造函数是一个特殊的成员函数&#xff0c;名字与类名相…

【GitHub】github学生认证,在vscode中使用copilot的教程

github学生认证并使用copilot教程 写在最前面一.注册github账号1.1、注册1.2、完善你的profile 二、Github 学生认证注意事项&#xff1a;不完善的说明 三、Copilot四、在 Visual Studio Code 中安装 GitHub Copilot 扩展4.1 安装 Copilot 插件4.2 配置 Copilot 插件&#xff0…

【C++】学习笔记——string_2

文章目录 六、string类2. 反向迭代器const迭代器 string类对象的容量操作&#xff08;补&#xff09;size() 3. string类的元素访问4. string类的修改 未完待续 结合文档食用~ 六、string类 2. 反向迭代器 一般来说&#xff0c;迭代器都是正向的遍历容器&#xff0c;虽然可以…

开源协议与商业许可:选择与遵循

文章目录 一、开源协议1.1 MIT许可证&#xff08;MIT License&#xff09;1.2 BSD许可证&#xff08;BSD License&#xff09;1.3 Apache许可证 2.0&#xff08;Apache License 2.0&#xff09;1.4 GNU宽松通用公共许可证&#xff08;GNU Lesser General Public License&#x…

# 从浅入深 学习 SpringCloud 微服务架构(七)Hystrix(3)

从浅入深 学习 SpringCloud 微服务架构&#xff08;七&#xff09;Hystrix&#xff08;3&#xff09; 一、hystrix&#xff1a;通过 Actuator 获取 hystrix 的监控数据 1、Hystrix 的监控平台介绍&#xff1a; 1&#xff09;Hystrix 除了实现容错功能&#xff0c;Hystrix 还…

spring boot运行过程中动态加载Controller

1.被加载的jar代码 package com.dl;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication public class App {public static void main(String[] args) {SpringApplication.run(A…

【MySQL精炼宝库】深度解析索引 | 事务

目录 一、索引 1.1 索引(index)概念&#xff1a; 1.2 索引的作用&#xff1a; 1.3 索引的缺点&#xff1a; 1.4 索引的使用场景&#xff1a; 1.5 索引的使用&#xff1a; 1.6 面试题:索引底层的数据结构&#xff08;核心内容&#xff09;&#xff1a; 1.7 索引列查询(主…

启发式搜索算法1 - 最佳优先搜索算法

启发式搜索算法有什么优势&#xff1f; 对于复杂问题的盲目搜索&#xff0c;常用广度优先搜索和深度优先搜索这两种盲目搜索算法&#xff0c;极大极小值和Alpha-beta剪枝算法是在盲目搜索过程中&#xff0c;通过剪枝避开一些不可能的结果&#xff0c;从而提高效率。 如果搜索…