Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)

news2024/11/13 15:26:46

Linux:进程间通信(二.共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)

上次结束了进程间通信一:Linux:进程间通信(一.初识进程间通信、匿名管道与命名管道、共享内存)


文章目录

  • 1.System V共享内存
    • 1.1相关函数介绍
    • 写个小项目
      • 项目规划
      • Cnmm.hpp
      • ShmClient.cpp
      • PipeServe.cpp
    • 1.2指令查看与删除
  • 2.System V消息队列
      • 消息队列的原理
      • 相关函数
  • 3.System V 信号量
    • 3.1概念
    • 3.2周边知识
  • 4.内核中

1.System V共享内存

在这里插入图片描述

实现进程间通信的前提就是如何让不同的进程看到同一份资源

  • 匿名管道我们是通过子进程继承父进程打开的资源
  • 命名管道是通过两个进程都打开具有唯一性标识的命名管道文件(路径+文件名)
  • 共享内存其实是通过OS创建一块shm

System V共享内存(Shared Memory)是一种Linux中用于进程间通信(IPC)的机制。它允许多个进程访问同一块物理内存区域,从而实现数据的快速共享和交换。

  1. 原理

    • 在物理内存中申请一块内存空间作为共享内存。
    • 将这块内存空间与各个进程的页表建立映射关系,使得这些进程在虚拟地址空间中可以看到并访问这块共享内存。
    • 通过这种方式,多个进程可以像访问自己的内存一样访问共享内存,从而实现数据的快速共享和交换。
  2. 使用方式

    • 创建:使用shmget()系统调用来创建共享内存。这个函数会分配一块指定大小的内存区域,并返回一个标识符,用于后续对这块共享内存的操作。
    • 关联:使用shmat()系统调用来将共享内存关联到进程的地址空间。这个函数会将共享内存的地址告诉进程,使得进程可以通过这个地址来访问共享内存。
    • 取消关联:当进程不再需要访问共享内存时,可以使用shmdt()系统调用来取消关联。这个函数会断开进程与共享内存之间的映射关系。
    • 释放:当所有进程都不再需要这块共享内存时,可以使用shmctl()系统调用来释放它。这个函数会回收这块内存区域,并释放相关的资源。
  3. 特性:

  • 共享内存的生命周期是在进程结束后仍然存在的,直到显式地将其删除或者系统重启。这种情况下,如果进程没有主动释放共享内存,那么共享内存将一直存在于系统中,直到以下情况之一发生才会被释放:
  1. 代码删除:可以通过调用shmctl函数来删除共享内存,释放其资源。删除共享内存后,系统会立即释放共享内存的资源。
  2. 指令删除:在Linux系统中,可以使用ipcs命令查看系统中的IPC资源(包括共享内存),并使用ipcrm命令来删除特定的IPC资源
  • 共享内存是所有进程间通信中速度最快的原因

无需内核参与:在共享内存中,多个进程可以直接访问同一块物理内存区域,而无需通过内核进行数据的拷贝和传输。这样可以避免了进程间切换和内核态和用户态之间的数据拷贝,从而提高了通信的效率。

  • 共享内存并不提供进程间协同的机制,也不提供同步和互斥的功能,需要我们用户自己来实现

1.1相关函数介绍

ftok() 函数 Linux中用于生成一个唯一的键值(key)的系统调用,这个键值通常用于在进程间通信(IPC)中标识共享内存段、消息队列或信号量集。ftok() 函数基于一个已经存在的文件路径和一个非零的标识符(通常是一个小的正整数)来生成这个键值。

#include <sys/ipc.h>  
#include <sys/types.h>  
  
key_t ftok(const char *pathname, int proj_id);

参数:

  • pathname:指向一个已经存在的文件路径的指针。这个文件通常被用作生成键值的“种子”或“基础”。
  • proj_id:一个非零的标识符,通常是一个小的正整数。这个值将与文件路径一起被用于生成键值。返回值:

如果成功,ftok() 函数返回一个唯一的键值key_t 类型),该键值可以在后续的 IPC 调用(如 shmget(), msgget(), semget() 等)中用作参数。如果失败,则返回 (key_t) -1 并设置 errno 以指示错误。

  1. shmget():创建或获取共享内存

shmget() 系统调用用于创建一个新的共享内存对象,或者如果它已存在,则返回该对象的标识符。

函数原型

int shmget(key_t key, size_t size, int shmflg);

参数

  • key:一个键,用于唯一标识共享内存对象。通常使用ftok()函数生成。

    1. 共享内存在内核中同时可以存在很多个,OS必须要管理所有的共享内存
    • 如何管理呢?先描述,在组织
    • 系统中会存在很多共享内存,怎么保证,多个不同的进程看到的是同共享内存呢? 要给共享内存提供唯一性的标识
    1. key便是那个唯一性标识符。那么为什么这个key要由我们用户来传入呢?
    • 如果然系统生成,将值返回让我们得到。那我们如何给另外一个进程呢?要做到就要有进程间通信,这不倒反天罡了?
  • size:共享内存的大小(以字节为单位)。

  • shmflg:权限标志和选项。通常设置为IPC_CREAT如果对象不存在则创建,存在的话直接获取)和权限(如0666)。

    若设置为IPC_CREAT|IPC_EXCL(如果对象不存在则创建,存在的话出错返回)

返回值:成功时返回共享内存对象的标识符;失败时返回-1并设置errno

  1. shmctl():控制共享内存

在这里插入图片描述

shmctl() 系统调用用于获取或设置共享内存的属性,或者删除共享内存对象。

函数原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

  • shmid:共享内存对象标识符。
  • cmd:要执行的操作。例如,IPC_RMID用于删除共享内存对象,IPC_STAT用于获取其状态。
  • buf:指向shmid_ds结构的指针,用于传递或接收共享内存的状态信息。

返回值:成功时返回0;失败时返回-1并设置errno

  1. shmat():将共享内存关联到进程的地址空间

shmat() (attach)系统调用用于将共享内存对象关联到调用进程的地址空间

在这里插入图片描述

函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数

  • shmidshmget()返回的共享内存对象标识符
  • shmaddr:希望将共享内存附加到的进程的地址。如果设置为NULL,则系统选择地址(一般都这样)。
  • shmflg:通常设置为0或SHM_RND(使附加地址向下舍入到最接近的SHMLBA边界)。

返回值:成功时返回共享内存附加到进程的地址;失败时返回(void *)-1并设置errno

  1. shmdt():取消共享内存的关联

在这里插入图片描述

shmdt() 系统调用用于取消之前通过shmat()附加到进程的共享内存的关联。

函数原型

int shmdt(const void *shmaddr);

参数

  • shmaddrshmat()返回的共享内存附加到进程的地址。

返回值:成功时返回0;失败时返回-1并设置errno

写个小项目

项目规划

在这里插入图片描述

  • Cnmm.hpp:函数的声明定义,头文件的包含、宏定义等任务
  • ShmClient.cpp:客户端,
  • ShmServer.cpp:服务端(服务器)

makefile:

.PHONY:all
all:shm_client shm_server

shm_server:ShmServer.cc
	g++ -o $@ $^ -std=c++11
shm_client:ShmClient.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f shm_client shm_server

Cnmm.hpp

#pragma once

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

using namespace std;

const char *pathname = "/home/zc/study/lesson26/5.4_shm_ipc";
const int proj_id = 0x1;
const int defaultsize = 4096; // 单位是字节

key_t GetShmKeyOrDie()
{
    key_t key = ftok(pathname, proj_id);
    if (key == -1)
    {
        cerr << "ftok error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;
        exit(1); // 出错就直接退出
    }
    return key;
}

int CreateShmOrDie(key_t key, int size, int flag)
{
    int shmid = shmget(key, size, flag);
    if (shmid < 0)
    {
        std::cerr << "shmget error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;
        exit(2); // 出错就直接退出
    }
    return shmid;
}

int CreateShm(key_t key, int size)
{
    return CreateShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666); // 没有就创建,有就报错,权限666
}

int GetShm(key_t key, int size)
{
    return CreateShmOrDie(key, size, IPC_CREAT); // 不存在则创建,存在的话直接获取
}

void DeleteShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    if (n == -1)
    {
        cerr << "shmctl error" << errno << endl;
    }
    else
    {
        cout << "delete successfully" << endl;
    }
}

void *ShmAttach(int shmid)
{
    void *addr = shmat(shmid, nullptr, 0);
    if ((long long int)addr == -1)
    {
        std::cerr << "shmat error" << std::endl;
        return nullptr;
    }
    return addr;
}

void ShmDt(void *addr)
{
    int n = shmdt(addr);
    if (n < 0)
    {
        cerr << "shmdt error" << endl;
    }
}

ShmClient.cpp

#include "Comm.hpp"

int main()
{
    // 获取key
    key_t key = GetShmKeyOrDie();
    cout << "key:" << key << endl;
    // 获取shmid
    int shmid = GetShm(key, defaultsize);
    cout << "shmid:" << shmid << endl;
    // 进行挂接
    void *tem = ShmAttach(shmid);
    char *addr = (char *)tem;
    cout << "Attch successfully" << endl;

    // 进行通信,这里进行写入
    for (char ch = 'A'; ch <= 'Z'; ch++)
    {
        addr[ch - 'A'] = ch;
    }
    // 取消挂接
    ShmDt(tem);

    return 0;
}

PipeServe.cpp

#include "Comm.hpp"
#include <unistd.h>

int main()
{
    // 获取key
    key_t key = GetShmKeyOrDie();
    cout << "key:" << key << endl;
    // 创建共享内存
    int shmid = CreateShm(key, defaultsize);
    cout << "shmid:" << shmid << endl;
    // 进行挂接
    void *tem = ShmAttach(shmid);
    char *addr = (char *)tem;
    cout << "Attch successfully" << endl;

    // 进行通信,这里进行读
    for (;;)
    {
        cout << "shm content: " << addr << std::endl;
    }
    // 取消挂接
    ShmDt(tem);

    // 删除共享内存
    sleep(50);
    DeleteShm(shmid);
    return 0;
}

1.2指令查看与删除

  1. 使用ipcs命令查看系统中的共享内存:
ipcs -m

显示的以下内容:

  1. 键值 (key):共享内存段的键值,用于唯一标识一个共享内存段。在创建共享内存段时可以指定一个键值,其他进程可以通过这个键值来访问同一个共享内存段。
  2. shmid:共享内存段的标识符,是系统为每个共享内存段分配的唯一标识符。其他进程可以通过 shmid 来操作同一个共享内存段。
  3. 权限 (perms):共享内存段的权限信息,包括读、写、执行权限。通常以八进制形式表示,比如 0600 表示用户具有读写权限,其他用户没有权限。
  4. 拥有者 (owner):共享内存段的拥有者,即创建该共享内存段的用户或进程。
  5. 大小 (bytes):共享内存段的大小,以字节为单位。表示该共享内存段所分配的内存空间大小。
  6. 附加进程数 (nattch):当前附加到共享内存段的进程数目。当一个进程附加到共享内存段时,nattch 数加一;当一个进程分离(detach)时,nattch 数减一。

在这里插入图片描述

  1. 使用ipcrm命令删除指定的共享内存:
ipcrm -m <shmid>

<shmid>是要删除的共享内存段的标识符。通过这个命令可以删除指定的共享内存段,释放其资源。

在这里插入图片描述

shmid与key分辨:

在共享内存的设计中,keyshmid 的使用确实是为了实现内核层和用户层之间的解耦,从而使它们在宏观层面上互不影响,具有独立性。下面详细解释一下这种设计的好处和原因:

  • 内核层使用 key 来唯一标识共享内存段,而用户层使用 shmid 来访问和操作已存在的共享内存段。这种设计使得内核层和用户层的代码逻辑相互独立,彼此不直接依赖。
  • 当内核层需要进行某些代码改动或优化时,只需在内核层处理 key 的逻辑,而不需要影响用户层的代码。用户层代码不需要关心内核层的具体实现细节,只需要通过 shmid 来操作共享内存即可。

2.System V消息队列

System V消息队列是一种进程间通信的机制,允许进程之间通过消息进行通信。消息队列提供了一个消息缓冲区,进程可以向消息队列发送消息,也可以从消息队列接收消息。下面我们来详细讲解消息队列的原理以及相关函数。

消息队列的原理

  1. 消息队列结构:消息队列是一个由内核维护的消息缓冲区,通常由消息类型和消息数据组成。每个消息都包含一个消息类型和消息数据,进程可以根据消息类型选择接收特定类型的消息。

  2. 消息发送:进程可以调用系统调用函数向消息队列发送消息,将消息写入消息队列中。发送消息时,需要指定消息队列的标识符、消息类型以及消息数据等信息。

  3. 消息接收:进程可以调用系统调用函数从消息队列接收消息,读取消息队列中的消息。接收消息时,可以选择阻塞式接收或非阻塞式接收,进程可以根据需要选择适合的接收方式。

  4. 消息类型:每个消息都有一个消息类型,进程可以根据消息类型选择接收特定类型的消息。这样可以实现不同类型的消息传递和处理。

  5. 消息队列管理:消息队列由内核管理,进程可以通过系统调用函数对消息队列进行创建、删除、发送消息、接收消息等操作。

相关函数

当使用System V消息队列相关函数时,需要了解函数的原型、参数和返回值。以下是这些函数的介绍:

  1. msggetint msgget(key_t key, int msgflg);
    • 参数key为消息队列的键值,msgflg为权限标志和操作标志的组合。
    • 返回值:成功时返回消息队列标识符,失败时返回-1并设置errno
  2. msgctlint msgctl(int msqid, int cmd, struct msqid_ds *buf);
    • 参数msqid为消息队列的标识符,cmd为控制命令,buf为消息队列信息结构体指针。
    • 返回值:成功时返回0,失败时返回-1并设置errno
  3. msgsndint msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • 参数msqid为消息队列的标识符,msgp为消息缓冲区指针,msgsz为消息长度,msgflg为标志位。
    • 返回值:成功时返回0,失败时返回-1并设置errno
  4. msgrcvssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    • 参数msqid为消息队列的标识符,msgp为消息缓冲区指针,msgsz为消息长度,msgtyp为消息类型,msgflg为接收标志。
    • 返回值:成功时返回接收到的消息数据的长度,失败时返回-1并设置errno

3.System V 信号量

3.1概念

  1. 信号量本质:
  • 信号量通常被用来保护公共资源,确保多个进程或线程能够有序地访问和使用这些资源

  • 信号量是一个整型变量,通常用于控制对共享资源的访问。信号量的值可以表示可用资源的数量或者某个资源的占用情况

  • 信号量:信号量本质是一个计数器,描述临界资源数量的计数器

    为什么不能直接用一个int来进行呢?

    无法在进程间共享 而且一个int ++- -不是原子的

  • 信号量:公共资源中,对局部性资源进行预定的机制。可以被用于对局部性资源进行预定,实现对资源的合理分配和控制

  • 信号量是让不同的进程看到同一个计数器资源,而进程间通信是让不同的进程看到一份资源。所以信号量也属于进程间通信

进程申请信号量一旦成功:就一定有这个进程的资源了(相当于去看电影买票,一定有我们的座位了,而且别人也买不了这个座位)

当我们释放信号量后,这份资源才能给别人(看完电影后,这个座位才能接着被下一个买)

申请信号量和释放信号量来保护临界资源, 是大家都要遵守的规则(我们程序员)

  • 所有的进程,访问临界资源,都必须先申请信号量, 所有进程都得先看到同一个信号量 ==> 信号量本身就是共享资源
  1. 信号量操作:

    那谁来保护信号量呢? 通过使用原子的PV操作来保障

  • P操作(等待操作):当进程需要访问共享资源时,首先执行P操作,即尝试获取信号量。如果信号量的值大于0,则将信号量的值减1,表示资源已被占用,进程可以继续执行;如果信号量的值为0,则表示资源已被占用,进程需要等待。(允许申请多个信号量)
  • V操作(释放操作):当进程使用完共享资源后,执行V操作,即释放信号量。释放信号量会增加信号量的值,表示资源已经释放,其他进程可以继续访问。

3.2周边知识

  1. 互斥和同步:

    • 互斥:指的是在访问共享资源时,任何时刻只允许一个执行流访问,其他执行流需要等待。这样可以避免多个执行流同时修改共享资源导致数据不一致。互斥是通过使用锁或信号量等机制来实现的。(任何时候都只有一个人能访问,当信号量为1——只有一个整体资源时,不就是互斥吗)
    • 同步:指的是在访问共享资源时,具有一定的顺序性,确保多个执行流按照特定的顺序访问资源。同步可以通过信号量、条件变量等机制来实现,以确保执行流之间的协调和顺序性。
  2. 临界资源和临界区:

    • 临界资源:是指被保护起来的,任何时刻只允许一个执行流访问的公共资源。访问临界资源的代码段被称为临界区。
    • 临界区:是指访问临界资源的代码段,程序员需要在临界区内使用互斥和同步机制来确保对临界资源的安全访问
  3. 原子性:指的是操作对象的操作是不可分割的,要么整个操作完成,要么没有开始。在多线程或多进程环境中,保证操作的原子性是非常重要的,可以通过锁或原子操作等机制来实现。

  4. 保护临界区的本质:程序员需要保护临界区,确保在任何时刻只有一个执行流可以访问临界资源,避免数据竞争和不一致性。这通常通过使用锁、信号量或其他同步机制来实现。


4.内核中

内核中,所有的描述管理IPC资源的结构体,第一个成员大家都一样 kern_ipc_perm。我们可以用指针数组来进行管理

在这里插入图片描述

在内核中,对IPC资源的管理也是转变为对数组的增删查改

类型不同我们怎么解决呢?——直接强转

  • (msg_ queue*)ipc[0]> q _time;
  • (sem_array)ipc[1]->q_time;
  • (shmid kernel)ipc[2]-> 。。。

那怎么知道是什么类型呢?

kern_ipc_perm里有一个有个mode变量,能来表示类型

#define IPC TYPE SHM Ox1
#define IPC TYPE MSG (0x1<<1)
#define IPC TYPE SEM(0x1 << 2)//定义这三个宏后

shmid kernel* (kern ipc_perm* p)
{
	if (p->mode & IPC TYPE SHM)
	{
		return (shmid kernel)ipc
	}
	else if (p->mode & IPC TYPE MSG)
	{
		///
	}
	else
	{
		///
	}
}

好啦,我也是结束了实训,才到家!!!
感谢大家的支持

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

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

相关文章

分布式IO模块软件配置

组态接口模块 1、打开网络视图 2、拖拽出ET200SP 3、双击ET200SP的图片&#xff0c;进入从站配置 总线适配器的组态更换 关于IO地址分配&#xff0c;需要建立好子网通信后&#xff0c;在主机上配置。 可以看到IP 和设备名 设备与控制器的Profinet连接 先找到设备名称再找…

H桥驱动器芯片详解

H桥驱动器芯片详解 上一篇文章讲解了H桥驱动器的控制原理&#xff0c;本文以汽车行业广泛应用的DRV8245芯片为例&#xff0c;详细讲解基于集成电路的H桥驱动器芯片。 1.概述 DRV824x-Q1系列器件是德州仪器&#xff08;TI&#xff09;的一款专为汽车应用设计的全集成H桥驱动器…

Linux——开发工具

1.yum yum是centos中的一个软件下载安装管理客户端&#xff0c;可以下载需要的软件或者解决依赖关系问题&#xff08;如动态库&#xff09;。程序都是来源于一段源代码&#xff0c;为了方便下载&#xff0c;源代码被提前在不同的环境下编译好生成对应的yum软件包&#xff0c;存…

微信小程序毕业设计-书店系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

自动群发消息插件常用源代码科普!

随着网络技术的快速发展&#xff0c;自动群发消息插件成为了众多企业和个人提高效率、加强沟通的重要工具。 然而&#xff0c;开发一个高效且稳定的自动群发消息插件并非易事&#xff0c;需要深入理解并熟练掌握相关的源代码。 本文将从五个方面&#xff0c;通过具体的源代码…

【Java】搜索引擎设计:信息搜索怎么避免大海捞针?

一、内容分析 我们准备开发一个针对全网内容的搜索引擎&#xff0c;产品名称为“Bingoo”。 Bingoo的主要技术挑战包括&#xff1a; 针对爬虫获取的海量数据&#xff0c;如何高效地进行数据管理&#xff1b;当用户输入搜索词的时候&#xff0c;如何快速查找包含搜索词的网页…

【代码随想录】【算法训练营】【第59天】 [卡码110]字符串接龙 [卡码105]有向图的完全可达性 [卡码106]岛屿的周长

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 59&#xff0c;周五&#xff0c;继续ding~ 题目详情 [卡码110] 字符串接龙 题目描述 卡码110 字符串接龙 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 […

深度整合全球资源,分贝通打造高效、合规的海外差旅管理平台

在全球化商业活动的背景下,中国企业出海已成为常态。然而,随着海外差旅市场的全面增长,企业在海外支出管理上面临诸多挑战。据2023年数据显示,分贝通出海差旅业务GMV同比增长高达500倍,这一增长背后隐藏着企业对于更省钱、更高效管控方式的迫切需求。 面对与日俱增的开支,企业开…

自动化办公004—有效解决ChatGPT数学公式无法复制到Office、MarkDown的问题

ChatGPT在输出数学公式的时候是正常的&#xff0c;但是粘贴到word、粘贴到MarkDown笔记里就直接变成了一堆代码&#xff0c;其实是有办法解决的&#xff0c;本篇文章主要解决一下无法复制问题 目录 一、问题复现二、效果展示1. 测试案例2. 开始转换&#xff08;1&#xff09;运…

【保姆级介绍下C语言中的运算符的优先级】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 🧇C语言中的运算符的优先级 🧇C语言中的运算符的优先级决定了表达…

2024-07-09 Linux 使用gpio-keyboard标准方式获取按键事件实例代码

一、kernel dts 配置修改&#xff1a; diff --git a/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-evb1-v11.dts b/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-evb1-v11.dts index d0d059a3b..584f3a00a 100755 --- a/sysdrv/source/kernel/arch/arm/boot/dts/rv110…

Hospital Management Startup 1.0 SQL 注入漏洞(CVE-2022-23366)

前言 CVE-2022-23366是一个影响HMS v1.0的SQL注入漏洞。该漏洞存在于patientlogin.php文件中&#xff0c;允许攻击者通过特定的SQL注入来获取或修改数据库中的敏感信息。 具体来说&#xff0c;攻击者可以通过向patientlogin.php发送恶意构造的SQL语句来绕过身份验证&#xff…

LabVIEW开发阀门自动校准装置

1. 装置概述与目标 在工业和实验室环境中&#xff0c;阀门的准确性和稳定性对于流体控制和实验数据的可靠性非常重要。LabVIEW可以作为开发阀门自动校准装置的理想工具&#xff0c;提供高度可定制化的解决方案。 2. 硬件与设备选择 型号选择&#xff1a;为了实现阀门自动校准…

三维点云配准 -- ICP 算法原理及推导

三维点云配准 -- ICP 算法原理及推导 - 知乎 (zhihu.com) 三维点云配准 -- ICP 算法 | Yilins Blog Alex Segal - Research - Generalized-ICP (ox.ac.uk)

Redisson分布式锁、可重入锁

介绍Redisson 什么是 Redisson&#xff1f;来自于官网上的描述内容如下&#xff01; Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格客户端&#xff08;In-Memory Data Grid&#xff09;。它不仅提供了一系列的 redis 常用数据结构命令服务&#xff0c;还提供了…

线程池案例

秒杀 需求 10个礼物20个客户抢随机10个客户获取礼物&#xff0c;另外10无法获取礼物 任务类 记得给共享资源加锁 public class MyTask implements Runnable{// 礼物列表private ArrayList<String> gifts ;// 用户名private String username;public MyTask( String user…

第十八节 LLaVA如何按需构建LORA训练(视觉、语言、映射多个组合训练)

文章目录 前言一、基于llava源码构建新的参数1、添加lora_vit参数2、训练命令脚本设置二、修改源码,构建lora训练1、修改源码-lora训练2、LLM模型lora加载3、VIT模型加载4、权重冻结操作5、结果显示三、实验结果前言 如果看了我前面文章,想必你基本对整个代码有了更深认识。…

技术速递|VS Code Java 6月更新 - 项目设置功能增强!大量 Spring 新特性

作者&#xff1a;Nick Zhu 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Visual Studio Code for Java 的六月更新&#xff01;在这篇博客中&#xff0c;我们将分享项目设置项目的重要更新以及一系列 Spring 的功能改进&#xff0c;让我们开始吧&#xff01; 项目设…

gitlab-runner安装部署CI/CD

手动安装 卸载旧版&#xff1a; gitlab-runner --version gitlab-runner stop yum remove gitlab-runner下载gitlab对应版本的runner # https://docs.gitlab.com/runner/install/bleeding-edge.html#download-any-other-tagged-releasecurl -L --output /usr/bin/gitlab-run…

工业一体机为数字化工厂带来高效作业指导

随着工业4.0的浪潮席卷全球&#xff0c;数字化工厂的概念深入人心。在这一背景下&#xff0c;工业一体机作为数字化转型的重要一环&#xff0c;凭借其强大的功能和灵活的应用&#xff0c;为工厂实现高效作业指导提供了强大的助力。 一、工业一体机的优势&#xff1a;赋能数字化…