【Linux】System V通信

目录

System V共享内存

共享内存数据结构

共享内存函数

共享内存的关联

共享内存的去关联

用共享内存实现serve&client通信

共享内存与管道进行对比


System V共享内存

管道通信本质是基于文件的,也就是说操作系统并没有为此做过多的设计工作,而system V IPC是操作系统特地设计的一种通信方式。但是不管怎么样,它们的本质都是一样的,都是在想尽办法让不同的进程看到同一份由操作系统提供的资源

system V IPC提供的通信方式有以下三种:

system V共享内存

system V消息队列

system V信号量

其中,system V共享内存和system V消息队列是以传送数据为目的的,而system V信号量是为了保证进程间的同步与互斥而设计的,虽然system V信号量和通信好像没有直接关系,但属于通信范畴。 

共享内存区是 最快的IPC 形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程 不再通过执行进入内核的系统调用 来传递彼此的数据

共享内存实现进程间通信,是操作系统在实际物理内存开辟一块空间,一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。

这样两个进程就可以看到一块相同的资源。当一个进程往该空间写入内容时,另外一进程访问该空间,会得到写入的值,即实现了进程间的通信。

共享内存数据结构

共享内存实现进程间通信不只仅限于两个进程之间,可以用于多个进程之间。并且系统中可能会有多个进程在进行多个通信。所以系统需要将这些通信的进程管理起来。如果不管理,操作系统怎么知道这块共享内存挂接了哪个进程等信息。这里就用到了先描述,再组织

这里可以简单看一下管理共享内存数据结构的代码:

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct shmid_ds {struct ipc_perm		shm_perm;	/* operation perms */int			shm_segsz;	/* size of segment (bytes) *///共享内存空间大小__kernel_time_t		shm_atime;	/* last attach time *///挂接时间__kernel_time_t		shm_dtime;	/* last detach time *///取消挂接时间__kernel_time_t		shm_ctime;	/* last change time *///改变时间__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */unsigned short		shm_nattch;	/* no. of current attaches *///进程挂接数unsigned short 		shm_unused;	/* compatibility */void 			*shm_unused2;	/* ditto - used by DIPC */void			*shm_unused3;	/* unused */
};

当我们申请了一块共享内存后,为了让要实现通信的进程能够看到同一个共享内存,因此每一个共享内存被申请时都要有一个key值,这个key值用于标识系统中共享内存的唯一性。
可以看到上面共享内存数据结构的一个成员是shm_perm,shm_perm是一个ipc_perm类型的结构体变量,每个共享内存的key值存储在shm_perm这个结构体变量当中,其中ipc_perm结构体的定义如下:

struct ipc_perm{__kernel_key_t  key;//共享内存的唯一标识__kernel_uid_t  uid;__kernel_gid_t  gid;__kernel_uid_t  cuid;__kernel_gid_t  cgid;__kernel_mode_t mode;//权限unsigned short  seq;
};

共享内存的建立与释放

共享内存的建立大致包括以下两个过程:

在物理内存当中申请共享内存空间。
将申请到的共享内存挂接到地址空间,即建立映射关系。

共享内存的释放大致包括以下两个过程:

将共享内存与地址空间去关联,即取消映射关系。
释放共享内存空间,即将物理内存归还给系统。

共享内存函数

  • ftok函数

其中:

  • pathname:是一个指向文件名的指针,该文件必须是存在且可以访问的。这个文件名可以是相对路径或绝对路径。
  • proj_id:是一个整数,通常用作项目的唯一标识符。在UNIX系统中,这个参数虽然是int类型,但实际上只使用8位(即0~255)。不能是0。

 ftok中的参数可以随便填写,但是要符合格式,ftok只是利用参数,再运用一套算法,算出一个唯一的key值返回。这个key值可以传给共享内存参数,作为struct ipc_perm中唯一标识共享内存的key。

 ftok函数并没有涉及内核层面。

使用示例:

int n = ftok(_pathname.c_str(), _proc_id);
if (n ==-1 )
{perror("ftok");
}

共享内存的创建

创建共享内存我们需要用shmget函数,shmget函数的函数原型如下:

参数:

   key:为共享内存的名字,一般是ftok的返回值。

  

   size:共享内存的大小,以page为单位,大小为4096的整数倍。

  

    shmflg:权限标志,常用两个IPC_CREAT和IPC_EXCL,一般后面还加一个权限,相当于文件的权限。

                                IPC_CREAT:创建一个共享内存返回,已存在打开返回

                                IPC_EXCL:配合着IPC_CREAT使用,共享内存已存在出错返回

                                使用:IPC_CREAT | IPC_EXCL | 0666

注意这里我们创建的时候,要加权限,权限或在shmflg中,如果没有权限的话,后面可能会关联失败。

 

返回值:

        成功返回一个有效的共享内存标识符(用户层标识符),失败返回-1。

使用示例:

int main() {  key_t key = ftok("/tmp", 0x66); // 使用ftok生成一个唯一的key  int shmid = shmget(key, 1024, 0666 | IPC_CREAT|IPC_EXCL); // 创建一个大小为1024字节的共享内存段且是全新的  if (shmid == -1) {  perror("shmget failed");  }  printf("Shared memory segment ID: %d\n", shmid);  // ... 后续可以使用shmat等函数操作共享内存 ...  // 最后,应使用shmctl(shmid, IPC_RMID, NULL)来删除共享内存段  return 0;  
}

当我执行两次程序,会发现,第一次程序没有报错,第二次程序报错了,错误信息为:文件存在。

这是因为我们在进程运行过程中创建了一个共享内存空间,但是在程序退出时并没有释放共享内存,再次执行程序时,共享内存依然存在,而我们用的参数组合是:IPC_CREAT | IPC_EXCL 。表示文件存在就出错返回。

当我们的进程运行完毕后,申请的共享内存依旧存在,并没有被操作系统释放。实际上,管道是生命周期是随进程的,而共享内存的生命周期是随内核的,也就是说进程虽然已经退出,但是曾经创建的共享内存不会随着进程的退出而释放。

这说明,如果进程不主动删除创建的共享内存,那么共享内存就会一直存在,直到关机重启(system V IPC都是如此),同时也说明了IPC资源是由内核提供并维护的。

此时我们若是要将创建的共享内存释放,有两个方法,一就是使用命令释放共享内存,二就是在进程通信完毕后调用释放共享内存的函数进行释放。

这里得出一个结论:IPC(进程将通信)资源生命周期不随进程,而是随内核的,不释放会一直占用,除非重启。所以,shmget创建的共享内存要释放掉,不然会内存泄漏。

这里我们可以执行命令行来释放共享内存:ipcrm -m shmid(shmget返回值)

这里介绍 ipcs命令:

单独使用ipcs命令时,会默认列出消息队列、共享内存以及信号量相关的信息,若只想查看它们之间某一个的相关信息,可以选择携带以下选项:

-q:列出消息队列相关信息。

 
-m:列出共享内存相关信息。

 
-s:列出信号量相关信息。

上面标题含义:

key 系统区别各个共享内存的唯一标识

shmid 共享内存的用户层id

owner 共享内存的拥有者

perms 共享内存的权限

bytes 共享内存的大小

nattch 关联共享内存的进程数

status 共享内存的状态

注意: key是在内核层面上保证共享内存唯一性的方式,而shmid是在用户层面上保证共享内存的唯一性,key和shmid之间的关系类似于fd和FILE*之间的的关系。

共享内存的释放

  • shmctl函数

参数

  • shmid:共享内存标识符,即要控制的共享内存段的标识符。这个值通常是通过调用shmget()函数获得的。
  • cmd:控制命令,用于指定要执行的操作。常用的命令包括IPC_STAT(获取共享内存段的状态)、IPC_SET(修改共享内存段的属性)和IPC_RMID(删除共享内存段)等。
  • buf:指向struct shmid_ds结构的指针,用于存储共享内存段的信息。当cmd为IPC_STAT时,该结构用于接收共享内存段的状态信息;当cmd为IPC_SET时,该结构中的信息将被用于更新共享内存段的属性。如果不需要获取或设置共享内存段的信息,可以将此参数设置为NULL。

返回值

  • 成功时,shmctl函数返回0。
  • 失败时,返回-1,并设置errno以指示具体的错误原因。

使用示例

int main() {  key_t key = ftok("somefile", 0x65); // Generate a unique key based on file path  int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // Create a new shared memory segment  if (shmid == -1) {  perror("shmget"); // Print error message if shmget fails  return 1;  }  // Remove the shared memory segment  if (shmctl(shmid, IPC_RMID, NULL) == -1) {  perror("shmctl"); // Print error message if shmctl fails  return 1;  }  return 0;  
}

用shmctl命令之后就可以删除释放共享内存,执行多次也不会发生内存泄漏。

我们可以在程序运行时,使用以下监控脚本时刻关注共享内存的资源分配情况:

while :; do ipcs -m;echo "###################################";sleep 1;done

共享内存的关联

shmat函数

作用:将共享内存与进程地址空间的映射起来,构建页表映射关系,物理内存加载到进程地址空间。

函数参数:

  • shmid:共享内存标识符,由shmget函数返回。
  • shmaddr:指定共享内存出现在进程内存地址的什么位置。如果设置为NULL,则由内核自动选择一个合适的地址位置。
  • shmflg:控制共享内存的附加方式和行为。常用的标志位包括:
    • SHM_RDONLY:以只读方式附加共享内存段。如果省略此标志,则默认以读写方式附加。
    • SHM_RND:将附加地址舍入到最接近的SHMLBA(共享内存锁定字节对齐)的倍数,以提高性能。
    • SHM_REMAP:如果该区域已经附加到调用进程的地址空间,则重新附加它。
    • SHM_EXEC:将共享内存段标记为可执行(这在某些系统上可能不被支持)。

函数返回值:

  • 成功:返回指向共享内存第一个字节的指针。这里与malloc相似
  • 失败:返回-1,并将错误代码存储在errno中。

使用示例:

    // 附加共享内存到进程地址空间  char *shmaddr;  shmaddr = shmat(shmid, NULL, 0);  if (shmaddr == (char *)-1) {  perror("shmat");  }  

注意这里是void*类型的-1 。

另外这里要注意权限问题,如果没有共享内存没有权限的话,可能会关联失败。

如果没有加上权限0666,则会出现下面的报错

当加入权限时,此时再运行程序,即可发现关联该共享内存的进程数由0变成了1,而共享内存的权限显示也不再是0,而是我们设置的666权限。

我们应该在使用shmget函数创建共享内存时,在其第三个参数处设置共享内存创建后的权限,权限的设置规则与设置文件权限的规则相同。

共享内存的去关联

取消共享内存与进程地址空间之间的关联我们需要用shmdt函数,shmdt函数的函数原型如下:

  • shmdt函数

作用:删除共享内存与进程地址空间的映射关系,将页表映射关系删除,释放进程地址空间。

参数:

  • shmaddr:这是需要分离的共享内存段的起始地址,该地址应该是之前通过shmat函数成功附加到当前进程地址空间的共享内存段的地址。

返回值:

  • 成功时,shmdt函数返回0。
  • 失败时,返回-1,并设置相应的错误码(errno)以指示错误原因

注意事项:

  • shmdt函数只是将共享内存从当前进程的地址空间中分离出来,它并不会删除该共享内存段。 将共享内存段与当前进程脱离不等于删除共享内存,只是取消了当前进程与该共享内存之间的联系。如果系统中没有其他进程再使用该共享内存段,并且已经通过shmctl函数设置了IPC_RMID命令来标记其删除,那么系统会在所有进程都分离该共享内存后将其删除。
  • 在使用shmdt函数之前,必须确保已经通过shmat函数成功附加了共享内存段,并且传入的shmaddr参数是有效的。shmat和shmdt要一起使用才起作用。
  • 在多进程环境中,需要特别注意同步和互斥问题,以避免数据竞争和不一致性。

用共享内存实现serve&client通信

  实现进程间通信步骤:

  1. 创建共享内存
  2. 共享内存关联进程
  3. 删除共享内存与进程的关联
  4. 释放共享内存

这里我们同样的将这些步骤封装在一个类当中。创建共享内存只需要实例化一个对象就行了。

需要client和server都和共享内存关联。client端不创建共享内存,不释放共享内存,server端创建共享内存,并且释放共享内存。

类中(shm.hpp) 内容如下:

#ifndef __SHM_HPP__
#define __SHM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define gCreater  1
#define gUser  2
const std::string pathname = "/home/HCC/linux/code11";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 这里系统是按照4096*n的样式开辟空间,因为4096是访问的基本单位,开4098的话会开辟2*4096的空间,但是会浪费4094的空间class Shm
{
private:key_t GetCommKey(){key_t k = ftok(_pathname.c_str(), _proj_id);if (k < 0){perror("ftok");}return k;}int GetShmHelper(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){perror("shmget");}return shmid;}std::string RoleToString(int who){if (who == gCreater)return "Creater";else if (who == gUser)return "gUser";elsereturn "None";}void *AttachShm()//挂接函数{if (_addrshm != nullptr)DetachShm(_addrshm);void *shmaddr = shmat(_shmid, nullptr, 0);if (shmaddr == nullptr){perror("shmat");}std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;return shmaddr;}void DetachShm(void *shmaddr){if (shmaddr == nullptr)return;shmdt(shmaddr);std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;}public:Shm(const std::string &pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetCommKey();if (_who == gCreater)GetShmUseCreate();else if (_who == gUser)GetShmForUse();_addrshm = AttachShm();std::cout << "shmid: " << _shmid << std::endl;std::cout << "_key: " << ToHex(_key) << std::endl;}~Shm(){DetachShm(_addrshm);//析构的时候先分离if (_who == gCreater){int res = shmctl(_shmid, IPC_RMID, nullptr);//移除空间——只能创建者移除}std::cout << "shm remove done..." << std::endl;}std::string ToHex(key_t key){char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}bool GetShmUseCreate(){if (_who == gCreater){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);if (_shmid >= 0)return true;std::cout << "shm create done..." << std::endl;}return false;}bool GetShmForUse(){if (_who == gUser){_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);if (_shmid >= 0)return true;std::cout << "shm get done..." << std::endl;}return false;}void Zero(){if(_addrshm){memset(_addrshm, 0, gShmSize);}}void *Addr(){return _addrshm;}private:key_t _key;int _shmid;std::string _pathname;int _proj_id;int _who;void *_addrshm;
};#endif

这样写客户端和服务端的话,会有一个问题。就是这段内存加载进进程地址空间,双方随时都可以访问,这样就造成了数据不一致问题,可能写端还没写完,读端就开始读了。我们为了解决这个问题,利用了管道使用write/read会阻塞的特性。使每次写端写完时读端才去读取,这样就初步解决了数据不一致问题。同时也能实现服务端随客户端同步关闭。

这里得出一个结论:共享内存实现的进程间通信底层不提供任何同步与互斥机制。如果想让两进程很好的合作起来,在IPC里要有信号量来支撑

客户端代码:client.cc

#include"Shm.hpp"
#include"NamedPipe.hpp"int main()
{Shm shm(pathname,gUser,gProc_id);shm.Zero();char *shmaddr=(char*)shm.AddrShm();NamedPipe fifo(path,gCreater);fifo.OpenForWrite();int i=0;for(char ch='a';ch<='f';ch++){sleep(1);shmaddr[i++]=ch;std::string tmp="wake";fifo.WriteNamedPipe(tmp);//当写完才给读端发信号,唤醒读端。}return 0;
}

服务端代码:server.cc

#include "NamedPipe.hpp"
#include "Shm.hpp"
int main()
{Shm shm(pathname, gCreater, gProc_id);char *shmaddr = (char *)shm.AddrShm();// 打开管道NamedPipe fifo(path, gCreater);fifo.OpenForRead();while (true){std::string tmp;int n=fifo.ReadNamePipe(&tmp);if(n==0){std::cout<<"read over "<<std::endl;break;}else if(n<0){std::cout<<" read error"<<std::endl;break;}std::cout << shmaddr << std::endl;}return 0;
}

共享内存与管道进行对比

当共享内存创建好后就不再需要调用系统接口进行通信了,而管道创建好后仍需要read、write等系统接口进行通信。实际上,共享内存是所有进程间通信方式中最快的一种通信方式。

先看看管道通信:

从这张图可以看出,使用管道通信的方式,将一个文件从一个进程传输到另一个进程需要进行四次拷贝操作:

服务端将信息从输入文件复制到服务端的临时缓冲区中。

  
将服务端临时缓冲区的信息复制到管道中。

  
客户端将信息从管道复制到客户端的缓冲区中。

  
将客户端临时缓冲区的信息复制到输出文件中。

再来看看共享内存通信:

从这张图可以看出,使用共享内存进行通信,将一个文件从一个进程传输到另一个进程只需要进行两次拷贝操作: 

从输入文件到共享内存。

从共享内存到输出文件。

所以共享内存是所有进程间通信方式中最快的一种通信方式,因为该通信方式需要进行的拷贝次数最少

当然这里还有一个问题:因为写端比读端慢,相比较于管道,当写端比读端慢时,没有写入时,读端要进入阻塞状态,但是上面发现使用共享内存的读端一直在读,并没有阻塞。

我们知道管道是自带同步与互斥机制的,但是共享内存并没有提供任何的保护机制,包括同步与互斥。

这里得出一个结论:共享内存实现的进程间通信底层不提供任何同步与互斥机制。如果想让两进程很好的合作起来,在IPC里要有信号量来支撑。

注意:两进程之间用通过同样的规则即ftok参数要一样,来获取同样的key值,然后在创建共享内存时,用这个key值唯一标识共享内存,所以两进程时通过同样的key值来实现看到同一份资源。

消息队列

消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
 
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
 
特性方面
 
IPC 资源必须删除,否则不会自动清除,除非重启,所以 system V IPC 资源的生命周期随内核

消息队列大概是这样的:

 

进程A如果需要将数据交给进程B,就把数据先描述再组织起来,然后放入msg_queue当中。这个msg_queue与共享内存一样是操作系统维护的资源,多个进程可以同时看到。打包的一个数据有一个标识符表明数据是A的。进程B 需要进程A的数据时,只需要将队列中标识符是A的一个元素提取出来,从而拿到A的数据。进程A要想拿到进程B的数据也是类似。

消息队列与共享内存有类似的接口,比如说:

 信号量

信号量主要用于同步和互斥的,下面先来看看什么是同步和互斥。

进程互斥
由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
在进程中涉及到互斥资源的程序段叫临界区
特性方面
IPC 资源必须删除,否则不会自动清除,除非重启,所以 system V IPC 资源的生命周期随内核

进程同步

        进程同步机制的主要任务,是对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间能按照一定的规则共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。

用户进程可以通过使用操作系统提供的一对原语(原子性)来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。

信号量其实就是一个变量 ,可以用一个信号量来表示系统中某种资源的数量,比如:系统中只有一台打印机,就可以设置一个初值为 1 的信号量。

共享内存没有同步互斥机制,信号量就可以解决这个问题。他给共享内存增加了PV操作。PV操作是原子性的,使得每次操作都是原子性的。(后置++/--不是原子性的,不能替代信号量)

信号量也与共享内存和消息队列有类似的接口:

OS统一管理共享内存,消息队列,信号量

1.System V

2.xxxget,xxxctl

3.xxxid_ds, ipc_perm

我们发现,共享内存,消息队列和信号量都有各自的 ipc_perm数据结构。

ipc_perm 是一个结构体,里面存储着ipc的一些基本信息。管理好他们也就管理了ipc。

OS把他们先描述再组织起来放在一个柔性数组(struct kern_ipc_perm*xxx[n])(可变数组)当中。

我们每创建一个ipc通信资源,这个数组就把资源加入数组中,当删除时,遍历数组然后从数组中去掉。

这个操作类似于文件描述符表与fd一样,每一个ipc资源在数组里面就是一个下标。从而OS就实现了对ipc资源的管理。

当然这里出现一个问题,就是3种方式共用同一个下标,OS是如何区分呢。在这个下标中有一个字段是指向目标类型的,就是xxx_per->mode。寻找时我们只需要通过这个标识符知道是哪种资源,然后通过指针强转去访问就可以得到对应的数据。这也类似于多态。

 

 

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

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

相关文章

【网络安全】服务基础第二阶段——第二节:Linux系统管理基础----Linux统计,高阶命令

目录 一、Linux高阶命令 1.1 管道符的基本原理 1.2 重定向 1.2.1 输出重定向 1.2.2 输入重定向 1.2.3 wc命令基本用法 1.3 别名 1.3.1 which命令基本语法 1.3.2 alias命令基本语法 1.4 压缩归档tar 1.4.1 第一种&#xff1a;gzip压缩 1.4.2 第二种&#xff1a;bzip…

SpringBoot3 简单集成 Mybatis plus

SpringBoot3 集成 Mybatis plus 1、引入Mybatisplus的starter <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.7</version></dependency>2、引入数据…

前端HTML基础笔记

HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是一种用于创建网页的标准标记语言。它通过一系列的元素&#xff08;或称为标签&#xff09;来定义网页的结构和内容。HTML文档由一系列的元素组成&#xff0c;这些元素可以包含文本、图片、链…

LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用

在国家“3060”碳达峰碳中和的政策背景下&#xff0c;如何寻求经济-能源-环境的平衡有效发展是国家、省份、城市及园区等不同级别经济体的重要课题。根据国家政策、当地能源结构、能源技术发展水平以及相关碳排放指标制定合理有效的低碳能源发展规划需要以科学准确的能源环境发…

培训第九周(部署k8s基础环境)

一、前期系统环境准备 1、关闭防火墙与selinux [rootk8s-master ~]# systemctl stop firewalld[rootk8s-master ~]# systemctl disable firewalldRemoved symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.Removed symlink /etc/systemd/system/dbus-o…

机器学习-神经网络:循环神经网络(RNN)详解

引言 在当今人工智能(AI)和深度学习(DL)领域,循环神经网络(RNN)作为一种专门处理序列数据的模型,具有不可忽视的重要性。RNN 的设计目标是模拟和处理序列中的时间依赖关系,使其成为许多应用场景的理想选择,如自然语言处理(NLP)、时间序列预测和语音识别等。它不仅…

【C语言】插入排序、希尔排序——动图展示

目录 1. 插入排序1.1 基本概念1.2 实现思路1.3 代码部分 2. 希尔排序2.1 为什么会有希尔排序&#xff1f;2.2 基本概念2.3 实现思想1&#xff09;单组排序2&#xff09;多组排序 2.4 代码部分 3. 总结 1. 插入排序 1.1 基本概念 把待排序的记录逐个插入到一个已经排好序的有序…

[数据集][目标检测]电动车头盔佩戴检测数据集VOC+YOLO格式4235张5类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4235 标注数量(xml文件个数)&#xff1a;4235 标注数量(txt文件个数)&#xff1a;4235 标注…

在OpenEuler(欧拉)系统上用kubeadm部署(k8s)Kubernetes集群

一、OpenEuler(欧拉) 系统简介 openEuler 是开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目&#xff1b; openEuler作为一个操作系统发行版平台&#xff0c;每两年推出一个LTS版本。该版本为企业级用户提供一个安全稳定可靠的操作系统。…

[数据集][目标检测]智慧农业草莓叶子病虫害检测数据集VOC+YOLO格式4040张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4040 标注数量(xml文件个数)&#xff1a;4040 标注数量(txt文件个数)&#xff1a;4040 标注…

10个好用的AI写作工具【亲测免费】

1. 光速写作 传送入口&#xff1a;http://u3v.cn/6hXWYa AI打工神器&#xff0c;一键生成文章&ppt 2. 讯飞写作 传送入口&#xff1a;http://m6z.cn/5ODiSw 3. 讯飞绘文 传送入口&#xff1a;https://turbodesk.xfyun.cn/?channelidgj3 4. AI排版助手 传送入口&#…

jenkins 安装

jenkins安装 jenkins官网 中文网址 安装设置 所有jenkins版本 内存512M以上&#xff0c;10Gb磁盘&#xff1b;安装jdk&#xff0c;需要java8以上下载较新的版本&#xff0c;否则安装插件时可能报错版本过低 # 搜索java yum search java | grep -iE "jdk"# 安装jd…

网络安全知识科普:什么是网络准入控制系统?有哪些?

在当今数字化时代&#xff0c;网络安全已成为企业和组织不可忽视的重要议题。随着远程工作模式的普及和物联网设备的增加&#xff0c;网络边界越来越模糊&#xff0c;传统防火墙已经不足以应对日益复杂的威胁环境。在这种背景下&#xff0c;网络准入控制系统(Network Access Co…

最多购买宝石数目

题目描述 橱窗里有一排宝石&#xff0c;不同的宝石对应不同的价格&#xff0c;宝石的价格标记为 gems[i] 0≤i<nn gems.length 宝石可同时出售0个或多个&#xff0c;如果同时出售多个&#xff0c;则要求出售的宝石编号连续; 例如客户最大购买宝石个数为m&#xff0c;购买…

Nginx 负载均衡+高可用 集群部署(Keepalived+LVS DR模式)

一、LVS负载均衡简介 1.1 LVS基本介绍 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导开发的开源负载均衡项目&#xff0c;目前LVS已经被集成在Linux内核中。该项目在Linux内核中实现了基于IP地址的请求数据负载均衡调度方…

如何使用正则表达式替换字符串中的特定位置数字

如何使用正则表达式替换字符串中的特定位置数字 1、效果 把字符串中的第一个123替换掉: 2、代码 使用正则中的sub函数: re.sub(pattern,repl,string,count=0,flags=0) pattern:表示需要匹配的模式,即需要被替换的字符或字符串。 repl:表示替换后的字符串或函数,用于…

多目标应用:四种多目标优化算法(NSGA2、NSPSO、NSDBO、NSCOA)求解柔性作业车间调度问题(FJSP),MATLAB代码

一、柔性作业车间调度问题 柔性作业车间调度问题(Flexible Job Scheduling Problem, FJSP) 的描述如下&#xff1a;n个工件 { J , J 2 , . . , J n } \{J,J_2,..,J_n\} {J,J2​,..,Jn​}要在 m m m 台机器 { M 1 , M 2 , . . , M m } \{M_1,M_2,..,M_m\} {M1​,M2​,..,Mm​} …

spring 事物使用场景说明

事务使用场景。 在某些业务场景下&#xff0c;如果一个请求中&#xff0c;需要同时写入多张表的数据。为了保证操作的原子性&#xff08;要么同时成功&#xff0c;要么同时失败&#xff09;&#xff0c;避免数据不一致的情况&#xff0c;我们一般都会用到spring事务。 确实&am…

【Shiro】Shiro 的学习教程(五)之 SpringBoot 集成 Shiro + JWT

与 Spring 集成&#xff1a;与 Spring 集成 与 SpringBoot 集成&#xff1a;与 SpringBoot 集成 1、SpringBoot Shiro Jwt ①&#xff1a;引入 pom.xml&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-…

【网络安全】服务基础第二阶段——第三节:Linux系统管理基础----Linux用户与组管理

目录 一、用户与组管理命令 1.1 用户分类与UID范围 1.2 用户管理命令 1.2.1 useradd 1.2.2 groupadd 1.2.3 usermod 1.2.4 userdel 1.3 组管理命令 1.3.1 groupdel 1.3.2 查看密码文件 /etc/shadow 1.3.4 passwd 1.4 Linux密码暴力破解 二、权限管理 2.1 文件与目…