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

【RTOS学习】源码分析(信号量和互斥量 事件组 任务通知)

🐱作者:一只大喵咪1201
🐱专栏:《RTOS学习》
🔥格言:你只管努力,剩下的交给时间!
图

目录

  • 🍓信号量和互斥量
    • 🍅创建
    • 🍅Take
    • 🍅Give
  • 🍓事件组
    • 🍅设置事件
    • 🍅等待事件
    • 🍅同步点
  • 🍓任务通知
    • 🍅发通知
    • 🍅等待通知
  • 🍓总结

🍓信号量和互斥量

信号量和互斥量几乎一模一样:

创建:

图
如上图所示,创建时使用的都是xSemaphoreCreateXXX函数,只是后面的XXX不一样,其他都非常类似,而且本质上都是调用的xQueueGenericCreate函数来创建通用队列,只是传入的参数不一样。

Give:

图
如上图所示,Give时,使用的都是xSemaphoreGiveXXX,本质上也是在调用xQueueGenericSend向通用队列中写数据,只是参数不同。

Take:

图
如上图所示,Take时,使用的都是xSemaphoreTakeXXX,本质上也是在调用xQueueSemaphoreTake这个函数,只是参数不同,注意,这里并不是调用xQueueGenericReceive函数,和队列的操作不同。

可以看到,信号量和互斥量在创建,释放,申请信号量时的函数非常类似,所以本喵只需要讲解一种即可,就介绍一下最复杂的递归互斥量吧。

🍅创建

图
如上图代码所示,调用xSemaphoreCreateRecursiveMutex创建递归互斥量,这是一个宏函数,会调用xQueueCreateMutex,在该函数中会调用xQueueGenericCreate创建通用队列。

但在这之前先让创建的队列长度为1,队列中每个数据的大小是0字节,然后再调用xQueueGenericCreate

根据前面创建队列时的分析我们知道,此时只会在堆区上创建一个队列结构体Queue_t,并不会分配环形缓冲区。

然后再调用prvInitialiseMutex函数初始化互斥量:

tu
如上图所示prvInitialiseMutex函数,将Queue_t中联合体中xSemaphore成员的xMutexHolder用来表示锁的所有者设置为NULL,再将uxRecursiveCallCount递归锁计数次数设置为0。

最后再调用xQueueGenericSend向队列中写数据,写入数据是NULL,这里仅仅是让uxMessagesWaiting = 1,好让互斥量有初始值。


图

如上图,所以此时得到的Queue_t队列是这样的,没有存放数据的环形缓冲区,只有一个队列头,其中的成员也被赋予了合适的初始值。

🍅Take

图

如上图所示,使用xSemaphoreTakeRecursive申请一把递归锁,该函数会调用xQueueTakeMutexRecursive函数,在这个函数中,先判断锁的持有者身份xMutexHolder,如果是当前任务pxCurrentTCB,则说明此时是递归上锁,则将递归上锁次数uxRecursiveCallCount加一。

如果持有者身份是NULL或者不是当前任务,调用xQueueSemaphoreTake申请锁,申请成功后让uxRecursiveCallCount加一。

图
如上图xQueueSemaphoreTake函数,先判断可否申请锁,也就是队列中的有效数据是否大于0,如果可以申请则将有效数据uxMessagesWaiting的数值减一,然后通过pvTaskIncrementMutexHeldCount函数记录持锁人身份到pxQueue->u.xSemaphore.xMutexHolder成员中。

图
如上图所示,如果队列中的有效数据小于等于0,说明此时无法申请锁,如果该任务不愿意等待的话就直接错误返回,如果愿意等待,则调用vTaskInternalSetTimeOutState记录当前时刻。

在确认要阻塞后,调用xTaskPriorityInherit函数进行优先级继承,然后将该任务放入等待读取数据的xTasksWaitingToReceive链表中,再主动发起调度,让当前任务阻塞。


图
如上图xTaskPriorityInherit函数,如果被阻塞任务的优先级大于持锁者的优先级,并且持锁者在就绪链表中,则交换双方的位置,也就是将二者的优先级交换,并且放入对应的就绪链表中。如果持锁者不在就绪链表中,则直接将当前阻塞任务的优先级给它即可。

如果被阻塞任务的优先级小于等于持锁者的优先级,则不需要进行优先级继承。


图
如上图所示,当这个申请锁的任务再次被唤醒时,也是有两种情况,如果是有人释放了锁,那么for循环中再次判断操作时会成功申请到锁,成功返回。

如果是超时被唤醒,则会先调用prvGetDisinheritPriorityAfterTimeout将被继承的优先级恢复原样,然后错误返回。

🍅Give

图
如上图所示xSemaphoreGiveRecursive,用来释放递归锁,最后会调用xQueueGiveMutexRecursive函数,在该函数中,首先判断释放锁的是否是锁的持有者,如果是持有者,则先将递归次数减一,当该次数为0时,说明不是递归释放,则向队列中写数据,就是让有效数据uxMessagesWaiting加一。

如果不是持有者,则直接错误返回,并不会阻塞。

  • 在释放锁的过程中,并不会阻塞,如果失败就直接返回。

总的来说,使用锁的过程分为如下几步:

  • 创建互斥锁并进行初始化,让锁有初始值,但是此时持锁者为NULL
  • 申请锁时:
    • 如果不是持有者申请锁,则看有效数据个数uxMessagesWaiting是否大于0,如果大于0说明可以申请,如果小于等于0,说明不可以申请,此时会将申请者放入到锁的管理读取数据的事件链表中。
    • 如果是锁的持有者,对于递归锁则会让递归次数增加,非递归锁和不是持有者的待遇一样。
  • 释放锁时:
    • 如果不是持有者,直接错误返回,因为锁不能被其他任务随意释放。
    • 如果是持有者,对于递归锁,则让递归次数减少,当递归次数减少为0时,则向队列中写数据,让有效数据的个数uxMessagesWaiting重新变为1。

🍓事件组

图

如上图所示事件组结构体EventGroup_t的定义,包含两个成员:

  • uxEventBits:这是一个32位的变量,用来存放事件,只是用低24位,每一个比特位代表一个事件。
  • xTasksWaitingForBits:这是一个链表头,该链表用来存放因等待事件就绪而阻塞的任务TCB。

图
如上图xEventGroupCreate函数所示,在创建事件组时,先在堆区上开辟一块存放EventGroup_t结构体的空间,然后将结构体中表示事件值的uxEventBits清0,再初始化一下链表xTasksWaitingForBits


图
如上图所示,每个任务TCB里的xEventListItem事件链表中,每个链表项中的xItemValue,该32位的变量也可以用来表示事件。

  • 高8位用来表示控制位:比如等待后是否清除事件等等。
  • 低24位用来表示事件:每一位表示一个事件和EventGroup_t中的事件位对应。

🍅设置事件

tu
如上图xEventGroupSetBits函数所示,在该函数中,先获取事件组中的链表里的第一个链表项,这里可能管理着因正在等待事件就绪而处于阻塞状态的任务。

然后将要设置的事件值,使用或等的方式赋值给EventGroup_t中的uxEventBits

图
如上图所示代码,在将事件值设置好以后,需要判断是否有任务可以唤醒,使用while循环遍历链表中的所有任务。

在判断过程中,先拿到事件控制位uxControlBits,也就是在等待事件的任务对事件的态度,如果不是eventWAIT_FOR_ALL_BITS,说明当前遍历的任务不要求所有等待是事件都就绪,只要uxBitsWaitedFor & pxEventBits->uxEventBits != 0,说明只有一个或者多个事件就绪,此时将xMatchFound = pdTRUE,表示可以唤醒链表中等待的任务。

如果控制位表示要等待所有事件就绪,则只有uxBitsWaitedFor & pxEventBits->uxEventBits == uxBitsWaitedFor时,也就是所有等待事件都就绪时,才可以唤醒链表中等待的任务。

接下来如果有任务可以唤醒时,从正在遍历的当前任务中uxControlBits拿到是否要清除已经就绪的事件所在的bit。

之后再调用vTaskRemoveFromUnorderedEventList将要唤醒的任务从事件组的链表中移除,并且设置标志eventUNBLOCKED_DUE_TO_BIT_SET,表示该任务被唤醒是由于等待事件成功。

最后返回事件组中的事件值pxEventBits->uxEventBits


在设置事件值时主要分为三大步:

  • 设置要等待的事件值。
  • 唤醒事件组链表中正在等待事件就绪的任务:
    • 等待事件的任务有要求全部事件就绪时才会被唤醒。
    • 等待事件的任务也有只要求有事件就绪时就可以被唤醒。
  • 在退出函数时,根据控制位决定是否清除事件组中已经就绪的事件。

🍅等待事件

图
如上图xEventGroupWaitBits函数,该函数是由某个任务调用的,用来等待事件组中要等待的事件。

首先就是获取事件组中的事件值uxCurrentEventBits,然后判断其与等待任务要等待的事件uxBitsToWaitFor是否相等,要根据xWaitForAllBits决定是所有事件都就绪才符合等待要求还是有事件就绪就符合等待要求。

当符合等待要求时,根据控制位决定是否在退出该函数前将事件组中已经就绪的事件值清除,如果不符合等待要求,且该任务不愿意等待,则超时返回,如果愿意等待,则将该任务放入到事件组用来维护等待事件的任务链表中。

然后主动发起调度,当前任务就阻塞在这里了。

当该任务再次被唤醒时,有可能是等待的事件就绪了被唤醒,也有可能是因为超时而被唤醒:

图
如上图所示,当等待事件的任务再次被唤醒时,根据eventUNBLOCKED_DUE_TO_BIT_SET判断一下是否因为事件就绪被唤醒,如果不是,则说明是超时,再判断一次事件是否就绪,没有就绪则超时返回。

如果是事件就绪而被唤醒,等待成功返回。


等待过程中注意分为三步:

  • 判断要等待的事件和事件组中的事件值,根据任务指定的控制值决定是所有事件都就绪才算等待成功还是只要有事件就绪就算等待成功。
  • 如果等待成功,则成功返回,等待不成功,则根据是否愿意等待决定是错误返回还是放入事件组的阻塞链表中。
  • 处于阻塞状态再次被唤醒后,根据eventUNBLOCKED_DUE_TO_BIT_SET位判断是事件就绪被唤醒还是超时唤醒。

🍅同步点

tu
如上图xEventGroupSync函数,调用该函数可以实现同步点,在函数内部,首先将要等待的事件设置到事件组中:

  • 如果在设置过程中,其他等待同步点的事件产生,则唤醒其他任务
  • 并且判断自己要等待的事件也全部发生,自己不会被阻塞。

如果自己要等待的事件没有全部就绪,说明其他任务也没有被唤醒,此时将当前任务继续添加到事件组的阻塞等待链表中。

如果当前任务愿意等待,则主动发起调度,阻塞到这里。

再次被唤醒后,如果eventUNBLOCKED_DUE_TO_BIT_SET不为1,说明是超时唤醒,则错误返回,如果该位为1,说明是事件被设置唤醒。

多个等待同步的任务都会从这里被唤醒,开始一起继续执行。

🍓任务通知

图
如上图所示,使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信。

图
如上图所示,使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"。

使用任务通知时,可以明确指定:通知哪个任务。

tu
如上图所示,每个任务都有一个结构体:TCB(Task Control Block),里面有2个成员:

  • 一个是uint8_t类型的ucNotifyState数组,用来表示通知状态。
  • 一个是uint32_t类型ulNotifiedValue数组,用来表示通知值。

数组的大小#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1为1,可以将数组看成一个变量。

通知状态有3种取值:

  • taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
  • taskWAITING_NOTIFICATION:任务在等待通知
  • taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为pending(有数据了,待处理)

🍅发通知

图

如上图所示xTaskNotify,调用该函数项目标任务发出通知时,会调用xTaskGenericNotify,在该函数中,首先获取被通知任务的状态值ucNotifyState,然后将该值设置为taskNOTIFICATION_RECEIVED,表示已经对该任务发出了通知。

根据eAction对被通知的任务通知值ulNotIfiedValue进行操作,可以赋值,可以加加,可以覆盖,也可以不做任何操作等等。

操作完毕后,判断一下被通知之前目标任务的状态,如果是taskWAITING_NOTIFICATION,说明目标任务在等待通知而处于阻塞状态,所以此时将目标任务放入到就绪链表中,如果被通知任务的优先级更高,则主动发起调度。


向任务发起通知总的来说就三步:

  • 设置通知状态为taskNOTIFICATION_RECEIVED
  • 根据eACction操作通知值ulNotIfiedValue
  • 任务如果原本阻塞,则将其放入就绪链表中。

🍅等待通知

图
如上图xTaskNotifyWait函数,任务调用该函数等待任务通知,最后会调用xTaskGenericNotifyWait函数,在该函数内,首先判断当前任务的任务状态ucNotifyState

  • 如果状态值不是taskNOTIFICATION_RECEIVED,说明没有接到通知:
    • 根据控制位决定是否在入口处清除指定事件。
    • 将当前任务状态设置为taskWAITING_NOTIFICATION,表示正在等待通知。
    • 如果愿意等待,则将当前任务放入到延时链表中,然后发起调度,当前任务阻塞。
  • 被唤醒或者第一次调用就接收到任务通知:
    • 再次判断任务状态,如果不是taskNOTIFICATION_RECEIVED,说明是超时唤醒,直接错误返回。
    • 如果是taskNOTIFICATION_RECEIVED,说明接收到任务通知被唤醒,根据控制位决定是否在出口处清除事件。

🍓总结

虽然互斥量信号量,事件组名字中不包含队列,但其本质上还是使用的通用队列,只是该队列中不存放数据本身,只靠Queue_t中的成员。

对于任务通知,不如说是通知任务,更是没有用于两个任务间通信的结构,直接从一个任务指定通知另一个任务,改变的是TCB中的任务通知状态和任务通知值。

相关文章:

【RTOS学习】源码分析(信号量和互斥量 事件组 任务通知)

🐱作者:一只大喵咪1201 🐱专栏:《RTOS学习》 🔥格言:你只管努力,剩下的交给时间! 目录 🍓信号量和互斥量🍅创建🍅Take🍅Give &#x…...

1316:【例4.6】数的计数(Noip2001) 代码+解析

1316:【例4.6】数的计数(Noip2001) 【题目描述】 我们要求找出具有下列性质数的个数(包括输入的自然数n )。先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理:不作任何处理;在它的左边加上一…...

征集倒计时 | 2023年卓越影响力榜单-第四届中国产业创新奖报名即将截止

第四届「ISIG中国产业智能大会」将于2024年3月16日在上海举办。2024 ISIG 以“与科技共赢,与产业共进”为主题,共设立RPA超自动化、 低代码、AIGC大模型、流程挖掘四大主题峰会。届时,大会组委会将颁发2023年度卓越影响力榜单—第四届中国产业…...

vue的语法模板与数据绑定的说明

vue的两大模板语法: 1.插值语法 2.指定语法 插值语法:{{}} 功能:用于解析标签体的内容 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性 指定语法: 功能:用于解析标签(包括:标签属性、标…...

VueCron使用方法

1)什么是vueCron Vue Cron 是基于 Vue.js 的定时任务管理组件,它提供了一种简单易用的方式来设定和管理定时任务。Vue Cron 提供了一个类似于 Linux crontab 的界面,用户可以通过它来创建、编辑和删除定时任务。 2)安装依赖及应…...

SpringBlade export-user SQL 注入漏洞复现

0x01 产品简介 SpringBlade 是一个由商业级项目升级优化而来的 SpringCloud 分布式微服务架构、SpringBoot 单体式微服务架构并存的综合型项目。 0x02 漏洞概述 SpringBlade v3.2.0 及之前版本框架后台 export-user 路径存在安全漏洞,攻击者利用该漏洞可通过组件customSqlS…...

结构体的一些补充知识

1、结构体后面分号前面的名字是什么意思。 在C中&#xff0c;结构体的定义格式为&#xff1a; struct <结构体名> {// 成员变量和成员函数 };在这个定义中&#xff0c;<结构体名>就是结构体的名称&#xff0c;而这个名称位于结构体定义的末尾&#xff0c;分号之前…...

20V升26V 600mA升压型LED驱动芯片,PWM调光芯片-AH1160

AH1160是一个功能强大的升压型LED驱动芯片&#xff0c;专为需要精确控制LED亮度的PWM调光应用而设计。它可将20V输入电压升压至26V&#xff0c;同时提供稳定的600mA电流输出&#xff0c;适用于各种LED照明设备。 芯片特点&#xff1a; 1. 输入电压范围&#xff1a;AH1160可在…...

如何在Go中制作HTTP服务器

引言 许多开发人员至少会花一些时间创建服务器,以便在互联网上分发内容。HTTP (Hypertext Transfer Protocol,超文本传输协议)提供了大部分这些内容,无论是请求一张猫的图片还是请求加载你正在阅读的教程。Go标准库为创建HTTP服务器以提供web内容或向这些服务器发出HTTP请求…...

Linux笔记---系统信息

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 命令 1. uname - 显示系统信息 2. hostname - 显示或设置系统主机名 3. top - 显示系统资源使用情况 4. df - 显示磁盘空间使用情…...

最新版android stuido加上namespace

每个 Android 模块都有一个命名空间&#xff0c;此命名空间用作其生成的 命名空间由模块的 build.gradle 文件中的 namespace 属性定义&#xff0c;如以下代码段所示。namespace 最初会设为您在创建项目时选择的软件包名称。 Kotlin Groovy android {namespace "com.ex…...

Wireshark基础及捕获技巧

第一章&#xff1a;Wireshark基础及捕获技巧 1.1 Wireshark基础知识回顾 1.2 高级捕获技巧&#xff1a;过滤器和捕获选项 1.3 Wireshark与其他抓包工具的比较 第二章&#xff1a;网络协议分析 2.1 网络协议分析&#xff1a;TCP、UDP、ICMP等 2.2 高级协议分析&#xff1a;HTTP…...

Windows下Navicat15.0连接Oracle11g报ORA-28547解决

目录 背景 一、相关环境 1、操作系统 2、Navicat版本 3、ORACLE连接 4、默认连接 二、问题分析 1、默认dll配置 三、修改配置 1、下载匹配的client 2、替换相应目录 总结 背景 最近在项目中需要使用Oracle数据库&#xff0c;当前很多应用系统的数据都存储在MySQL或者Pos…...

21 Vue3中使用v-for遍历对象数组

概述 使用v-for遍历对象数组在真实的开发中也属于非常常见的用法&#xff0c;需要重点掌握。 因为目前流行的是前后端分离开发&#xff0c;在前后端分离开发中&#xff0c;最常需要处理的就是对象数组类型的数据了。 比如&#xff0c;将员工信息渲染到表格中。 这节课我们就…...

深入理解Java自定义异常与全局异常处理 @RestControllerAdvice

异常主要是包括编译时的异常和运行时的异常。编译时的异常可以通过捕获异常获取&#xff0c;运行时候的异常主要是通过代码规范&#xff0c;或者测试。 Spring Boot提供了两种异常处理方式来统一处理和维护异常信息。 第一种方式是使用RestControllerAdvice注解与ExceptionHand…...

h5页面跳转微信小程序(最简单的方法|URL Scheme)

文章目录 导文实现获取 URL Scheme加密 URL Scheme获取方式拼接参数 明文 URL Scheme获取方式 实际项目展示&#xff1a;频率限制注意事项开放范围示例代码包 导文 H5页面跳转微信小程序的需求是普遍存在的。由于微信小程序是一种只能在微信内部访问的应用程序&#xff0c;而H5…...

智能优化算法应用:基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.非洲秃鹫算法4.实验参数设定5.算法结果6.…...

持续集成交付CICD:Jenkins使用GitLab共享库实现前端项目镜像构建

目录 一、实验 1. GitLab修改项目文件与Harbor环境确认 2.Jenkins使用GitLab共享库实现前端项目镜像构建 3.优化CI流水线封装Harbor账户密码 4.Jenkins再次使用GitLab共享库实现前端项目镜像构建 一、实验 1. GitLab修改项目文件与Harbor环境确认 &#xff08;1&#xf…...

SQL server 数据库 SQL语句高级用法

1、表的高级查询 use student select * from stuinfo1 -- 使用 in 的子查询 select * from stuinfo where stu_age in ( select stu_age from stuinfo where cla_id 12345 ) select * from stuinfo where stu_age in ( 19 , 20 , 21 , 25 , 23 , 1…...

wavlink 路由器 多处前台RCE漏洞复现

0x01 产品简介 WAVLINK是中国睿因科技(WAVLINK)公司开发的一款路由器。 0x02 漏洞概述 WAVLINK路由器mesh.cgi、nightled.cgi、live-api.cgi等接口处存在命令执行漏洞,攻击者可通过该漏洞获取服务器权限。包含型号WN530HG4、WN531G3、WN572HG3、WN535G3、WN575A4等。 0x…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...