Linux管道和FIFO应用笔记

strongerHuang 2023-03-07 08:24
关注+星标公众,不错过精彩内容
来源 | TLPI系统编程笔记

整理&排版 | 嵌入式应用研究院

概述

管道最常见的地方是shell中,比如:

$ ls | wc -l

为了执行上面的命令,shell创建了两个进程来分别执行 lswc (通过 fork()exec() 完成),如下:

从上图可以看出,可以将管道看成是一组水管,它允许数据从一个进程流向另一个进程,这也是管道名称的由来。

从上图可以看出,由两个进程连接到了管道上,这样写入进程 ls 就将其标准输出(文件描述符为1)连接到来管道的写入段,读取进程 wc 就将其标准输入(文件描述符为0)连接到管道的读取端。实际上,这两个进程并不知道管道的存在,它们只是从标准文件描述符中读取和写入数据。shell 必须要完成相关的工作。

一个管道是一个字节流

管道是一个字节流,即在使用管道时是不存在消息或者消息边界的概念的:

  • 从管道中读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是什么

  • 通过管道传递的数据是顺序的,从管道中读取出来的字节的顺序与它们被写入管道的顺序是完全一样的,在管道中无法使用 lseek() 来随机的访问数据

如果需要在管道中实现离散消息的概念,那么就必须要在应用程序中完成这些工作。虽然这是可行的,但如果碰到这种需求的话最好使用其他 IPC 机制,如消息队列和数据报 socket。

从管道中读取数据

试图从一个当前为空的管道中读取数据将会被阻塞直至至少有一个字节被写入到管道中为止。

如果管道的写入端被关闭了,那么从管道中读取数据的进程在读完管道中剩余的所有数据之后将会看到文件结束(即 read() 返回 0)。

管道是单向的

在管道中数据的传递方向是单向的。管道的一端用于写入,另一端则用于读取。

在其他一些 UNIX 实现上,特别是那些从 System V Release 4 演化而来的系统,管道是双向的(所谓的流管道)。双向管道并没有在任何 UNIX 标准中进行规定,因此即使在提供了双向管道的实现上最好也避免依赖这种语义。作为替代方案,可以使用 UNIX domain 流 socket 对(通过 socketpair() 系统调用来创建),它提供了一种标准的双向通信机制,并且其语义与流管道是等价的。

可以确保写入不超过 PIPE_BUF 字节的操作是原子的

如果多个进程写入同一个管道,那么如果它们在一个时刻写入的数据量不超过 PIPE_BUF 字节,那么就可以确保写入的数据不会发生相互混合的情况。

SUSv3 要求 PIPE_BUF 至少为 _POSIX_PIPE_BUF(512)。一个实现应该定义 PIPE_BUF(在 中)并/或允许调用 fpathconf(fd,_PC_PIPE_BUF) 来返回原子写入操作的实际上限。不同 UNIX 实现上的 PIPE_BUF 不同,如在 FreeBSD 6.0 其值为 512 字节,在 Tru64 5.1 上其值为 4096 字节,在 Solaris 8 上其值为 5120 字节。在 Linux 上,PIPE_BUF 的值为 4096。

  • 当写入管道的数据块的大小超过了 PIPE_BUF 字节,那么内核可能会将数据分割成几个较小的片段来传输,在读者从管道中消耗数据时再附加上后继的数据(write()调用会阻塞直到所有数据被写入到管道为止)

  • 当只有一个进程向管道写入数据时(通常的情况),PIPE_BUF 的取值就没有关系了

  • 但如果有多个写入进程,那么大数据块的写入可能会被分解成任意大小的段(可能会小于 PIPE_BUF 字节),并且可能会出现与其他进程写入的数据交叉的现象

只有在数据被传输到管道的时候 PIPE_BUF 限制才会起作用。当写入的数据达到 PIPE_BUF 字节时,write() 会在必要的时候阻塞知道管道中的可用空间足以原子的完成此操作。如果写入的数据大于 PIPE_BUF 字节,那么 write() 会尽可能的多传输数据以充满整个管道,然后阻塞直到一些读取进程从管道中移除了数据。如果此类阻塞的 write() 被一个信号处理器中断了,那么这个调用会被解除阻塞并返回成功传输到管道中的字节数,这个字节数会少于请求写入的字节数(所谓的部分写入)。

管道的容量是有限的

管道其实是一个在内核内存中维护的缓冲器,这个缓冲器的存储能力是有限的。一旦管道被填满之后,后继向管道的写入操作就会被阻塞直到读者从管道中移除了一些数据为止。

SUSv3 并没有规定管道的存储能力。在早于 2.6.11 的 Linux 内核中,管道的存储能力与系统页面的大小是一致的(如在 x86-32 上是 4096 字节),而从 Linux 2.6.11 起,管道的存储能力是 65,536 字节。其他 UNIX 实现上的管道的存储能力可能是不同的。

一般来讲,一个应用程序无需知道管道的实际存储能力。如果需要防止写者进程阻塞,那么从管道中读取数据的进程应该被设计成以尽可能快的速度从管道中读取数据。

创建和使用管道

#include 

int pipe(int fd[2]);    
  • pipe() 创建一个新管道

  • 成功的调用在数组 fd 中返回两个打开的文件描述符,一个表示管道的读取端 fd[0],一个表示管道的写入端 fd[1]

调用 pipe() 函数时,首先在内核中开辟一块缓冲区用于通信,它有一个读端和一个写端,然后通过 fd 参数传出给用户进程两个文件描述符,fd[0] 指向管道的读端,fd[1] 指向管道的写段。

不要用 fd[0] 写数据,也不要用 fd[1] 读数据,其行为未定义的,但在有些系统上可能会返回 -1 表示调用失败。数据只能从 fd[0] 中读取,数据也只能写入到fd[1],不能倒过来。

与所有文件描述符一样,可以使用 read() 和  write() 系统调用来在管道上执行 IO,一旦向管道的写入端写入数据之后立即就能从管道的读取端读取数据。管道上的 read()  调用会读取的数据量为所请求的字节数与管道中当前存在的字节数两者之间的较小值。当管道为空时,读取操作阻塞。

也可以在管道上使用 stdio 函数(printf()scanf() 等),只需要首先使用 fdopen() 获取一个与 filedes 中的某个描述符对应的文件流即可。但在这样做的时候需要解决 stdio 缓冲问题。

管道可以用于进程内部自己通信:

管道可以用于亲缘关系(子进程会继承父进程中的文件描述符的副本)进程中通信:

不建议将单个 pipe 用作全双工的,或者不关闭用作半双工而不关闭相应的读端/写端,这样很可能导致死锁:如果两个进程同时试图从管道中读取数据,那么就无法确定哪个进程会首先读取成功,从而产生两个进程竞争数据了。要防止这种竞争情况的出现就需要使用某种同步机制。这时,就需要考虑死锁问题了,因为如果两个进程都试图从空管道中读取数据或者尝试向已满的管道中写入数据就可能会发生死锁。

如果我们想要一个双向数据流时,可以创建两个管道,每个方向一个。

管道允许相关进程间的通信

其实管道可以用于任意两个甚至更多相关进程之间的通信,只要在创建子进程的系列 fork() 调用之前通过一个共同的祖先进程创建管道即可。

关闭未使用管道文件描述符

关闭未使用管道文件描述符不仅仅是为了确保进程不会消耗尽其文件描述符的限制。

从管道中读取数据的进程会关闭其持有的管道的写入描述符,这样当其他进程完成输出并关闭其写入描述符之后,读者就能够看到文件结束。反之,如果读取的进程没有关闭管道的写入端,那么在其他进程关闭了写入描述符之后,即使读者已经读完了管道中的所有数据,也不会看到文件结束。因为此时内核知道至少还有一个管道的写入描述符打开着,从而导致 read() 阻塞。

当一个进程视图向一个管道中写入数据但没有任何进程拥有该管道的打开着的读取描述符时,内核会向写入进程发送一个 SIGPIPE 信号,默认情况下,这个信号将会杀死进程,但进程可以选择忽略或者设置信号处理器,这样 write() 将因为 EPIPE 错误而失败。收到 SIGPIPE 信号和得到 EPIPE  错误对于标识管道的状态是有意义的,这就是为什么需要关闭管道的未使用读取描述符的原因。如果写入进程没有关闭管道的读取端,那么即使在其他进程已经关闭了管道的读取端之后,写入进程仍然能够向管道写入数据,最后写入进程会将数据充满整个管道,后续的写入请求会将永远阻塞。

使用管道连接过滤器

当管道被创建之后,为管道的两端分配的文件描述符是可用描述符中数值最小的两个,由于通常情况下,进程已经使用了描述符 0,1,2,因此会为管道分配一些数值更大的描述符。如果需要使用管道连接两个过滤器(即从 stdin 读取和写入到 stdout),使得一个程序的标准输出被重定向到管道中,就需要采用复制文件描述符技术。

int pfd[2];
pipe(pfd);

close(STDOUT_FILENO);
dup2(pfd[1],STDOUT_FILENO);

上面这些调用的最终结果是进程的标准输出被绑定到管道的写入端,而对应的一组调用可以用来将进程的标准的输入绑定到管道的读取端上。

通过管道与 shell 命令进行通信: popen()

#include 

FILE *popen (const char *command, const char *mode);
  • pipe()close() 是最底层的系统调用,它的进一步封装是 popen()pclose()

  • popen()函数创建了一个管道,然后创建了一个子进程来执行 shell,而 shell 又创建了一个子进程来执行command字符串

  • mode 参数是一个字符串:

    • 它确定调用进程是从管道中读取数据(moder)还是将数据写入到管道中(modew

    • 由于管道是向的,因此无法在执行的 command 中进行双向通信

    • mode  的取值确定了所执行的命令的标准输出是连接到管道的写入端还是将其标准输入连接到管道的读取端

  • popen() 在成功时会返回可供 stdio 库函数使用的文件流指针。当发生错误时,popen() 会返回 NULL 并设置 errno 以标示出发生错误的原因

  • popen() 调用之后,调用进程使用管道来读取 command 的输出或使用管道向其发送输入。与使用 pipe() 创建的管道一样,当从管道中读取数据时,调用进程在 command 关闭管道的写入端之后会看到文件结束;当向管道写入数据时,如果 command 已经关闭了管道的读取端,那么调用进程就会收到 SIGPIPE 信号并得到 EPIPE 错误

#include 

int pclose ( FILE * stream);
  • 一旦IO结束之后可以使用  pclose() 函数关闭管道并等待子进程中的 shell 终止(不应该使用 fclose() 函数,因为它不会等待子进程。)

  • pclose() 在成功时会返回子进程中 shell 的终止状态(即 shell 所执行的最后一条命令的终止状态,除非 shell 是被信号杀死的)

  • system() 一样,如果无法执行shell,那么 pclose() 会返回一个值就像子进程中的 shell 通过调用 _exit(127) 来终止一样

  • 如果发生了其他错误,那么 pclose() 返回 −1。其中可能发生的一个错误是无法取得终止状态

当执行等待以获取子进程中 shell 的状态时,SUSv3 要求 pclose()system() 一样,即在内部的 waitpid() 调用被一个信号处理器中断之后自动重启该调用。

system() 一样,在特权进程中永远都不应该使用 popen()

popen优缺点:

  • 优点:在 Linux 中所有的参数扩展都是由 shell 来完成的。所以在启动 command 命令之前程序先启动 shell 来分析 command 字符串,就可以使用各种 shell 扩展(比如通配符),这样我们可以通过 popen() 调用非常复杂的 shell 命令

  • 缺点:对于每个 popen() 调用,不仅要启动一个被请求的程序,还需要启动一个 shell。即每一个 popen() 将启动两个进程。从效率和资源的角度看,popen() 函数的调用比正常方式要慢一些

pipe() VS popen()

  • pipe()是一个底层调用,popen() 是一个高级的函数

  • pipe() 单纯的创建管道,而 popen() 创建管道的同时 fork() 子进程

  • popen() 在两个进程中传递数据时需要调用 shell 来解释请求命令;pipe() 在两个进程中传递数据不需要启动 shell 来解释请求命令,同时提供了对读写数据的更多控制(popen() 必须时 shell 命令,pipe() 则无硬性要求)

  • popen() 函数是基于文件流(FILE)工作的,而 pipe() 是基于文件描述符工作的,所以在使用 pipe() 后,数据必须要用底层的read()write() 调用来读取和发送

管道和 stdio 缓冲

由于 popen() 调用返回的文件流指针没有引用一个终端,因此 stdio 库会对这种流应用块缓冲。这意味着当 mode 的值为 w 来调用 popen() 时,默认情况下只有当 stdio 缓冲区被充满或者使用 pclose() 关闭了管道之后才会被发送到管道的另一端的子进程。在很多情况下,这种处理方式是不存在问题的。但如果需要确保子进程能够立即从管道中接收数据,那么就需要定期调用 fflush() 或使用 setbuf(fp, NULL) 调用禁用  stdio  缓冲。当使用 pipe() 系统调用创建管道,然后使用 fdopen() 获取一个与管道的写入端对应的 stdio 流时也可以使用这项技术

如果调用 popen() 的进程正在从管道中读取数据(即 moder),那么事情就不是那么简单了。在这样情况下如果子进程正在使用 stdio 库,那么——除非它显式地调用了 fflush()setbuf() ,其输出只有在子进程填满 stdio 缓冲器或调用了 fclose() 之后才会对调用进程可用。(如果正在从使用 pipe() 创建的管道中读取数据并且向另一端写入数据的进程正在使用 stdio 库,那么同样的规则也是适用的。)如果这是一个问题,那么能采取的措施就比较有限的,除非能够修改在子进程中运行的程序的源代码使之包含对 setbuf() 或  fflush()  调用。

如果无法修改源代码,那么可以使用伪终端来替换管道。一个伪终端是一个 IPC 通道,对进程来讲它就像是一个终端。其结果是 stdio 库会逐行输出缓冲器中的数据。

命名管道(FIFO)

上述管道虽然实现了进程间通信,但是它具有一定的局限性:

  • 匿名管道只能是具有血缘关系的进程之间通信

  • 它只能实现一个进程写另一个进程读,而如果需要两者同时进行时,就得重新打开一个管道

为了使任意两个进程之间能够通信,就提出了命名管道(named pipe 或 FIFO):

  • FIFO 与管道的区别:FIFO 在文件系统中拥有一个名称,并且其打开方式与打开一个普通文件一样,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信

  • 一旦打开了 FIFO,就能在它上面使用与操作管道和其他文件的系统调用一样的 IO 系统调用 read()write()close()。与管道一样,FIFO 也有一个写入端和读取端,并且总是遵循先进先出的原则,即第一个进来的数据会第一个被读走

  • 与管道一样,当所有引用  FIFO 的描述符都关闭之后,所有未被读取的数据都将被丢弃

  • 使用 mkfifo 命令可以在 shell 中创建一个 FIFO:

mkfifo [-m mode] pathname
  • pathname 是创建的 FIFO 的名称,-m 选项指定权限 mode,其工作方式与 chmod 命令一样

  • fstat()stat() 函数会在 stat  结构的 st_mode 字段返回 S_IFIFO,使用 ls -l 列出文件时,FIFO 文件在第一列的类型为 pls -F 会在 FIFO 路径名后面附加管道符 |

#include 
#include

int mkfifo(const char *pathname,mode_t mode);
  • mode 参数指定了新 FIFO 的权限,这些权限会按照进程的  umask 值来取掩码

  • 一旦创建了 FIFO,任何进程都能够打开它,只要它通过常规的文件权限检测

  • 使用 FIFO 时唯一明智的做法是在两端分别设置一个读取进程和一个写入进程。这样在默认情况下,打开一个 FIFO 以便读取数据(open() O_RDONLY 标记)将会阻塞直到另一个进程打开 FIFO 以写入数(open() O_WRONLY 标记)为止。相应地,打开一个 FIFO 以写入数据将会阻塞直到另一个进程打开 FIFO 以读取数据为止。换句话说,打开一个 FIFO 会同步读取进程和写入进程。如果一个 FIFO 的另一端已经打开(可能是因为一对进程已经打开了 FIFO 的两端),那么open() 调用会立即成功。

在大多数 Unix 实现上(包含 Linux),当打开一个 FIFO 时可以通过指定 O_RDWR 标记来绕过打开 FIFO 时的阻塞行为。这样,open() 会立即返回,但无法使用返回的文件描述符在 FIFO 上读取和写入数据。这种做法破坏了 FIFO 的 IO 模型,SUSv3 明确指出以 O_RDWR 标记打开一个 FIFO 的结果是未知的,因此出于可移植性的原因,开发人员不应该使用这项技术。对于那些需要避免在打开 FIFO 时发生阻塞的需求,open()O_NONBLOCK 标记提供了一种标准化的方法来完成这个任务:

  open(const char *path, O_RDONLY | O_NONBLOCK);
open(const char *path, O_WRONLY | O_NONBLOCK);

在打开一个 FIFO 时避免使用 O_RDWR 标记还有另外一个原因,当采用那种方式调用 open() 之后,调用进程在从返回的文件描述符中读取数据时永远都不会看到文件结束,因为永远都至少存在一个文件描述符被打开着以等待数据被写入 FIFO,即进程从中读取数据的那个描述符。

使用  FIFO 和 tee 创建双重管道线

shell 管道线的其中一个特征是它们是线性的,管道线中的每个进程都能读取前一个进程产生的数据并将数据发送到其后一个进程中,使用 FIFO 就能够在管道线中创建子进程,这样除了将一个进程的输出发送给管道线中的后面一个进程之外,还可以复制进程的输出并将数据发送到另一个进程中,要完成这个任务就需要使用 tee 命令,它将其从标准输入中读取到的数据复制两份并输出:一份写入标准输出,另一份写入到通过命令行参数指定的文件中。

mkfifo myfifo
wc -l < myfifo &
ls -l | tee myfifo | sort -k5n

非阻塞 IO

当一个进程打开一个 FIFO 的一端时,如果 FIFO 的另一端还没有被打开,那么该进程会被阻塞。但有些时候阻塞并不是期望的行为,而这可以通过在调用 open() 时指定 O_NONBLOCK  标记来实现。

如果 FIFO 的另一端已经被打开,那么 O_NONBLOCKopen() 调用不会产生任何影响,它会像往常一样立即成功地打开 FIFO。只有当 FIFO 的另一端还没有被打开的时候 O_NONBLOCK 标记才会起作用,而具体产生的影响则依赖于打开 FIFO 是用于读取还是用于写入的:

  • 如果打开 FIFO 是为了读取,并且 FIFO 的写入端当前已经被打开,那么 open() 调用会立即成功(就像 FIFO 的另一端已经被打开一样)

  • 如果打开 FIFO 是为了写入,并且还没有打开 FIFO 的另一端来读取数据,那么 open() 调用会失败,并将 errno 设置为 ENXIO

为读取而打开 FIFO 和为写入而打开 FIFO 时 O_NONBLOCK 标记所起的作用不同是有原因的。当 FIFO 的另一个端没有写者时打开一个 FIFO 以便读取数据是没有问题的,因为任何试图从 FIFO 读取数据的操作都不会返回任何数据。但当试图向没有读者的 FIFO 中写入数据时将会导致 SIGPIPE 信号的产生以及 write() 返回 EPIPE 错误。

在 FIFO 上调用 open() 的语义总结如下:

在打开一个 FIFO 时,使用 O_NOBLOCK 标记存在两个目的:

  • 它允许单个进程打开一个 FIFO 的两端,这个进程首先会在打开 FIFO 时指定 O_NOBLOCK 标记以便读取数据,接着打开 FIFO 以便写入数据

  • 它防止打开两个 FIFO 的进程之间产生死锁

例如,下面的情况将会发生死锁:

非阻塞 read()write()

O_NONBLOCK 标记不仅会影响 open() 的语义,而且还会影响——因为在打开的文件描述中这个标记仍然被设置着——后续的 read()write() 调用的语义。

有些时候需要修改一个已经打开的 FIFO(或另一种类型的文件)的 O_NONBLOCK 标记的状态,具体存在这个需求的场景包括以下几种:

  • 使用 O_NONBLOCK 打开了一个 FIFO 但需要后续的 read()write() 在阻塞模式下运行

  • 需要启用从 pipe() 返回的一个文件描述符的非阻塞模式。更一般地,可能需要更改从除 open() 调用之外的其他调用中,如每个由 shell 运行的新程序中自动被打开的三个标准描述符的其中一个或 socket() 返回的文件描述符,取得的任意文件描述符的非阻塞状态

  • 出于一些应用程序的特殊需求,需要切换一个文件描述符的 O_NONBLOCK 设置的开启和关闭状态

当碰到上面的需求时可以使用 fcntl() 启用或禁用打开着的文件的 O_NONBLOCK  状态标记。通过下面的代码(忽略的错误检查)可以启用这个标记:

int flags;

flags = fcntl(fd, F_GETFL);
flags != O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

通过下面的代码可以禁用这个标记:

flags = fcntl(fd, F_GETFL);
flags &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

管道和 FIFO 中 read() 和  write() 的语义

FIFO 上的 read() 操作:

只有当没有数据并且写入端没有被打开时阻塞和非阻塞读取之间才存在差别。在这种情况下,普通的 read() 会被阻塞,而非阻塞 read() 会失败并返回 EAGAIN 错误。

O_NONBLOCK 标记与 PIPE_BUF 限制共同起作用时 O_NONBLOCK  标记对象管道或 FIFO  写入数据的影响会变得复杂。

FIFO 上的 write() 操作:

  • 当数据无法立即被传输时 O_NONBLOCK  标记会导致在一个管道或 FIFO 上的 write() 失败(错误是 EAGAIN)。这意味着当写入了 PIPE_BUF 字节之后,如果在管道或 FIFO 中没有足够的空间了,那么 write() 会失败,因为内核无法立即完成这个操作并且无法执行部分写入,否则就会破坏不超过 PIPE_BUF 字节的写入操作的原子性的要求

  • 当一次写入的数据量超过 PIPE_BUF 字节时,该写入操作无需是原子的。因此,write() 会尽可能多地传输字节(部分写)以充满管道或 FIFO。在这种情况下,从 write() 返回的值是实际传输的字节数,并且调用者随后必须要进行重试以写入剩余的字节。但如果管道或 FIFO 已经满了,从而导致哪怕连一个字节都无法传输了,那么 write() 会失败并返回 EAGAIN 错误

声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

------------ END ------------



●专栏《嵌入式工具

●专栏《嵌入式开发》

●专栏《Keil教程》

●嵌入式专栏精选教程


关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。




点击“阅读原文”查看更多分享。

strongerHuang 作者黄工,高级嵌入式软件工程师,分享嵌入式软硬件、物联网、单片机、开发工具、电子等内容。
评论
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 112浏览
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 108浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 97浏览
  • 在不断发展的电子元件领域,继电器——作为切换电路的关键设备,正在经历前所未有的技术变革。固态继电器(SSR)和机械继电器之间的争论由来已久。然而,从未来发展的角度来看,固态继电器正逐渐占据上风。本文将从耐用性、速度和能效三个方面,全面剖析固态继电器为何更具优势,并探讨其在行业中的应用与发展趋势。1. 耐用性:经久耐用的设计机械继电器:机械继电器依靠物理触点完成电路切换。然而,随着时间的推移,这些触点因电弧、氧化和材料老化而逐渐磨损,导致其使用寿命有限。因此,它们更适合低频或对切换耐久性要求不高的
    腾恩科技-彭工 2025-01-10 16:15 77浏览
  • 说到福特,就要从亨利·福特(Henry Ford)这个人物说起。在发明大王爱迪生的电气工厂担任工程师的福特下班后,总是在自家仓库里努力研究和开发汽车。1896年,福特终于成功制造出一辆三轮车,开启了福特汽车的传奇。最初几年,福特都是独自制造汽车并同时进行销售。 (今天很多人都知道的精益管理中的5S方法,或多或少地受到了福特 CANDO方法的影响。)1903年,福特从牧师、律师、银行家、会计师等十一位股东那里筹集了十万美元,并在自家庭院成立了美国第五百零三家汽车公司——福特汽车公司(Fo
    优思学院 2025-01-10 11:21 29浏览
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 84浏览
  • 电动汽车(EV)正在改变交通运输,为传统内燃机提供更清洁、更高效的替代方案。这种转变的核心是电力电子和能源管理方面的创新,而光耦合器在其中发挥着关键作用。这些不起眼的组件可实现可靠的通信、增强安全性并优化电动汽车系统的性能,使其成为正在进行的革命中不可或缺的一部分。光耦合器,也称为光隔离器,是一种使用光传输电信号的设备。通过隔离高压和低压电路,光耦合器可确保安全性、减少干扰并保持信号完整性。这些特性对于电动汽车至关重要,因为精确控制和安全性至关重要。 光耦合器在电动汽车中的作用1.电池
    腾恩科技-彭工 2025-01-10 16:14 59浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 70浏览
  • Snyk 是一家为开发人员提供安全平台的公司,致力于协助他们构建安全的应用程序,并为安全团队提供应对数字世界挑战的工具。以下为 Snyk 如何通过 CircleCI 实现其“交付”使命的案例分析。一、Snyk 的挑战随着客户对安全工具需求的不断增长,Snyk 的开发团队面临多重挑战:加速交付的需求:Snyk 的核心目标是为开发者提供更快、更可靠的安全解决方案,但他们的现有 CI/CD 工具(TravisCI)运行缓慢,无法满足快速开发和部署的要求。扩展能力不足:随着团队规模和代码库的不断扩大,S
    艾体宝IT 2025-01-10 15:52 155浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 148浏览
  • 随着全球向绿色能源转型的加速,对高效、可靠和环保元件的需求从未如此强烈。在这种背景下,国产固态继电器(SSR)在实现太阳能逆变器、风力涡轮机和储能系统等关键技术方面发挥着关键作用。本文探讨了绿色能源系统背景下中国固态继电器行业的前景,并强调了2025年的前景。 1.对绿色能源解决方案日益增长的需求绿色能源系统依靠先进的电源管理技术来最大限度地提高效率并最大限度地减少损失。固态继电器以其耐用性、快速开关速度和抗机械磨损而闻名,正日益成为传统机电继电器的首选。可再生能源(尤其是太阳能和风能
    克里雅半导体科技 2025-01-10 16:18 89浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 114浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 101浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦