【Linux线程(一)】线程初理解

前言:

(一)线程的概念

(二)线程的理解

(三)示例

(四)线程优缺点

线程的优点

 线程的缺点

(五)线程和进程的切换

1.线程的切换

2.进程的切换

(六)线程的控制

1.POSIX 线程库

2.线程的创建

3.线程的查看

4.线程的等待

5.线程的退出

6.线程的取消


前言:

在讲线程之前,我们需要回顾之前的进程:

程序运行后,操作系统创建对应的PCB数据结构,然后生成虚拟地址空间,分配内存资源,相关的代码和数据加载到物理内存中,并通过页表映射关系和虚拟地址空间建立联系。        

 如此一看,创建一个进程,需要创建PCB、开辟地址空间等,操作系统需要做很多工作,所以创建一个进程所需的成本是很高的,而想要在一个程序中使用多执行流,如果创建多个进程的话,那么程序运行的成本就太高了。

我们仔细观察进程的地址空间,这里面的资源都是被一个task_struct的结构体所享有的,页表也是其所独有的;如果我们创建一个“子进程”,但让它不去创建新的地址空间和页表映射,而是和父进程PCB共享地址空间和页表,程序运行时,父进程就可以将部分代码给“子进程”,这样父进程运行的同时,“子进程”也可以运行,而父进程能创建一个这样的“子进程”,也能创建很多个:

我们新创建出来的“子进程”,它们的执行粒度要比“父进程”要细一些,因为“父进程”需要执行全部代码,而“子进程”只需执行部分代码。 正式的讲,我们把这些“子进程”,统称为线程

所以在Linux中,线程在进程“内部”执行,就是线程在进程的地址空间内运行,那么为什么线程要在进程的地址空间中运行呢?首先,任何执行流要执行,都要有资源。而地址空间是进程的资源窗口,但线程有地址空间吗?没有,因为它没有建立和内存的联系,所以线程需要依附于进程

在CPU角度,CPU需要知道要执行的到底是进程还是线程吗?不需要,因为它的核心工作就是执行代码,而进程和线程都是执行流,都能执行代码。

所以我们创建的多个新的task_struct都能被CPU调度,而不像之前只能进程被调度,并且创建线程的成本远比进程低,所以大大提高了CPU的效率。这些能被CPU调度、却没被操作系统分配资源的task_struct就叫做线程!

(一)线程的概念

有了上面的引入,我们重新定义线程和进程。

如何看待今天的进程呢?进程 = 内核数据结构+代码和数据

对于之前的进程来说,它是内核中只有一个执行流的进程;而现在的进程是内核中有多个执行流的进程。所以进程是承担系统资源的基本实体。 

所以我们可以得出

  • 线程是CPU调度的最小单位。
  • 进程是调度和分配的基本单位。

(二)线程的理解

由上面的图,我们可以看出:

每个进程都有自己独立的地址空间,地址空间是指进程可访问的内存范围,包括了数据段、代码段、堆、栈等。

因为进程之间的地址空间都是独立的,所以不同进程之间的数据都是相互隔绝的,一个进程无法直接访问另一个进程的地址空间,进程间通信需要通过特定的机制来完成,比如共享内存、消息队列、信号量等。

而在多线程的情况下,每个线程并不具有独立的地址空间。线程是进程内的执行单元。多个线程共享同一个进程的地址空间,所有线程都可以访问同一个进程的地址空间,共享相同的全局变量和静态变量。

由于线程共享进程的地址空间,因此线程之间可以更方便的进行数据共享和通信。但是也会带来一些潜在的问题,比如线程之间可能会互相干扰,需要通过同步机制来确保线程之间数据访问的正确性。

为什么线程的执行粒度比进程的低?

  1. 资源共享和切换开销较小:线程是在同一个进程内创建的轻量级执行单元,它们共享进程的地址空间和资源。因此,线程之间的切换开销通常比进程之间的切换开销小,这使得线程更适合处理需要频繁切换和共享数据的任务。
  2. 更快的创建和销毁速度:线程的创建和销毁通常比进程快得多,因为线程之间的资源共享较多,创建线程时只需要复制一份线程控制块和栈空间即可。相比之下,进程的创建和销毁需要复制整个地址空间,资源消耗更大。
  3. 更好的并发性和并行性:由于线程共享进程的地址空间,线程之间的通信和同步更加方便快捷。在多核处理器上,多线程程序可以更好地利用多核资源进行并行计算,提高系统的整体性能。
  4. 更灵活的设计和实现:多线程编程相对于多进程编程来说,通常更灵活、更容易实现。线程之间可以直接共享内存,不需要通过进程间通信的机制来进行数据传递和同步,这简化了程序的设计和调试过程。

为什么线程调度的成本比进程的低?

  1. 线程共享资源:线程是进程的子集,多个线程共享同一进程的资源,包括内存空间、文件描述符等。因此,在调度线程时,不需要切换和分配额外的资源,只需切换线程的上下文即可。
  2. 线程切换开销小:线程的切换只需要切换线程的上下文,而进程的切换需要保存和恢复整个进程的状态,包括地址空间、文件描述符等。
  3. 线程调度更灵活:由于线程共享进程的资源,操作系统可以更灵活的调度线程,不需要额外的资源分配和回收操作,提高了调度的效率和性能。

(三)示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>int gcnt = 100;
void *ThreadRoutine(void *arg)
{const char *threadname = (const char *)arg;while (true){std::cout << "I am a new thread,pid:" << getpid() << " gcnt:" << gcnt << " &gcnt:" << &gcnt << std::endl;gcnt--;sleep(1);}
}int main()
{// 创建线程之前,已经有进程了pthread_t tid1;pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");while (true){std::cout << "I am main thread,pid:" << getpid() << ",gcnt:" << gcnt << " &gcnt:" << &gcnt << std::endl;sleep(1);}return 0;
}

运行程序:

 从上面我们可以看出来,主进程和线程两个执行流它们的pid是相同的,所调取的全局变量gcnt的物理地址、大小也是相同的。

由此可以看出,线程和进程或者说线程和线程之间并不是父子进程的关系,它们所用的地址空间也是同一个。

使用下面的命令我们可以查出当前正在运行的线程/进程的信息

while :; do ps -aL | head -1 && ps -aL | grep testThread ; sleep 1;done

 

可以看到,运行程序后两个线程的PID是相同的,但是LWP的值是不同的,那么LWP是什么呢?

 LWP(LightWeight Process,轻量级进程):在Linux系统中,线程被实现为轻量级进程,每个线程都有自己的线程控制块(TCB),用于保存线程的上下文信息。LWP之间可以共享进程的资源,如地址空间、文件描述符等,同时可以独立地进行调度和执行。

在这里的LWP指的是线程的编号

(四)线程优缺点

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小的多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作

 线程的缺点

  • 性能损失

一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

  • 健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因为共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

  • 缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

  • 编程难度提高

编写与调试一个多线程程序比单线程程序困难的多。

 那为什么一个线程引发的错误要让整个进程来承担呢?

  1. 共享地址空间:在POSIX线程中,同一进程内的所有线程共享相同的地址空间,这意味着它们可以访问相同的内存区域,包括全局变量、堆和栈。因此,如果一个线程在共享内存区域中写入了错误的数据或者越界访问量某个数据结构,这个错误将会影响到其他进程以及进程的运行状态。
  2. 信号和异常处理:在Linux中,当一个线程引发了一个信号或者异常,操作系统会将这个信号或异常发送给整个进程,而不是仅仅发送给产生错误的线程。这是因为在POSIX线程中,默认情况下所有线程共享相同的信号处理器设置。因此,如果一个线程引发了一个信号,整个进程都会接收到该信号,这可能导致整个进程的状态被改变或者终止。
  3. 资源共享:进程中的所有线程共享各种资源,包括打开的文件、网络连接、进程ID等。如果一个线程错误的关闭了一个文件描述符,或者引发了其他类似的资源管理错误,这会影响到整个进程的其他线程。
  4. 取消线程:在POSIX线程中,可以使用pthread_cancel()函数来取消一个线程。如果一个线程被取消,那么整个进程的其他线程也可能会受到影响,具体取决于取消动作的方式和取消点的设置。

(五)线程和进程的切换

1.线程的切换

线程的切换是指从一个线程切换到另一个线程执行的过程。在多线程程序中,操作系统负责管理线程的调度和切换,确保多个线程能够合理地共享CPU资源。

线程的切换通常发生在以下几种情况下:

  1. 时间片耗尽:操作系统会为每个线程分配一定的时间片(即CPU时间),当线程执行的时间片耗尽时,操作系统会进行线程切换,将CPU时间分配给其他线程执行。这样可以保证每个线程都有机会执行。
  2. 阻塞或等待:当一个进程因为等待某些事件发生而被阻塞时,操作系统会进程线程切换,将CPU事件分配给其他可执行的线程。当等待的事件发生后,被阻塞的线程会被唤醒,重新进入就绪状态,等待再次被调度执行。
  3. 线程调用了阻塞系统调用:某些系统调用(例如I/O操作)会导致线程进入阻塞状态,等待系统完成相应的操作。在这种情况下,操作系统会进行线程切换,执行其他可执行的线程,直到系统调用完成并唤醒线程。
  4. 显示调用线程调度函数:在某些情况下,程序员可以显式地调用线程调度函数,例如 pthread_yield() 函数,来请求操作系统进行线程切换。

线程切换的具体实现由操作系统负责,通常包括以下步骤:

  • 保存上下文:保存当前正在执行线程的上下文信息,包括程序计数器、寄存器状态、栈指针等。

  • 选择下一个线程:根据调度算法选择下一个要执行的线程,可能会根据线程的优先级、调度策略等进行选择。

  • 恢复上下文:恢复被选中线程的上下文信息,并将控制权转移到该线程的执行代码处

         线程切换是操作系统中一项重要的工作,其效率和性能对系统的整体性能有很大影响。因此,操作系统的线程调度算法和线程切换的实现通常会进行优化,以提高系统的性能和响应能力。

2.进程的切换

进程的切换是指从一个进程切换到另一个进程执行的过程。

进程切换通常涉及更多的开销和复杂性,因为不同进程之间的地址空间是独立的,需要进行上下文的完全切换。

进程的切换通常发生在以下几种情况下:

  1. 时间片耗尽:每个进程被分配一个时间片来执行,当时间片耗尽时,操作系统会强制进行进程切换,将 CPU 时间分配给其他就绪态的进程。这样可以保证每个进程都有机会执行,并避免了某个进程长时间占用 CPU 而导致其他进程无法执行的情况。

  2. 阻塞或等待:当一个进程需要等待某些事件发生(如 I/O 操作完成、信号等)而被阻塞时,操作系统会进行进程切换,将 CPU 时间分配给其他就绪态的进程。当等待的事件发生后,被阻塞的进程会被唤醒,重新进入就绪态,等待再次被调度执行。

  3. 进程调用了阻塞系统调用:某些系统调用(例如等待 I/O 完成的系统调用)会导致进程进入阻塞状态,等待系统完成相应的操作。在这种情况下,操作系统会进行进程切换,执行其他可执行的进程,直到系统调用完成并唤醒进程。

  4. 优先级调度:如果有更高优先级的进程需要执行,操作系统可能会进行进程切换,将 CPU 时间分配给优先级更高的进程,以提高系统对高优先级任务的响应速度。

  5. 抢占式调度:在支持抢占式调度的系统中,高优先级进程可以在任何时候抢占低优先级进程的 CPU 时间,导致进程切换。这样可以确保高优先级任务的及时执行。

以下是进程切换的一般过程:

  1. 保存上下文:操作系统会保存当前进程的所有寄存器状态、程序计数器和其他必要的执行环境信息,以便稍后恢复。

  2. 选择下一个进程:操作系统根据调度算法选择下一个要执行的进程。调度算法可能基于进程的优先级、调度策略等进行选择。

  3. 保存进程状态:操作系统会保存下一个进程的状态信息。这包括进程的寄存器状态、程序计数器以及其他与进程执行相关的状态。

  4. 切换地址空间:由于不同进程具有不同的地址空间,因此在切换进程时,操作系统需要将当前的地址空间切换为下一个进程的地址空间。这通常涉及修改内存管理单元(MMU)或处理器的地址转换表。

  5. 恢复上下文:操作系统会恢复被选中进程的上下文信息,并将控制权转移到该进程的执行代码处。

进程切换通常比线程切换开销更大,因为进程拥有独立的地址空间,需要额外的资源来管理和维护。此外,进程切换还可能涉及到内核态和用户态之间的切换,因此开销更大。然而,与线程切换相比,进程切换的优点在于进程之间的隔离性更好,更不容易相互影响。

(六)线程的控制

1.POSIX 线程库

POSIX线程库( pthreads)是一种标准的线程库,定义了一组用于创建和管理线程的API。POSIX是Portable Operating System Interface的缩写,是一个IEEE标准,定义了操作系统接口的标准,包括线程、进程、信号、文件系统等。

2.线程的创建

       #include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

作用:

用于创建一个进程。

参数:

  • pthread_t *thread:一个指向pthread_t 类型的指针,用来存储新创建线程的标识符。
  • const pthread_attr_t *attr:一个指向pthread_attr_t类型的指针,用于指定新线程的属性。通常可以传入NULL,表示使用默认属性。
  • void *(*start_routine)(void*):一个函数指针,指向新线程将要执行的函数。函数的返回值和参数必须都是void *类型。
  • void *arg:传递给start_routine函数的参数。

返回值: 

函数执行成功,将会创建一个新的线程,并将其标识符存储在thread指针指向的内存当中,返回0;如果执行失败,将返回错误编号。

3.线程的查看

查看线程既可以在Linux终端中输入ps命令进程查看,也可以在程序中调用系统函数pthread_self()来查看。

       #include <pthread.h>pthread_t pthread_self(void);

 作用:

在线程中获取自身的线程ID,以便于进程一些线程相关的操作,比如打印线程信息、线程同步等。

返回值:

返回线程自身的线程ID。

4.线程的等待

在线程编程中,有时候需要等待一个线程完成其任务,然后才能继续执行其他操作。在 POSIX 线程库中,可以使用 pthread_join() 函数来等待一个线程结束。

       #include <pthread.h>int pthread_join(pthread_t thread, void **retval);

作用:

调用pthread_join函数将会阻塞当前进程,直到指定的线程结束为止。当目标线程结束时,当前线程会继续执行,并且获取目标线程的返回值。

参数:

  • pthread_t thread:要等待的进程ID。
  • void **retval:用于存储目标进程的返回值。如果不关心目标线程返回值,可以传入NULL

返回值: 

  • 如果pthread_join函数成功返回,即等待目标线程结束,并成功获取了目标线程的返回值,返回值为0。
  • 如果pthread_join函数返回一个非零值,表示等待目标线程结束时出现了错误,此时可以根据返回值判断具体的错误类型。

5.线程的退出

在进程当中,我们学习了用exit来结束一个进程,而对于线程来说也同样适用,只不过对线程使用exit函数会导致整个进程一起退出。

另外,还可以利用return来让线程退出。

我们还可以先程序中调用pthread_exit()函数来让单个线程退出,它不会让整个进程一起退出。

       #include <pthread.h>void pthread_exit(void *retval);

作用:

在某个线程中调用该函数,可以终止当前线程的执行,并返回指定的值。

参数:

  • void *retval:可选参数,表示线程的退出状态,类型为void*。该参数可以用于向调用pthread_join函数的线程传递一个退出状态值。如果不需要向其他线程传递退出状态,可以将retval设为NULL。

6.线程的取消

线程的取消是指在某个线程执行过程中,另一个线程主动终止目标线程的执行。POSIX线程库提供了pthread_cancel()函数来取消一个线程的执行。

       #include <pthread.h>int pthread_cancel(pthread_t thread);

 作用:

取消目标线程的执行。

参数:

  • pthread_t thread:thread_t类型,表示要取消执行的进程ID。

返回值:

如果成功发送了取消请求给目标进程,返回0;

如果发送请求时出现了错误,返回非零值。

以上就是对线程的初理解啦,写了7K字,觉得写的不错的小伙伴可以点个赞~

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

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

相关文章

【Docker学习】重启容器的docker restart

命令&#xff1a; docker container restart 描述&#xff1a; 重启一个或多个容器 用法&#xff1a; docker container restart [OPTIONS] CONTAINER [CONTAINER...] 别名&#xff1a; docker restart(docker的一些命令可以简写&#xff0c;docker restart就等同于docker cont…

对Windows超融合S2D的一些补充

先说一个不知道算不算BUG的例子&#xff0c;下面这个存储池是用两台服务器各2块10G建立的&#xff0c;除去系统保留的部分&#xff0c;显示还有13G可用。 但如果使用其新建虚拟磁盘会显示可用的空间为0 然后我又各增加了一块10G硬盘进池&#xff0c;变成了可用空间为30.5GB …

JavaEE之线程(4)——线程安全、线程安全的原因,synchronized关键字

前言 在本栏的前面的内容中&#xff0c;我们介绍了线程的创建、Thread 类及常见方法、线程的状态&#xff0c;今天我们来介绍一下关于线程的另一个重点知识——线程安全。 一、线程安全 基本概念&#xff1a; 线程安全的确切定义是复杂的&#xff0c;但我们可以这样认为&…

哪里可以找到可靠的代理IP?

需要代理来访问受限制的网站或改善您的在线隐私&#xff1f;别再犹豫了&#xff01;在这篇博文中&#xff0c;我们将探讨您可以使用的选项&#xff0c;并提供有关在哪里获取代理的指导。 首先&#xff0c;让我们了解什么是代理及其工作原理。代理充当您的设备和互联网之间的中介…

《Mybatis》系列文章目录

什么是 MyBatis&#xff1f; MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff…

【触想智能】无风扇工控一体机的优点与定制要求分析

随着工业自动化的不断推进&#xff0c;工控一体机作为自动化生产的核心设备&#xff0c;在工业生产中发挥着越来越重要的作用。 在工控一体机的设计中&#xff0c;散热是一个非常关键的问题&#xff0c;而无风扇工控一体机的出现为解决这个问题提供了新方法。 无风扇工控一体机…

Rx(Reactive Extensions)的由来

既然我们已经介绍了响应式编程&#xff0c;现在是时候了解我们的明星了:响应式扩展&#xff0c;通常简称为Rx。微软开发了Reactive扩展库&#xff0c;使其易于处理事件流和数据流。在某种程度上&#xff0c;时变值本身就是一个事件流;每个值更改都是一种类型的事件它会更新依赖…

使用Docker安装Nginx

一、Nginx介绍 Nginx 是一款高性能的开源 Web 服务器和反向代理服务器&#xff0c;具有高效能、高稳定性、低资源消耗等优点。可以处理大量并发请求&#xff0c;支持多种协议&#xff0c;还能实现负载均衡、缓存等功能&#xff0c;在互联网应用中被广泛使用。在Nginx中&#xf…

【数据可视化01】matplotlib实例介绍1

目录 一、引言二、实例介绍1.柱状图1)简单柱状图2)堆叠柱状图 2.线条形式3.折线图&#xff08;多子图&#xff09;4.散点图5.水平和垂直线条6.饼状图1&#xff09;饼状图2&#xff09;“条形饼”图 一、引言 matplotlib是一个用于绘制数据可视化的Python库。它可以创建各种静态…

虚拟资源在线交易服务平台源码 线上虚拟商品交易平台搭建

在信息爆炸的时代&#xff0c;虚拟资源、素材、源码系统等等以其独特的魅力&#xff0c;逐渐成为人们日常生活和工作中不可或缺的一部分。如何高效地获取、管理和交易这些虚拟资源&#xff0c;分享一款虚拟资源在线交易服务平台源码&#xff0c;轻松搭建线上虚拟商品交易平台&a…

C++之Eigen库基本使用(下)

1、常见变换 Eigen::Matrix3d //旋转矩阵&#xff08;3*3&#xff09; Eigen::AngleAxisd //旋转向量&#xff08;3*1&#xff09; Eigen::Vector3d //欧拉角&#xff08;3*1&#xff09; Eigen::Quaterniond //四元数&#xff08;4*1&#xff09; Eigen::Isom…

Docker运行出现iptables: No chain/target/match by that name报错如何解决?

在尝试重启 Docker 容器时遇到的错误信息表明有关 iptables 的配置出了问题。这通常是因为 Docker 需要配置网络&#xff0c;而 iptables 规则没有正确设置或被意外删除。具体到你的错误信息中&#xff0c;报错 iptables: No chain/target/match by that name 表示 Docker 尝试…

深入理解与应用C++ Vector

1. C Vector 简介与基本使用 C 的 vector 是一个序列容器&#xff0c;用于表示可变大小的数组。它结合了数组的高效元素访问和动态大小调整的灵活性。与静态数组相比&#xff0c;vector 的大小可以根据需要自动调整&#xff0c;这是通过在底层使用动态数组来实现的。当新元素被…

【Day2:JAVA变量、数据类型、运算符的认识】

目录 1、变量的介绍、定义、使用2、标识符2.1 概念2.2 标识符的命名规则2.3 标识符的命名规范 3、数据类型3.1 基本数据类型3.2 引用数据类型 4、Scanner键盘录入4.1 键盘录入效果介绍4.2 键盘录入的三个步骤 5、运算符5.1 运算符和表达式5.2 算数运算符5.3 自增自减运算符5.4 …

【教学类-55-02】20240512图层顺序挑战(四格长条纸加黑色边框、4*4、7张 、43200张去掉非7色有23040张,去掉重复样式有几种?)

作品展示 背景需求&#xff1a; 之前的代码吗存在几个问题&#xff0c;最大的问题是不能生成“”长条黑边框”” 【教学类-55-01】20240511图层顺序挑战&#xff08;四格长条纸&#xff09;&#xff08;4*4&#xff09;和“手工纸自制参考图”-CSDN博客文章浏览阅读485次&…

使用Navicat将MySql数据库导入和导出

一&#xff0c;导出数据表 1.使用Navicat打开数据库&#xff0c;右键数据库&#xff0c;点击转储SQL文件&#xff0c;点击结构和数据。 2.选择生成文件的地方 3.等待生成完成 4.生成完成 二&#xff0c;导入数据库表和数据SQL文件 1.新建一个数据库 2.右键选择运行SQl文件 记…

如何利用甘特图来提高资源的是使用效率?

在项目管理中&#xff0c;甘特图是一种常用的工具&#xff0c;用于规划和跟踪项目进度。它通过条形图的形式展示项目的时间表和任务依赖关系&#xff0c;帮助项目经理和团队成员清晰地了解项目的时间线和进度。通过合理利用甘特图&#xff0c;可以显著提高资源的使用效率&#…

一文入门DNS

概述 DNS是一个缩写&#xff0c;可以代表Domain Name System&#xff0c;域名系统&#xff0c;是互联网的一项基础服务。也可以代表Domain Name Server&#xff0c;域名服务器&#xff0c;是进行域名和与之相对应的IP地址相互转换的服务器。DNS协议则是用来将域名转换为IP地址…

1W、2W 3KVAC隔离 宽电压输入 交直两用AC/DC 电源模块 ——TP01(02)AZ 系列

TP01(02)AZ为客户提供一款超小体积模块式开关电源&#xff0c;该系列模块电源输出功率为1W、2W&#xff0c;具有极低的空载损耗&#xff0c;低漏电流仅0.1mA&#xff0c;小体积&#xff0c;隔离耐压高达3KV等特点。产品安全可靠&#xff0c;EMC 性能好&#xff0c;EMC 及安全规…

树莓派配置双网卡分别为AD HOC和AP模式

树莓派配置双网卡分别为AD HOC和AP模式 需求说明&#xff1a;为了实现分级网络管理&#xff0c;将多个无人机分簇&#xff0c;簇间使用AD HOC进行无中心自组织的网络&#xff0c;簇内使用AP-AC模式进行中心化网络。因此&#xff0c;需要配置一台设备&#xff0c;同时完成AD HOC…