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

[Linux]:线程(三)

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. POSIX 信号量

1.1 信号量的概念

为了解决多执行流访问临界区,造成数据不一致等问题,我们除了使用互斥锁外,我们还可以使用一种 POSIX信号量的方法。

当我们运用互斥锁来保护临界资源时,意味着我们把这块临界资源视为一个不可分割的整体,在同一时刻只准许一个执行流对其进行访问。

其实我们也能将这块临界资源进一步划分成多个区域。当多个执行流有访问临界资源的需求时,若让这些执行流同时去访问临界资源的不同区域,此时也并不会引发数据不一致等问题。信号量就是基于此的解决方法。

POSIX信号量本质上是一个计数器,用于衡量临界资源中的资源数目。它对临界资源内部的资源数进行统计,同时操作系统为其提供了一种对临界资源的预定机制。所有执行流在访问临界资源之前,必须先申请信号量。

画板

信号量的 PV 操作:

  • P操作:我们将申请信号量称为P操作,申请信号量的本质就是申请获得临界资源中某块资源的使用权限,当申请成功时临界资源中资源的数目应该减一,因此P操作的本质就是让计数器减一。
  • V操作:我们将释放信号量称为V操作,释放信号量的本质就是归还临界资源中某块资源的使用权限,当释放成功时临界资源中资源的数目就应该加一,因此V操作的本质就是让计数器加一。

并且由于因信号量的 PV 操作同样属于临界资源,所以 PV 操作肯定是原子的。

值得注意的是: 虽然 POSIX信号量和SystemV信号量作用相同,都是用于同步操作,但POSIX信号量常用于线程间同步,而 SystemV 信号量常用于进程间通信。

1.2 信号量的接口

1.2.1 初始化信号量

我们首先需要使用 sem_init初始化信号量,其用法如下:

  1. 函数接口:int sem_init(sem_t *sem, int pshared, unsigned int value);
  2. 参数:
  • sem:需要初始化的信号量。
  • pshared:传入0值表示线程间共享,传入非零值表示进程间共享。
  • value:信号量的初始值(计数器的初始值)。
  1. 返回值:初始化信号量成功返回0,失败返回-1。
1.2.2 销毁信号量

在使用完信号量之后,我们就需要用 sem_destory 对其进行销毁,其用法如下:

  1. 函数接口:int sem_destroy(sem_t *sem);
  2. 参数:
  • sem:需要销毁的信号量。
  1. 返回值:销毁信号量成功返回0,失败返回-1。
1.2.3 申请信号量

申请信号量也就是 P 操作,我们需要使用 sem_wait函数,其用法如下:

  1. 函数接口:int sem_wait(sem_t *sem);
  2. 参数:
  • sem:需要申请的信号量。
  1. 返回值:申请信号量成功返回0,信号量的值减一。申请信号量失败返回-1,信号量的值保持不变。如果信号量为 0,则该执行流会被阻塞,直至信号量大于 0。
1.2.4 释放信号量

释放信号量也就是 V 操作,我们需要使用 sem_post函数,其用法如下:

  1. 函数接口:int sem_post(sem_t *sem);
  2. 参数:
  • sem:需要释放的信号量。
  1. 返回值:释放信号量成功返回0,信号量的值加一。释放信号量失败返回-1,信号量的值保持不变。

如果信号量的初始值为1,那么此时信号量所描述的临界资源只有一份,这个临界资源也只能同时被一个执行流访问。此时信号量的作用基本等价于互斥锁,这种信号量我们称为二元信号量。

比如我们下面可以通过二元信号量实现我们的抢票逻辑:

#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
class Sem
{
public:Sem(int num){sem_init(&_sem, 0, num);}~Sem(){sem_destroy(&_sem);}void P(){sem_wait(&_sem);}void V(){sem_post(&_sem);}private:sem_t _sem;
};
int tickets = 1000;
Sem sem(1);
void *getTickets(void *args)
{uint64_t i = (uint64_t)args;char buffer[64] = {0};snprintf(buffer, sizeof(buffer), "thread %llu", i);while (true){sem.P();if (tickets > 0){usleep(1000);std::cout << buffer << " get a ticket,tickets left: " << --tickets << std::endl;sem.V();}else{sem.V();break;}}std::cout << buffer << " quit ..." << std::endl;return nullptr;
}
int main()
{pthread_t tids[5];for (uint64_t i = 0; i < 5; i++){pthread_create(tids + i, nullptr, getTickets, (void *)i);}for (int i = 0; i < 5; i++){pthread_join(tids[i], nullptr);}return 0;
}

2. 生产者消费者模型

2.1 概念

生产者 - 消费者模型是一种经典的多线程或多进程同步模型。它主要用于解决在数据生产和数据消费速度不一致的情况下,如何安全、高效地处理数据的问题。

在这个模型中,有两类角色:生产者消费者。生产者负责生产数据,例如在一个文件读取系统中,生产者可能是读取文件内容并将其转换为特定格式数据的线程或进程;消费者则负责消费(处理)生产者生产的数据,比如将读取到的数据进行进一步的分析或者存储到数据库中的线程或进程。

画板

利用该模型我们能实现生产者与消费者之间的解耦,并且生产者在生产时,其它生产者可以获取数据,消费者可以处理数据,消费者在消费时也是同理,一定程度上实现了并发。

2.2 特点

生产者-消费者模型一般具有以下三个特点:

  • 三种关系: 生产者和生产者(互斥关系)、消费者和消费者(互斥关系)、生产者和消费者(互斥关系、同步关系)。
  • 两种角色: 生产者和消费者。(通常由进程或线程承担)
  • 一个交易场所: 通常指的是内存中的一段缓冲区。

因为容器是能够被多个执行流访问的一个共享资源,所以生产者与生产者,消费者与消费者,生产者与消费者之间是一个互斥关系,而我们访问数据一定是生产者先生产,消费者再消费,所以生产者与消费者之间是一个同步关系。

3. 生产者消费者模型的实现

3.1 基于阻塞队列实现

阻塞队列就是队列的一种,但其要求:

  • 当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中放入了元素。
  • 当队列满时,往队列里存放元素的操作会被阻塞,直到有元素从队列中取出。

其中阻塞队列最典型的应用场景实际上就是管道的实现。

画板

首先我们可以先实现 BlockingQueue的框架:首先我们需要一个队列 _q 作为成员变量以及表示其容量的 _cap,并且因为涉及多执行流访问,需要一把互斥锁 _mutex,最后我们还需要两个条件变量 _empty与·_full分别表示当我们队列为空时,执行消费的执行流需加入 _empty 条件变量与当我们队列为满时,执行生产的执行流需加入该条件变量 _full

#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
const int defaultnum = 5;
template <class T>
class BlockQueue
{bool IsFull(){return _q.size() == _cap;}bool IsEmpty(){return _q.empty();}
public:BlockQueue(int cap = defaultnum): _cap(cap){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_full, nullptr);pthread_cond_init(&_empty, nullptr);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_full);pthread_cond_destroy(&_empty);}void Push(const T&data);void Pop(T&data);
private:std::queue<T> _q;int _cap;pthread_mutex_t _mutex;pthread_cond_t _full;pthread_cond_t _empty;
};

并且我们实现生产 Push与消费 Pop操作也十分简单,生产时如果队列为满,则加入条件变量 _full 等待,没有则正常生产,生产完毕后该队列一定有数据,这时我们就需要唤醒 _empty条件变量执行消费操作。而消费操作正好对应,如果消费时如果队列为空,则加入条件变量 _empty 等待,否则正常消费,消费完毕后该队列一定不为空,这时我们就需要唤醒 _full条件变量执行生产操作。并且生产与消费操作都属于临界资源,所以需要加锁。

void Push(const T&data)
{pthread_mutex_lock(&_mutex);while(IsFull()){pthread_cond_wait(&_full,&_mutex);}_q.push(data);pthread_mutex_unlock(&_mutex);pthread_cond_signal(&_empty);
}
void Pop(T&data)
{pthread_mutex_lock(&_mutex);while(IsEmpty()){pthread_cond_wait(&_empty,&_mutex);}data=_q.front();_q.pop();pthread_mutex_unlock(&_mutex);pthread_cond_signal(&_full);
}

需要注意的是,<font style="color:rgb(28, 31, 35);">pthread_cond_wait</font> 函数作为让当前执行流进行等待的函数,存在调用失败的可能性,若调用失败,该执行流会继续往后执行。

在多生产者的情形下,当消费者消费了一个数据后,若使用 <font style="color:rgb(28, 31, 35);">pthread_cond_broadcast</font> 函数唤醒多个生产者,此时若阻塞队列仅有一个空位,且唤醒的生产者与消费者竞争,当生产者持续竞争锁成功时,就可能出现错误。鉴于此,为避免上述情况发生,必须让线程被唤醒后再次进行判断,以确认是否真正满足生产消费条件,所以这里要用 <font style="color:rgb(28, 31, 35);">while</font> 进行判断。

最后我们创建多个线程,进行对应的生产与消费操作即可。

#include "BlockQueue.hpp"
#include <cstdlib>
#include <ctime>
void *Producer(void *args)
{pthread_detach(pthread_self());BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);while (true){int data = rand() % 100 + 1;bq->Push(data); std::cout << "Producer: " << data << std::endl;}
}
void *Consumer(void *args)
{pthread_detach(pthread_self());BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);while (true){int data = 0;bq->Pop(data); std::cout << "Consumer: " << data << std::endl;sleep(1);}
}
int main()
{srand((unsigned int)time(nullptr));BlockQueue<int> *bq = new BlockQueue<int>;for (int i = 0; i < 5; i++){pthread_t tid;pthread_create(&tid, nullptr, Producer, bq);}for (int i = 0; i < 5; i++){pthread_t tid;pthread_create(&tid, nullptr, Consumer, bq);}while(true);return 0;
}

3.2 基于循环队列实现

我们同样也可以通过循环队列来实现生产者消费者模型,并且在循环队列不为空或者满的情况下,生产者与消费者可以同步执行。并且要求:

  • 当生产和消费指向同一个资源的时候,只能一个执行流访问。为空的时候,由生产者去访问;为满的时候,由消费者去访问。
  • 消费者不能超过生产者。
  • 生产者不能把消费者套圈,因为这样会导致数据被覆盖。

画板

首先我们可以先实现 RingQueue的框架:首先我们可以使用数组来模仿队列 _q ,以及表示其容量的 _cap,然后用 _p_pos_c_pos分别表示生产者与消费者访问数据的下标,其中我们需要两个信号量 _blank_sem与·_data_sem分别表示队列未填数据的个数与已填数据的个数,并且因为涉及多执行流访问,我们最后要用两把互斥锁 _p_mutex_c_mutex来保护生产与消费的临界资源。

#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <unistd.h>
const int defaultnum = 5;
template <class T>
class RingQueue
{void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}void Lock(pthread_mutex_t *mutex){pthread_mutex_lock(mutex);}void UnLock(pthread_mutex_t *mutex){pthread_mutex_unlock(mutex);}public:RingQueue(int cap = defaultnum): _cap(cap), _p_pos(0), _c_pos(0){_q.resize(_cap);sem_init(&_blank_sem, 0, _cap);sem_init(&_data_sem, 0, 0);pthread_mutex_init(&_p_mutex, nullptr);pthread_mutex_init(&_c_mutex, nullptr);}~RingQueue(){sem_destroy(&_blank_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}void Push(const T &data);void Pop(T &data);
private:std::vector<T> _q;int _cap;int _p_pos;int _c_pos;sem_t _blank_sem;sem_t _data_sem;pthread_mutex_t _p_mutex;pthread_mutex_t _c_mutex;
};

我们实现生产 Push与消费 Pop操作也十分简单,生产时如果队列为满,那么未填数据个数 _blank_sem为 0,该执行流就会被阻塞,没有则正常生产。而消费操作正好对应,如果消费时如果队列为空,那么已填数据个数 _data_sem为 0,该执行流就会被阻塞,否则就正常消费,并且生产与消费操作都属于临界资源,所以需要加锁。

void Push(const T &data)
{P(_blank_sem);Lock(&_p_mutex);_q[_p_pos] = data;_p_pos++;_p_pos %= _cap;UnLock(&_p_mutex);V(_data_sem);
}
void Pop(T &data)
{P(_data_sem);Lock(&_c_mutex);data = _q[_c_pos];_c_pos++;_c_pos %= _cap;UnLock(&_c_mutex);V(_blank_sem);
}

相关文章:

[Linux]:线程(三)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. POSIX 信号量 1.1 信号量的概念 为了解决多执行流访问临界区&#xff0c…...

云原生(四十一) | 阿里云ECS服务器介绍

文章目录 阿里云ECS服务器介绍 一、云计算概述 二、什么是公有云 三、公有云优缺点 1、优点 2、缺点 四、公有云品牌 五、市场占有率 六、阿里云ECS概述 七、阿里云ECS特点 阿里云ECS服务器介绍 一、云计算概述 云计算是一种按使用量付费的模式&#xff0c;这种模式…...

qemu-system-aarch64开启user用户模式网络连接

一、问题 在使用qemu构建arm64的虚拟机时&#xff0c;虚拟机没有网络&#xff0c;桥接方式相对麻烦&#xff0c;我只是需要联网更新即可。与宿主机的通信我使用共享文件夹即可满足要求。 使用指令启动虚拟机时&#xff0c;网络部分的参数为 -net user,hostfwdtcp::10022-:22 …...

Android车载——VehicleHal初始化(Android 11)

1 概述 VehicleHal是AOSP中车辆服务相关的hal层服务。它主要定义了与汽车硬件交互的标准化接口和属性管理&#xff0c;是一个独立的进程。 2 进程启动 VehicleHal相关代码在源码树中的hardware/interfaces/automotive目录下 首先看下Android.bp文件&#xff1a; cc_binary …...

CTFshow 命令执行 web37-web40

目录 web37 方法一&#xff1a;php://input 方法二&#xff1a;data协议 web38 web39 web40 方法一&#xff1a;构造文件读取 方法二&#xff1a;构造数组rce web37 error_reporting(0); if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/flag/i", $c)){incl…...

数据结构与算法篇((原/反/补)码 进制)

目录 讲解一&#xff1a;原/反/补)码 一、原码 二、反码 三、补码 四、有符号位整型 五、无符号位整型 六、Java中的整型 七、整数在底层存储形式 讲解二&#xff1a;进制 一、简介 二、常用的进制 十进制 二进制 八进制 十六进制 知识补充 三、进制转换 1. 二…...

Python画笔案例-077 绘制 颜色饱和度测试

1、绘制 颜色饱和度测试 通过 python 的turtle 库绘制 颜色饱和度测试,如下图: 2、实现代码 绘制 颜色饱和度测试,以下为实现代码: """饱和度渐变示例,本程序需要coloradd模块支持,请在cmd窗口,即命令提示符下输入pip install coloradd进行安装。本程序演…...

简历投递经验01

嵌入式简历制作指南与秋招求职建议 技术要求概览 在嵌入式领域求职时&#xff0c;技术能力是HR和面试官最关注的点之一。以下是一些关键技术点&#xff0c;以及它们在简历中的体现方式。 1. 编程语言与开发环境 掌握C/C语言。熟悉至少一种单片机或微处理器的开发环境。 2.…...

数据和算力共享

数据和算力共享 针对数字化应用实践中需要在不同的物理域和信息域中进行数据的访问交换以及共享计算等需求,本文分析了在数据平台、数据集成系统以及信息交换系统中存在的问题。 在基于联邦学习的基础上,提出一种跨域数据计算共享系统,能够同时共享数据和计算资源,并支持在线…...

SpringBoot 集成 Ehcache 实现本地缓存

目录 1、Ehcache 简介2、Ehcache 集群方式3、工作原理3.1、缓存写入3.2、缓存查找3.3、缓存过期和驱逐3.4、缓存持久化 4、入门案例 —— Ehcache 2.x 版本4.1、单独使用 Ehcache4.1.1、引入依赖4.1.2、配置 Ehcache4.1.2.1、XML 配置方式4.1.2.1.1、新建 ehcache.xml4.1.2.1.2…...

CSP-J 复赛真题 P9749 [CSP-J 2023] 公路

文章目录 前言[CSP-J 2023] 公路题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 示例代码代码解析思考过程总结 总结 前言 在CSP-J 2023的复赛中&#xff0c;出现了一道引人注目的题目——“公路”。这道题目不仅考察了选手们对算法的理解和运用能力&#xff0c…...

MeterSphere压测配置说明

在MeterSphere中&#xff0c;执行性能测试时的配置参数对测试结果有重要影响。以下是对MeterSphere压测配置中几个关键参数的解释&#xff1a; 执行方式&#xff1a;决定了测试的执行模式&#xff0c;例如可以按照持续时间或迭代次数来执行测试。 按持续时间&#xff1a;在这种…...

数据库软题6.1-关系模式-关系模式的各种键

关系模式的各种键 题1-由关系模式求候选键 1. 候选键唯一不冗余 对选项进行闭包运算&#xff0c;如果得到全部属性U&#xff0c;则为候选码 A:AC-ABC-ABCD B:AB-ABC-ABCD C:AE-ABE-ABCE -ABCDE-ABCDEH D:DE2. R的候选码可以从A1,A2,A3,A1A2,A1A3,A2A3,A1A2A3中选择&#xff…...

ulimit:资源限制

一、命令简介 ​ulimit​ 是一个用于资源管理的工具&#xff0c;对于确保系统资源的合理分配和安全使用至关重要。 ‍ 使用场景&#xff1a; 系统管理&#xff1a;限制用户进程使用的资源&#xff0c;防止资源滥用&#xff0c;保证系统稳定。调试&#xff1a;调整核心文件大…...

解决Python使用Selenium 时遇到网页 <body> 划不动的问题

如果在使用 Selenium 时遇到网页的 <body> 划不动的问题&#xff0c;这通常是因为页面的滚动机制&#xff08;例如&#xff0c;可能使用了一个具有固定高度的容器或自定义的滚动条&#xff09;导致无法通过简单的 JavaScript 实现滚动。可以通过以下方法来解决该问题。 …...

pytorch版本和cuda版本不匹配问题

文章目录 &#x1f315;问题&#xff1a;Python11.8安装pytorch11.3失败&#x1f315;CUDA版本和pytorch版本的关系&#x1f315;安装Pytorch2.0.0&#x1f319;pip方法&#x1f319;cuda方法 &#x1f315;问题&#xff1a;Python11.8安装pytorch11.3失败 &#x1f315;CUDA版…...

Vue/组件的生命周期

这篇文章借鉴了coderwhy大佬的Vue生命周期 在Vue实例化或者创建组件的过程中 内部涉及到一系列复杂的阶段 每一个阶段的前后时机都可能对应一个钩子函数 以下是我根据coderwhy大佬文章对于每一个阶段的一些看法 1.过程一 首先实例化Vue或者组件 在实例化之前 会对应一个钩子函…...

【Nacos架构 原理】内核设计之Nacos寻址机制

文章目录 前提设计内部实现单机寻址文件寻址地址服务器寻址 前提 对于集群模式&#xff0c;集群内的每个Nacos成员都需要相互通信。因此这就带来一个问题&#xff0c;该以何种方式去管理集群内部的Nacos成员节点信息&#xff0c;即Nacos内部的寻址机制。 设计 要能够感知到节…...

入门案例:mybatis流程,核心,常见错误

入门案例&#xff1a;mybatis执行流程分析 说明&#xff1a; 1.第一步&#xff1a;是从核心配置文件mybatis-config.xml中构建SqlSessionFactory对象&#xff0c;由于核心配置文件mybatis-config.xml中关联了映射文件UserMapper.xml,所以在SqlSessionFactory中也存在映射文件的…...

C++ | Leetcode C++题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution { public:bool find132pattern(vector<int>& nums) {int n nums.size();vector<int> candidate_i {nums[0]};vector<int> candidate_j {nums[0]};for (int k 1; k < n; k) {auto it_i upper_…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...