解码死锁的产生与解决

news/2025/11/18 18:29:12/文章来源:https://www.cnblogs.com/YouEmbedded/p/19226049

死锁

死锁是多线程 / 多进程并发编程中常见的严重问题,指两个或多个线程 / 进程因互相争抢资源而陷入无限等待状态,若无外力干预将永久无法继续执行。其本质是资源分配与调度不当导致的 “资源僵局”,典型现实类比是 “哲学家就餐问题”:五位哲学家围坐圆桌,每人需拿起左右两根筷子才能吃饭,若所有哲学家同时拿起左侧筷子,将永远等待右侧筷子,最终全部陷入停滞。

image

死锁产生的四个必要条件

死锁的发生必须同时满足以下四个条件,缺一不可,这是理解和解决死锁的核心基础:

  • 资源互斥:资源在同一时间只能被一个线程 / 进程占用,无法被多个线程 / 进程同时使用(如互斥锁、打印机等硬件资源)。

    image

  • 请求且保持:线程 / 进程已持有部分资源,又主动申请新的资源,且在申请新资源的过程中,不释放已持有的资源。

    image

  • 不可剥夺:已被线程 / 进程占用的资源,不能被强行剥夺,只能由资源持有者主动释放。

    image

  • 循环等待:多个线程 / 进程之间形成头尾相接的环形等待链,每个线程 / 进程都在等待下一个线程 / 进程持有的资源(如线程 A 等线程 B 的资源,线程 B 等线程 A 的资源)。

    image

死锁的预防策略(从根源杜绝死锁)

预防死锁的核心思路是破坏上述四个必要条件中的至少一个,从设计阶段杜绝死锁发生,是最有效且常用的方案:

破坏 “请求且保持” 条件

  • 核心方案:一次性申请所有资源。线程 / 进程在启动前,需一次性申请执行过程中所需的全部资源,若无法完全满足则放弃所有申请,进入等待状态,待资源充足时再重新申请。
  • 特点:实现简单,能彻底避免 “请求且保持” 导致的死锁,但资源利用率极低,可能造成大量资源闲置。

破坏 “不可剥夺” 条件

  • 核心方案:引入资源抢占机制。若线程 / 进程持有部分资源后,申请新资源失败,需主动释放已持有的所有资源,后续需重新申请全部资源(包括之前释放的)。
  • 实操方式:结合超时机制实现,使用带超时的资源申请函数(如pthread_mutex_timedlock),超时未获取新资源时,自动释放已持有资源,避免长期占用资源导致死锁。
  • 特点:需额外处理资源释放与重新申请的逻辑,增加编程复杂度和上下文切换开销。

破坏 “循环等待” 条件(最常用、最实用)

  • 核心方案:资源有序分配法。给所有资源(如锁、设备等)定义统一的优先级或序号,所有线程 / 进程必须按照 “递增顺序” 申请资源,禁止逆序申请。
  • 原理:强制统一的申请顺序后,线程 / 进程间无法形成环形等待链。例如给锁 L1、L2、L3 编号为 1、2、3,所有线程必须先申请 L1,再申请 L2,最后申请 L3,避免 A 等 B、B 等 A 的环路。
  • 特点:实现成本低、资源利用率高,是实际开发中首选的预防方案。

死锁的避免策略(运行时动态规避)

避免死锁不提前限制资源申请方式,而是在资源分配时动态判断,避免系统进入 “不安全状态”(可能发生死锁的状态):

  • 核心算法:银行家算法。系统维护所有资源的分配状态和进程的最大资源需求,每次分配资源前,先模拟分配过程,若分配后系统仍处于安全状态(存在一条进程执行序列,可让所有进程顺利完成)则分配,否则拒绝分配。
  • 特点:资源利用率高于预防策略,但存在明显局限性 —— 需提前预知所有进程的最大资源需求(实际开发中难以做到),且算法本身存在计算开销,仅适用于操作系统等底层系统设计,通用应用开发中极少使用。

死锁的检测与处理(事后解决方案)

该策略放任死锁发生,通过定期检测发现死锁后,采取措施打破僵局,适用于无法提前预防或避免的场景:

死锁检测

  • 核心机制:维护资源分配图。系统记录所有资源的分配关系(哪个进程持有哪个资源)和申请关系(哪个进程申请哪个资源),形成有向图,定期调用环路检测算法,若图中存在环路,则判定发生死锁。
  • 实操方式(Linux 系统):通过/proc/locks文件查看所有持有锁的进程 ID 和锁信息,命令:cat /proc/locks;也可使用ipcs -s查看 System V 信号量相关的进程资源占用。

死锁处理

  • 进程终止法:
    • 终止所有死锁进程:简单直接,但代价极高,可能导致数据丢失和任务中断。
    • 逐个终止进程:每次终止一个 “代价最小” 的进程(如优先级最低、已运行时间最短的进程),释放其资源后重新检测,直到死锁解除。
  • 资源抢占法:
    • 从死锁进程中抢占资源,分配给其他死锁进程,直到打破等待环路。需解决三个问题:选择哪个进程作为牺牲者(最小化损失)、如何回滚进程状态(抢占资源后,进程需回滚到未获取该资源的安全状态)、避免饥饿(防止同一进程反复被抢占)。

死锁场景代码示例(错误与修正)

死锁场景(错误代码)

两个线程因加锁顺序不一致,形成循环等待,最终触发死锁:

image

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 定义两个全局互斥锁
pthread_mutex_t lockA = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lockB = PTHREAD_MUTEX_INITIALIZER;// 线程1:先获取lockA,再获取lockB
void *thread1(void *arg) {// 加锁lockA,成功则持有该锁pthread_mutex_lock(&lockA);printf("线程1:持有lockA,尝试获取lockB\n");// 睡眠1秒,让线程2有机会获取lockB,制造循环等待条件sleep(1);// 尝试获取lockB,此时线程2已持有lockB,陷入等待pthread_mutex_lock(&lockB); // 死锁触发点:等待线程2的lockBprintf("线程1:成功获取lockB\n");// 解锁操作(死锁后无法执行到此处)pthread_mutex_unlock(&lockB);pthread_mutex_unlock(&lockA);return NULL;
}// 线程2:先获取lockB,再获取lockA(与线程1加锁顺序相反)
void *thread2(void *arg) {// 加锁lockB,成功则持有该锁pthread_mutex_lock(&lockB);printf("线程2:持有lockB,尝试获取lockA\n");// 睡眠1秒,让线程1有机会获取lockA,制造循环等待条件sleep(1);// 尝试获取lockA,此时线程1已持有lockA,陷入等待pthread_mutex_lock(&lockA); // 死锁触发点:等待线程1的lockAprintf("线程2:成功获取lockA\n");// 解锁操作(死锁后无法执行到此处)pthread_mutex_unlock(&lockA);pthread_mutex_unlock(&lockB);return NULL;
}int main() {pthread_t tid1, tid2;// 创建线程1pthread_create(&tid1, NULL, thread1, NULL);// 创建线程2pthread_create(&tid2, NULL, thread2, NULL);// 等待线程结束(死锁后永久阻塞)pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

修正方案:统一加锁顺序(破坏循环等待条件)

将所有线程的加锁顺序统一为 “先 lockA 后 lockB”,避免循环等待,从根源解决死锁:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t lockA = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lockB = PTHREAD_MUTEX_INITIALIZER;// 线程1:保持先lockA后lockB的顺序
void *thread1(void *arg) {pthread_mutex_lock(&lockA);printf("线程1:持有lockA,尝试获取lockB\n");sleep(1);pthread_mutex_lock(&lockB);printf("线程1:成功获取lockB\n");// 业务操作完成后,按加锁逆序解锁pthread_mutex_unlock(&lockB);pthread_mutex_unlock(&lockA);return NULL;
}// 线程2:修改为与线程1一致的加锁顺序(先lockA后lockB)
void *thread2(void *arg) {pthread_mutex_lock(&lockA); // 统一先加lockAprintf("线程2:持有lockA,尝试获取lockB\n");sleep(1);pthread_mutex_lock(&lockB); // 再加lockBprintf("线程2:成功获取lockB\n");// 按加锁逆序解锁,避免资源泄漏pthread_mutex_unlock(&lockB);pthread_mutex_unlock(&lockA);return NULL;
}int main() {pthread_t tid1, tid2;pthread_create(&tid1, NULL, thread1, NULL);pthread_create(&tid2, NULL, thread2, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}

线程取消导致的死锁及解决方案

线程执行过程中被取消(如调用pthread_cancel)时,若已持有资源(如锁)且未释放,会导致其他线程永久等待该资源,引发死锁。需通过特定机制确保线程取消时释放所有资源:

方案 1:使用取消处理函数(自动释放资源)

通过pthread_cleanup_pushpthread_cleanup_pop函数,在线程被取消时自动执行资源释放操作,确保资源不泄露:

函数解析

#include <pthread.h>/*** 线程取消请求发送函数(POSIX标准接口)* @brief 向指定线程发送取消请求,请求线程终止执行,是线程间主动终止目标线程的核心接口* @param thread 目标线程的ID(由pthread_create创建线程时输出参数获取)* @return int 返回值含义:*              - 0:取消请求发送成功(仅表示请求已递交,不代表线程已终止);*              - ESRCH:目标线程ID不存在(如线程已退出、ID无效);*              - EINVAL:线程取消机制被禁用(目标线程通过pthread_setcancelstate设置为PTHREAD_CANCEL_DISABLE)* @note 取消响应特性:线程是否响应取消、何时响应,由目标线程的取消状态和类型决定,非立即终止;*       取消状态控制:目标线程需处于PTHREAD_CANCEL_ENABLE状态(默认启用)才能接收取消请求,禁用状态下请求会被忽略;*       取消类型影响:*           - PTHREAD_CANCEL_DEFERRED(默认):延迟响应,线程仅在执行到"取消点"时才处理取消请求(如sleep、read、pthread_join等系统调用);*           - PTHREAD_CANCEL_ASYNCHRONOUS:立即响应,无需等待取消点,收到请求后尽快终止(需谨慎使用,可能导致资源泄漏);*       资源泄漏风险:若目标线程持有锁、动态内存等资源时被取消,且未设置清理函数,会导致资源永久占用(死锁诱因之一);*       线程退出状态:被取消的线程退出状态为PTHREAD_CANCELED(宏定义为(void*)-1),可通过pthread_join的第二个参数获取;*       不可取消场景:线程执行某些关键操作(如内核态代码、原子操作)时,可能暂时无法响应取消,需等待操作完成;*       与pthread_exit区别:pthread_exit是线程主动终止,pthread_cancel是外部线程发起的被动终止请求*/
int pthread_cancel(pthread_t thread);/*** 线程取消清理函数压栈函数(POSIX标准接口)* @brief 将指定的清理函数压入当前线程的"取消清理栈",线程被取消或执行pthread_cleanup_pop(非0)时,自动执行该函数* @param routine 取消清理函数指针,函数原型必须为void (*routine)(void *),线程触发清理时自动调用,参数为arg* @param arg 传递给清理函数routine的参数(可传递锁地址、内存指针等需释放的资源标识)* @return 无返回值* @note 栈特性:清理栈遵循"后进先出(LIFO)"原则,最后压入的函数最先执行;*       配对使用要求:必须与pthread_cleanup_pop函数成对出现,且在同一代码块(如同一函数、同一循环体)中,否则编译报错;*       压栈时机:需在获取资源(如加锁、分配内存)之前压入对应清理函数,确保资源持有期间触发取消时能正确释放;*       执行触发条件:*           - 线程被pthread_cancel取消且响应成功;*           - 线程调用pthread_exit主动退出;*           - 调用pthread_cleanup_pop时参数为非0值(无论是否取消,均执行弹出的清理函数);*       正常执行场景:若线程未被取消且正常执行到pthread_cleanup_pop(0),则仅弹出清理函数,不执行;*       嵌套使用:支持多次压栈多个清理函数,适用于多资源持有场景(如同时持有多个锁,需按逆序释放)*/
void pthread_cleanup_push(void (*routine)(void *), void *arg);/*** 线程取消清理函数出栈函数(POSIX标准接口)* @brief 将当前线程"取消清理栈"顶部的清理函数弹出,根据参数决定是否执行该函数* @param execute 执行控制参数:*                - 0:仅弹出清理函数,不执行(适用于线程正常执行完成、无需清理资源的场景);*                - 非0:弹出清理函数的同时,立即执行该函数(适用于线程主动退出前、需手动触发资源清理的场景)* @return 无返回值* @note 与push的绑定关系:必须与pthread_cleanup_push一一对应,数量不匹配会导致编译链接错误(编译器通过宏检查配对);*       执行时机影响:参数非0时,无论线程是否被取消,都会执行弹出的清理函数,可用于主动释放资源(如函数退出前统一清理);*       资源释放顺序:若多次嵌套push,pop时按逆序执行(最后push的函数最先pop并执行),需与资源申请顺序相反(如先申请锁A再申请锁B,清理时先释放B再释放A);*       代码块限制:push和pop必须在同一作用域(如{}内),跨函数或跨条件分支的配对会导致编译失败;*       与线程退出的配合:线程调用pthread_exit前,若未执行pop(非0),会自动执行清理栈中所有未弹出的清理函数,确保资源释放;*       避免重复释放:清理函数中释放的资源(如锁、内存),需确保未被其他代码提前释放,防止双重释放导致程序崩溃*/
void pthread_cleanup_pop(int execute)

示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 互斥锁定义
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;/*** @brief 锁资源释放的取消处理函数* @param arg 传递的参数(此处为锁的地址)* @note 线程被取消时,该函数会自动执行,释放已持有的锁*/
void lock_cleanup_handler(void *arg) {pthread_mutex_t *mutex = (pthread_mutex_t *)arg;printf("线程被取消,自动释放锁资源\n");pthread_mutex_unlock(mutex); // 释放锁,避免死锁
}/*** @brief 线程执行函数* @param arg 线程参数(未使用)* @return NULL 线程退出返回值*/
void *task_func(void *arg) {// 加锁前,将解锁函数压入取消处理栈// 第一个参数:取消时执行的处理函数地址// 第二个参数:传递给处理函数的参数(锁的地址)pthread_cleanup_push(lock_cleanup_handler, &lock);// 获取锁资源pthread_mutex_lock(&lock);printf("线程获取锁,开始执行业务逻辑\n");// 模拟耗时操作,期间可能被取消while (1) {sleep(1);printf("线程执行中...\n");}// 正常执行时,弹出取消处理函数且不执行(参数0)// 若线程被取消,此处不会执行,栈中的处理函数会自动调用pthread_cleanup_pop(0);return NULL;
}int main() {pthread_t tid;// 创建线程pthread_create(&tid, NULL, task_func, NULL);// 主线程睡眠3秒,让子线程获取锁并执行sleep(3);// 发送线程取消请求printf("主线程发送取消请求\n");pthread_cancel(tid);// 等待线程退出,回收资源pthread_join(tid, NULL);printf("子线程已退出,资源回收完成\n");// 销毁互斥锁pthread_mutex_destroy(&lock);return 0;
}

方案 2:控制线程取消状态与类型

通过pthread_setcancelstatepthread_setcanceltype函数,控制线程对取消请求的响应方式,避免在持有资源时被取消:
函数解析

#include <pthread.h>/*** 设置线程取消状态(POSIX标准接口)* @brief 控制当前线程是否接受外部取消请求(pthread_cancel),是线程取消机制的"总开关"* @param state 线程新的取消状态:*              - PTHREAD_CANCEL_ENABLE:启用取消机制(默认值),线程可接收并响应取消请求;*              - PTHREAD_CANCEL_DISABLE:禁用取消机制,线程会忽略所有取消请求(请求会被挂起,直到重新启用)* @param oldstate 输出参数(可为NULL),用于保存线程修改前的旧取消状态,若需后续恢复旧状态可传入有效指针* @return int 返回值含义:*              - 0:状态设置成功;*              - EINVAL:state参数无效(既不是PTHREAD_CANCEL_ENABLE,也不是PTHREAD_CANCEL_DISABLE)* @note 核心作用:决定线程是否"允许被取消",与pthread_setcanceltype(取消响应方式)配合使用;*       禁用时的请求处理:线程禁用取消期间,外部调用pthread_cancel会返回0(请求递交成功),但线程不会响应,*          直到重新启用取消状态,挂起的请求才会被处理(延迟响应场景下需等待取消点);*       状态切换的原子性:该函数是原子操作,不会被取消请求打断,确保状态切换的一致性;*       与清理函数的配合:禁用取消状态时,即使线程持有资源,也不会因取消请求导致资源泄漏(因请求被忽略);*       适用场景:执行关键任务(如数据写入、资源初始化)时,禁用取消避免任务中断;任务完成后重新启用;*       线程创建默认值:新创建的线程默认取消状态为PTHREAD_CANCEL_ENABLE,无需手动启用*/
int pthread_setcancelstate(int state, int *oldstate);/*** 设置线程取消类型(POSIX标准接口)* @brief 当线程取消状态为启用(PTHREAD_CANCEL_ENABLE)时,指定线程响应取消请求的方式* @param type 线程新的取消类型:*              - PTHREAD_CANCEL_DEFERRED:延迟响应(默认值),线程仅在执行到"取消点"时才处理取消请求;*              - PTHREAD_CANCEL_ASYNCHRONOUS:立即响应,线程收到取消请求后尽快终止(无需等待取消点)* @param oldtype 输出参数(可为NULL),用于保存线程修改前的旧取消类型,若需后续恢复旧类型可传入有效指针* @return int 返回值含义:*              - 0:类型设置成功;*              - EINVAL:type参数无效(既不是PTHREAD_CANCEL_DEFERRED,也不是PTHREAD_CANCEL_ASYNCHRONOUS);*              - ENOTSUP:部分系统不支持PTHREAD_CANCEL_ASYNCHRONOUS类型(极少数嵌入式系统)* @note 依赖取消状态:仅当线程取消状态为PTHREAD_CANCEL_ENABLE时,取消类型才生效;禁用状态下类型设置无意义;*       取消点说明:延迟响应(DEFERRED)模式下,线程仅在执行标准定义的"取消点"时响应取消,常见取消点包括:*           - 系统调用:sleep、read、write、pthread_join、pthread_cond_wait、select等;*           - 库函数:malloc、free、printf等(部分标准库函数会触发取消点);*           - 无取消点场景:纯计算循环(无系统调用/库函数)中,延迟响应的线程可能永远不响应取消(需手动插入pthread_testcancel());*       立即响应风险:ASYNCHRONOUS类型可能导致线程在任意时刻终止(如执行临界区代码、分配内存后未初始化时),*          极易引发资源泄漏、数据损坏,仅适用于无资源持有、无状态的简单线程;*       清理函数兼容性:无论哪种类型,线程被取消时都会执行取消清理栈中的函数(pthread_cleanup_push注册的函数),*          但立即响应类型的清理函数执行时机更不确定,需确保清理函数是线程安全的;*       与pthread_testcancel配合:延迟响应模式下,若线程执行长时间纯计算(无取消点),可手动调用pthread_testcancel()*          插入取消点,让线程有机会响应取消请求;*       线程创建默认值:新创建的线程默认取消类型为PTHREAD_CANCEL_DEFERRED,推荐优先使用该类型*/
int pthread_setcanceltype(int type, int *oldtype);
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;/*** @brief 线程执行函数* @param arg 线程参数(未使用)* @return NULL 线程退出返回值*/
void *task_func(void *arg) {// 设置线程取消状态为“禁用”(不接受取消请求)// 第一个参数:PTHREAD_CANCEL_DISABLE(禁用)/PTHREAD_CANCEL_ENABLE(启用)// 第二个参数:保存旧状态的指针(NULL表示不保存)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);// 持有资源执行核心业务(禁用取消,避免中途被取消导致资源泄漏)pthread_mutex_lock(&lock);printf("线程获取锁,执行核心业务(禁用取消)\n");int count = 5;while (count--) {sleep(1);printf("核心业务执行中,剩余%d秒\n", count);}// 释放资源pthread_mutex_unlock(&lock);printf("核心业务完成,释放锁资源\n");// 重新启用取消请求,允许后续被取消pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);// 设置取消类型为“立即响应”(无需等待取消点)// 第一个参数:PTHREAD_CANCEL_ASYNCHRONOUS(立即响应)/PTHREAD_CANCEL_DEFERRED(延迟响应)// 第二个参数:保存旧类型的指针(NULL表示不保存)pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);// 后续循环执行,可被立即取消while (1) {sleep(1);printf("非核心业务执行中(允许立即取消)\n");}return NULL;
}int main() {pthread_t tid;pthread_create(&tid, NULL, task_func, NULL);sleep(3);printf("主线程发送取消请求\n");pthread_cancel(tid);pthread_join(tid, NULL);printf("子线程已退出\n");pthread_mutex_destroy(&lock);return 0;
}

取消点说明

线程取消请求的响应依赖 “取消点”—— 系统定义的函数调用点(如sleepreadpthread_join等,详细查看man手册第七章pthreads)。

image

延迟响应模式(PTHREAD_CANCEL_DEFERRED)下,线程仅在执行到取消点时才响应取消请求;

立即响应模式(PTHREAD_CANCEL_ASYNCHRONOUS)下,无需等待取消点,可随时响应取消。

死锁预防的其他实用技巧

  • 减少锁的使用范围:仅在必要的临界区使用锁,避免大面积加锁;临界区代码尽量简短,执行完成后立即解锁,减少锁持有时间。
  • 使用带超时的加锁函数:优先使用pthread_mutex_timedlock(互斥锁超时加锁)、pthread_rwlock_timedwrlock(读写锁写超时加锁)等函数,超时未获取锁时,主动释放已持有资源并重试或退出,避免永久阻塞。
  • 避免嵌套锁:尽量不要在持有一个锁的同时,再申请另一个锁(嵌套加锁),嵌套层级越多,死锁风险越高。
  • 资源复用与池化:通过资源池(如线程池、连接池)管理资源,减少动态资源申请与释放,降低资源竞争概率。

死锁检测与处理的实操命令(Linux 系统)

  • 查看锁持有情况cat /proc/locks,列出所有进程持有的锁信息(包括进程 ID、锁类型、锁地址等)。
  • 查看 System V 信号量 / 共享内存资源ipcs -s(信号量)、ipcs -m(共享内存),查看系统中残留的资源。
  • 手动删除残留资源ipcrm -s 信号量ID(删除信号量)、ipcrm -m 共享内存ID(删除共享内存),用于清理死锁进程残留的资源。
  • 强制终止死锁进程kill -9 进程ID,通过终止死锁进程释放资源,需先通过pstop命令获取死锁进程的 ID。

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

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

相关文章

uniapp的rich-text在渲染长数字与长字母时不换行

在rich-text的容器上新增 容器 容器 容器 不是rich-text组件本身word-break: break-all; word-wrap: break-word; 出现此问题是因为浏览器将其视为了一个单词所以未进行换行

头部厂商易路AI HR实战解析:从人海战术到智能闭环的合规跃迁

在连锁零售与餐饮行业,食品安全是企业的生命线,而员工健康证管理则是保障这条生命线的第一道防线。对于拥有数千家门店、数万名员工的行业巨头而言,健康证管理绝非一项简单的事务性工作,它关乎企业合规的基石、品牌…

【微信小程序 + 登录流程】微信小程序授权登录完整流程,一篇搞定!(含代码实现) - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

linux auto

您提到的“Linux auto”可能是指“Linux 自动化”或“Linux 自动化工具”,我将为您介绍一些常见的 Linux 自动化工具和命令,帮助您实现自动化任务。一、Linux 自动化工具 1. Ansible功能:基于 YAML 的自动化工具,支…

记录相关的操作

details summary::before { content: "▶"; position: absolute; left: 8px; transition: all 0.3s ease } details[open] summary::before { transform: rotate(90deg) } summary { list-style: none; curs…

P9846 [ICPC 2021 Nanjing R] Paimons Tree

派蒙题。 首先发现填完权值后的直接必定是原树直径中的某一条,但是无法确定是其中哪一条,所以这个发现是没用的/tx。 发现按照题目的染色方法,每个时刻染色的边必然为一条路径。 观察到 \(n\) 的范围很小,首先考虑…

linux audio

在 Linux 系统中,音频相关操作可以通过多种方式实现,包括使用内核音频驱动、 ALSA(Advanced Linux Sound Architecture)或 JACK 音频服务器等。以下是一些常见的 Linux 音频操作命令和脚本示例,适用于大多数 Linu…

不同方向的箭头符号

1. 基础方向箭头 ▶ 右箭头 (U+25B6) ◀ 左箭头 (U+25C0) ▼ 下箭头 (U+25BC) ▲ 上箭头 (U+25B2)2. 实心箭头 🔺 实心上三角 (U+1F53A) 🔻 实心下三角 (U+1F53B) ⏩ 快进右箭头 (U+23E9) ⏪ 快退左箭头 (U+23EA)3…

11.13 表子查询 内连接补充 事务

SELECT xx.*FROM(SELECT。。。。。; 内连接出来的是笛卡尔积,必须用条件消除 事务就是捆绑的操作集合 事务操作 SELECT @@autocommit ==1,自动提交 SET @@zutocommit=0; 手动提交: commint; 执行失败要回滚事务 ro…

Elasticsearch 7.17 集群添加账号密码

Elasticsearch 7.17 集群添加账号密码1. 环境信息 1.1 主机列表IP 主机名 操作系统 JAVA_HOME10.0.0.22 SY-AFP-ES01 Red Hat Enterprise Linux release 8.6 (Ootpa) /opt/app/middles/jdk1.8.0_47110.0.0.23 SY-AFP-E…

实用指南:【XR硬件系列】影目GO3智能眼镜发布:AI翻译+轻薄设计,重塑人机交互体验

实用指南:【XR硬件系列】影目GO3智能眼镜发布:AI翻译+轻薄设计,重塑人机交互体验pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; fo…

深入解析:推荐给硬件工程师的技术书籍

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

全球可观测厂商怎么选?2025年可观测性平台深度分析

在Gartner发布的《2025可观测性平台关键能力报告》中,评估维度涵盖数据摄取与存储、交互式探索、成本控制、LLM可观测性等核心能力,为全球企业选型提供了权威参考 根据Gartner报告描述的这几个核心能力,我们做了一个…

完整教程:PRCV 2025:文本何以成为 AGI 的必经之路?

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Ubuntu Server 22.04.5 linux系统安装教程

Ubuntu Server 22.04系统镜像下载本教程的Ubuntu Server 22.04系统镜像是从Ubuntu官网下载的,下载链接如下:https://ubuntu.com/download/server/step1打开VMware Workstation Pro ,打开创建新的虚拟机3.点击自定义…

2025 ICPC 沈阳区域赛 游记

2025 ICPC 沈阳区域赛 游记 计院没钱了,ICPC不给报销,遂开放报名。重金获得一次参赛机会。 为了节省住宿钱,我们选择星期六早上出发,星期天晚上回,只住一晚,订了个三人间。 Day 0 三个人都是两点钟睡觉六点钟起床…

在树莓派中配置X11桌面的HDMI配置

在Linux系统中配置X11的HDMI输出,主要通过修改Xorg配置文件或使用xrandr命令实现。以下是具体方法:一、通过Xorg配置文件固定HDMI分辨率‌生成分辨率模型‌ 使用cvt命令生成所需分辨率的模型参数(如19201080@60H…

2025年最新苗木批发基地综合实力排行榜单,国槐/樱花/红叶李/苗木/金叶复叶槭/红叶石楠/丝棉木/油松/白蜡/金叶女贞/紫薇种植推荐

行业权威榜单发布,优质苗木供应商全景解析 随着城乡绿化建设持续推进,苗木批发行业迎来新一轮发展机遇。基于近三年市场表现、供应链能力及客户反馈等维度,专业调研机构对国内苗木批发基地进行综合评估,现发布2025…

VideoLLaMA 3新一代前沿多模态基础模型赋能图像与视频深度理解| LLM | 计算机视觉

VideoLLaMA 3是基于Llama 3的前沿多模态基础模型,深度融合视觉、听觉与语言理解能力,支持高分辨率图像和长视频的端到端分析。其核心技术包括统一的视听语言架构、高效长视频词元化和万亿级多模态预训练,具备复杂的…

2025 最新移动厕所源头厂家推荐:千台设备储备 + 全国服务网点,国际测评认证优质品牌榜单工地临时/户外移动厕所出租/移动公厕租赁/出租移动厕所公司推荐

引言 随着大型活动、工程建设、户外文旅等场景对临时厕所的需求呈爆发式增长,行业对优质供应商的筛选标准愈发严苛。据国际环境卫生协会(IEHA)最新测评数据显示,全球优质移动厕所品牌需同时满足设备储备量≥500 台…