【linux】进程间通信(IPC)——匿名管道,命名管道与System V内核方案的共享内存,以及消息队列和信号量的原理概述

目录

✈必备知识

进程间通信概述

🔥概述

🔥必要性

🔥原理

管道概述

🔥管道的本质

🔥管道的相关特性

🔥管道的同步与互斥机制

匿名管道

🔥系统调用接口介绍

🔥内核原理

🔥匿名管道小实战——进程池

命名管道

🔥概述

🔥创建命名管道的指令

🔥创建命名管道函数

system V共享内存

🔥概述

🔥系统调用接口

🔥代码示例

消息队列

🔥原理概述

🔥接口介绍

创建消息队列

向消息队列发送消息

从消息队列接收消息

控制消息队列

信号量

🔥概述

🔥接口介绍

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


🤗个人主页:东洛的克莱斯韦克

必备知识

进程地址空间

进程控制——进程创建,进程退出,进程等待

理解文件系统

进程间通信概述

🔥概述

进程间通信简称IPC,是在不同进程之间传播或交换信息的一种机制。

管道是基于内存级文件的通信方案

System V解决方案中有共享内存消息队列信号量。在POSIX IPC解决方案中有共享内存,消息队列信号量互斥量条件变量读写锁

这些通信方案在系统中都属于IPC资源

🔥必要性

每个进程都有自己的地址空间,这保证了进程的独立性。即使两个进程是父子关系,可以看到同一份数据,但只要有一方进行写入操作都会发生写时拷贝

进程A异常了,不会影响到进程B,进程C......本质上来讲进程的独立性是因为进程间的数据具有独立性。

进程具有独立性是在内核设计时就有的理念。但后来发现,进程有时相互传输数据是必要的,所以必须提出解决方案——不破坏进程的独立性又能实现进程的通信。

🔥原理

先让两个进程看到同一份公共资源,这公共资源可以是文件,也可以是一块内存。

进程A向公共资源写入数据,进程B向公共资源读数据。此时两个进程完成了数据的交互,这样进程A和进程B就实现了通信。

管道概述

🔥管道的本质

管道也是文件的一种,属于p类型的文件。管道文件属于公共资源,如果多个进程打开同一个管道文件,就能相互传输数据。

管道文件是内存级文件,它在磁盘中并没有对应的数据块来为它保存文件内容。系统只会在内存中为管道文件开辟文件缓冲区来保存数据。这样做避免了内存与外设的IO交互,提高进程间通信效率。

管道文件在磁盘中有个简单的映像,这个映像的大小为 0 ,这么做是为了让文件系统知道有这么一个文件存在。

🔥管道的相关特性

管道一般是单向通信——一方是读端,一方是写端
管道具有同步与互斥机制——保护数据
管道的生命周期随进程——被打开的文件的声明周期随进程
管道是面向字节流的

🔥管道的同步与互斥机制

同步与互斥机制是为了保护管道数据的安全

读写端都正常,管道为空,读端阻塞
读写端都正常,管道为满,写端阻塞
读端正常,写端关闭,读端会读到 0 ,相当于读到文件结尾,进程正常执行后续代码
读端关闭,写端正常,写端的进程会被信号杀掉,进程异常终止

匿名管道

匿名管道本质上是没有名字的内存级的文件——适用于具有血缘关系的进程

🔥系统调用接口介绍

头文件 #include <unistd.h>
功能 : 创建匿名管道
原型 int pipe(int fd[2]);
参数 :fd文件描述符数组 , 其中 fd[0]表示读端 , fd[1]表示写端
返回值 : 成功返回 0 ,失败返回错误代码

🔥内核原理

在使用pipe系统调用之后,系统会为该进程分配两个文件描述符——files_struct数组下标。会有两个file结构体指向同一个文件缓冲区。分别以只读和只写打开文件缓冲区。

使用fork系统调用之后,子进程会继承父进程的内核数据,也包括了对应的文件描述符。此时,父子进程已经能看到同一份公共资源了——有了通信的基础。

父进程和子进程分别关掉一端文件描述符,就能实现通信啦~

🔥匿名管道小实战——进程池

与进程池概念类似的还有内存池。这些都属于池化技术——申请资源时申请走一大块资源,用户层把这些资源维护起来,在需要申请资源的时候不用再向系统中申请,提高程序的效率

我们要设计的进程池基本原理如下

父进程创建一批子进程,父子进程用匿名管道通信,父进程只要不向管道里写数据,子进程就会一直阻塞(因为父进程是写端,子进程是读端)。

父进程想让子进程执行任务时不需要再向OS申请进程,直接选一个在阻塞状态的子进程即可。

🔥代码示例

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <unistd.h>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
using namespace std;
#define PID_QUANTITY 10  // 子进程数量
#define NUMBER_OF_BYTES 4// 子进程一次读取的字节数
#define SEND 10//父进程发送消息的数量class pipeline // 描述进程池属性
{
public:pipeline(int fd, pid_t pid) // 构造函数: _fd(fd), _pid(pid){}int _fd;    // 文件描述符pid_t _pid; // 进程pid
};void reception() // 子进程接收任务
{int tmp = 0;while (true){int i = read(0, &tmp, NUMBER_OF_BYTES); // 从信道里读取if (NUMBER_OF_BYTES == i){printf("我是子进程%d,我读到的数据是:%d\n", getpid(), tmp);}if (0 == i)break;}
}void Cistern(std::vector<pipeline> *vp) // 创建进程池,输出型参数vector<pipeline> * vp
{std::vector<int> oldfds;for (int i = 0; i < PID_QUANTITY; i++){int fds[2]; // 0下标表示读端,1下表表示写端    pipe(fds); // 建立信道pid_t tmp_pid = fork(); // 创建子进程if (0 == tmp_pid) // 子进程{for (auto fd : oldfds) // 关闭子进程多余的写端{close(fd);}close(fds[1]); // 关闭子进程写端dup2(fds[0], 0); // 本来从键盘读取变为从信道读取,减少传参close(fds[0]); //  关闭多余的读端reception(); // 接收任务exit(0); // 子进程结束}close(fds[0]);                            // 关闭父进程读端vp->push_back(pipeline(fds[1], tmp_pid)); // 向父进程管理的进程池中添加字段,父进程信道的文件描述符,以及子进程的pidoldfds.push_back(fds[1]); // 记录子进程的写端,方便下一个进程继承后关闭~}
}void Reclaim(std::vector<pipeline> &vp) // 回收子进程
{for (auto c : vp)
{close(c._fd);waitpid(c._pid, nullptr, 0);
}}void Send(std::vector<pipeline> &vp) //向子进程发消息
{for (int k = 0; k < SEND; k++)         {int i = rand() % 10;int j = rand() % 10;write(vp[i]._fd, &j, sizeof(int));printf("我是父进程,我向子进程传递的数据是%d\n", j);sleep(1);    }
}int main()
{srand(time(nullptr) ^ getpid() ^ 1023); // 种一个随机数种子std::vector<pipeline> pv; // 创建容器管理进程池Cistern(&pv); // 初始化Send(pv); // 父进程派发任务Reclaim(pv); // 关闭信道和等待子进程return 0;
}

命名管道

🔥概述

命名管道的本质是具有名字的内存级文件。

使用命名管道通信无需在意进程间是否具有血缘关系。只要不同的进程能打开同一个命名管道文件——就有了通信的基础。

对命名管道文件的操作只需像普通文件操作一样调用系统的文件操作接口即可,如open  close  read  write  lseek

🔥创建命名管道的指令

 mkfifo 文件名

🔥创建命名管道函数

头文件

#include <sys/stat.h>

#include <fcntl.h>

函数原型

int mkfifo(const char *filename,mode_t mode);

参数

filename指向以 null 结尾的字符串的指针,该字符串指定了命名管道的名称(包括路径)。如果指定的文件已存在且不是一个命名管道,mkfifo 会失败。

mode指定了命名管道的权限,与 open 或 creat 函数中的 mode 参数类似。这些权限会受到进程的文件模式创建掩码(umask)的影响。

返回值

成功时,mkfifo 返回 0,出错时,返回 -1。

system V共享内存

🔥概述

进程间通信的基础是让不同进程看到同一份资源,为了达到这种目的,共享内存是一种更直接的方案。

OS会在物理内存中开辟一块空间,这块空间就是公共资源。只要把这块公共资源挂接到相应进程的地址空间上就实现了通信的基础。

只要把内存挂接到进程的地址空间的内存映射段上,该进程就有对该空间的读写权限。每一个进程都会认为该空间是自己的,那么进程之间在传输数据时不会考虑彼此的感受。换句话说,共享内存没有同步与互斥的保护机制

进程地址空间示意图

共享内存的通信方案时最快的。只要进程A把数据刷到共享内存中,其他进程就能立马拿到数据。

🔥系统调用接口

key_t ftok(const char *pathname, int proj_id);
pathname:指定字符串,一般是文件路径
proj_id:指定一个整数
通过上述的参数, ftok系统调用会通过一系列的算法生成一个 key值,给的参数越具有唯一性,ftok系统调用生成的 key值越不容易冲突。
 int shmget(key_t key, size_t size, int shmflg);

功能:创建一块共享内存

参数:第一个参数是key值,是内核标识IPC资源的唯一标识符。

第二个参数size则指定共享内存的大小,建议传入4096(4KB)的整数倍,如果传入了4097,那么系统会申请4096 * 2 的大小,因为系统申请内存是以4KB为单位大小申请的。但用户层只能用4097大小的内存。

第三个参数传入权限码和选项。0666 | IPC_CREAT|IPC_EXCL,这样传表示创建新的、唯一的共享内存段,并且权限为0666。值传入IPC_CREAT表示要获取返回值。

返回值:该函数的返回值表示用户层的key值。也就是说用户层不使用ftok返回的key值,而是使用shmget返回的值。

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

功能:把共享内存挂接到地址空间

参数:第一个参数是shmget的返回值。第二个表示要挂接到进程地址空间的什么位置,设为nullptr即可,表示让系统指定。第三个传 0即可。

返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -1

int shmdt(const void *shmaddr);

功能:地址空间和共享内存去关联

参数指向共享内存第一个节,也就是shmat的返回值

返回值 :成功返回 0 ;失败返回 -1
将共享内存段与当前进程脱离不等于删除共享内存段

🔥代码示例

IPC.hpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/types.h>  key_t Ftok() //获取key值
{key_t key = ftok("/home/ccc/d2", 's');     return key; 
}int Shmget(int i)  //创建
{int shm_id;size_t shm_size = 4096;        // 共享内存段的大小,例如1024字节key_t key = Ftok();shm_id = shmget(key, shm_size, i); return shm_id; 
}int createshm() //创建
{return Shmget(0666 | IPC_CREAT|IPC_EXCL);//权限  | 创建 有的话就获取 | 创建新的、唯一的共享内存段
}int getshm() //获取
{return Shmget(IPC_CREAT);  
}

Client.cc

// 客户端
#include "IPC.hpp"int main()
{int shm_id = getshm();char *ptr = (char *)shmat(shm_id, nullptr, 0); // 挂接printf("%s", ptr);sleep(15);shmdt(ptr);return 0;
}

Server.cc

// 服务端
#include "IPC.hpp"int main()
{int shm_id = createshm(); char* ptr = (char*)shmat(shm_id, nullptr, 0); strcpy(ptr, "你是个小可爱");printf("%s", ptr);sleep(15);shmdt(ptr);return 0;
}

消息队列

🔥原理概述

这种通信方案的公共资源是由内核维护的一种数据结构——队列

不同进程间需要先看到同一个消息队列,这样就有了通信的基础。进程向消息队列发送的是一个个带类型的数据块,带类型是为了区分该数据块是由那个进程发送的,其他进程只需要取走不属于自己的数据块就可以实现通信了。

🔥接口介绍

创建消息队列
int msgget(key_t key, int msgflg);

参数

  • key:消息队列的标识符,通常通过ftok函数生成。
  • msgflg:创建标志,常用的有IPC_CREAT(如果不存在则创建)和IPC_EXCL(与IPC_CREAT一起使用时,如果队列已存在则创建失败)。还可以按位或上权限值(如0666)来设置队列的权限。

返回值

  • 成功时返回消息队列的ID。
  • 失败时返回-1,并设置errno以指示错误原因。
向消息队列发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数

  • msqid:消息队列的ID。
  • msgp:指向要发送的消息的指针,通常是一个结构体,其第一个成员为消息类型(long类型),后续为消息数据。
  • msgsz:消息的大小(不包括消息类型),即msgp指向的结构体中消息数据的长度。
  • msgflg:控制标志,常用的有0(阻塞发送)和IPC_NOWAIT(非阻塞发送)。

返回值

  • 成功时返回0。
  • 失败时返回-1,并设置errno以指示错误原因。
从消息队列接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数

  • msqid:消息队列的ID。
  • msgp:指向接收消息的缓冲区的指针,该缓冲区应与发送消息时使用的结构体类型相同。
  • msgsz:缓冲区的最大长度,即能接收的最大消息大小(不包括消息类型)。
  • msgtyp:消息类型,用于指定接收哪种类型的消息。可以指定为0、正数或负数,具体含义见参考文章。
  • msgflg:控制标志,常用的有0(阻塞接收)和IPC_NOWAIT(非阻塞接收)。

返回值

  • 成功时返回实际接收到的消息大小(不包括消息类型)。
  • 失败时返回-1,并设置errno以指示错误原因。
控制消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数

  • msqid:消息队列的ID。
  • cmd:控制命令,常用的有IPC_STAT(获取消息队列的状态)、IPC_SET(设置消息队列的属性)和IPC_RMID(删除消息队列)。
  • buf:指向msqid_ds结构体的指针,用于存储或设置消息队列的状态和属性。

返回值

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

信号量

🔥概述

信号量本质是一把计数器

互斥:任何时刻只允许一个执行流访问资源

临界资源:共享的,只能被一个执行流访问的资源

临界区:访问临界资源的代码

访问公共资源时不能直接访问,而是要让计数器减减。也就是说,要想申请访问公共资源必须访问计数器资源,如果计数器资源为 0 ,就不能访问公共资源。

如果计数器资源为 1 ——二元信号量,那么只能有一个执行流再访问公共资源,这就时互斥,也是

计数器资源也会被多个执行流访问,那么计数器资源也是公共资源。如果如果信号量要保证公共资源的安全性,就必须保证自己的计数操作是原子性的。

原子性:本质就是两态性,即一件事要么没有做,要么做完了,没有正在做的状态。

对计数器减减称为 P 操作,对计数器加加称为 V操作。PV操作是原子性的

🔥接口介绍

创建或获取信号量集

int semget(key_t key, int nsems, int sem_flags);

参数

  • key:用于标识信号量集的键值,通常使用ftok函数生成。
  • nsems:信号量集中包含的信号量数量,通常为1。
  • sem_flags:用于指定信号量的权限和标志,常用的标志包括IPC_CREAT(如果不存在则创建)、IPC_EXCL(与IPC_CREAT一起使用,确保创建的信号量集是唯一的)。

返回值

成功时返回信号量集的标识符(semid),失败时返回-1。

控制信号量集

int semctl(int semid, int semnum, int cmd, ...);

参数说明

  • semid:信号量集的标识符。
  • semnum:信号量的索引,通常为0(如果信号量集中只有一个信号量)。
  • cmd:控制命令,用于指定要进行的操作,如SETVAL(设置信号量的值)、IPC_RMID(删除信号量集)等。
  • ...:根据cmd的不同,可能需要额外的参数,如使用SETVAL时需要指定信号量的新值。

返回值:根据不同的命令,返回不同的值。

操作信号量

int semop(int semid, struct sembuf *sops, unsigned nsops);

参数

  • semid:信号量集的标识符。
  • sops:指向sembuf结构体数组的指针,每个sembuf结构体描述了一个对信号量的操作。
  • nsops:sops数组中结构体的数量,即要执行的操作数。

sembuf结构体

struct sembuf {  unsigned short sem_num;  // 信号量在信号量集中的索引  short sem_op;            // 操作值,正数表示V操作(释放信号量),负数表示P操作(等待信号量)  short sem_flg;           // 操作标志位,如SEM_UNDO(使操作系统跟踪信号量,并在进程终止时自动释放)  
};

返回值

成功时返回0,失败时返回-1。

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

操作系统为了管理这些IPC资源,必须先用结构体描述这些IPC资源。如下是内核显示给用户层的部分内核数据字段

🔥共享内存

struct shmid_ds

{

ipc_perm

shm_perm:这是一个ipc_perm结构体,包含了共享内存段的权限和所有权信息,如UID(用户ID)、GID(组ID)、权限模式(读写执行)等。
shm_segsz:共享内存段的大小(以字节为单位)。
shm_atime:最后一次附加(attach)到共享内存段的时间。
shm_dtime:最后一次从共享内存段分离(detach)的时间。
shm_ctime:共享内存段最后一次改变的时间(如权限或所有权改变)。
shm_cpid:创建共享内存段的进程的PID(进程ID)。
shm_lpid:最后一次执行shmat(附加)或shmdt(分离)操作的进程的PID。
shm_nattch:当前附加到共享内存段的进程数。

}

🔥消息队列

struct msqid_ds

{

ipc_perm

msg_perm:这是一个ipc_perm结构体,包含了消息队列的权限和所有权信息。
key:用于msgget系统调用的键,用于获取消息队列的标识符。
msg_stime:最后一次msgsnd(发送消息)操作的时间。
msg_rtime:最后一次msgrcv(接收消息)操作的时间。
uid、gid:消息队列拥有者的有效UID和GID。
cuid、cgid:创建消息队列的进程的有效UID和GID。
msg_ctime:消息队列最后一次改变的时间(如权限或所有权改变)。
msg_qbytes:消息队列的最大字节数。
msg_qnum:消息队列中当前的消息数。
msg_lspid、msg_lrpid(未在文本中直接列出,但通常存在):最后发送消息和最后接收消息的进程的PID。

}

🔥信号量

struct semid_ds

{

ipc_perm

sem_perm:这是一个ipc_perm结构体,包含了信号量集的权限和所有权信息。
sem_otime:最后一次semop(信号量操作)操作的时间。
sem_ctime:信号量集最后一次改变的时间(如权限或所有权改变)。
sem_nsems:信号量集中的信号量数量。

}

🔥ipc_perm

struct  ipc_perm

{

key:IPC 对象的键(key)。这是一个用于唯一标识 IPC 对象的值,尽管它并不直接用于权限控制,但在创建 IPC 对象时作为查找或创建对象的依据。
uid:用户标识符(User ID)。这表示 IPC 对象的拥有者的用户 ID。只有拥有者(或其具有适当权限的进程)才能修改 IPC 对象的某些属性。
gid:组标识符(Group ID)。这表示 IPC 对象所属的组。组内的成员可能具有访问 IPC 对象的特定权限。
cuid:创建者用户标识符(Creator's User ID)。这表示创建 IPC 对象的进程的用户 ID。这个字段通常用于记录谁创建了 IPC 对象,但它本身并不直接用于权限控制。
cgid:创建者组标识符(Creator's Group ID)。这表示创建 IPC 对象的进程的组 ID。与 cuid 类似,这个字段用于记录谁创建了 IPC 对象,但本身不直接用于权限检查。
mode:权限模式。这个字段是一个位掩码,指定了 IPC 对象的访问权限。这些权限通常包括读(r)、写(w)和执行(x)权限,但对于 IPC 对象来说,“执行”权限并不适用。相反,这些权限位用于控制对 IPC 对象的访问(如读取消息、写入消息、修改信号量值等)。权限位可以分别设置给拥有者(user,即 uid)、组(group,即 gid)和其他(others)用户。
seq:序列号(Sequence Number)。这是一个内部使用的字段,用于内核中跟踪 IPC 对象的版本或变化。它对于用户空间程序来说通常是不透明的。

}

共享内存,消息队列,信号量他们之间的通信方式肯定有差异,但也有相同的属性,这些相同的属性被放在ipc_perm结构体中,ipc_perm就是基类,shmid_dsmsqid_dssemid_ds就是派生类。

内核中用一个数组储存ipc_perm类型指针,ipc_perm类型指针可能指向不同的IPC资源,这就是面向对象中多态的概念。

内核反馈给用户层的就是该数组的下标。该数组下标是分配是线性递增的,即使删除了IPC资源,反馈给用户层的数组也是线性递增的。当增到某个阈值值就会回绕,也就是再从 0 开始。


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

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

相关文章

C++ QT开发 学习笔记(1)

C QT开发 学习笔记(1) 考试系统 创建项目 新建Qt桌面应用程序&#xff0c;项目名&#xff1a;ExamSys。 类信息&#xff1a;类名LoginDialog继承自QDialog &#xff08;1&#xff09; ExamSys.pro 工程文件&#xff0c;包含当前工程的相关信息。 QDialog 是 Qt 框架中用…

二、计划任务

1.什么是计划任务 对于一些特定的任务&#xff0c;可以设定任务&#xff0c;让服务在规定时间去执行 2.windows中的计划任务 打开控制面板》管理工具》任务计划程序》创建基本任务 3.linux中的计划任务 周期性的计划crontab crontab -l :显示当前的计划惹怒我 -e&#…

大健康产业运营模式|大健康行业商业模式|健康管理盈利模式

大家好&#xff01;今天我们来聊聊如何在大健康行业中选择一个适合自己的商业模式&#xff0c;从保健、医疗、健身、美容、养老等方面快速发展并取得成功。 首先&#xff0c;大健康行业涵盖了很多领域&#xff0c;但最核心的是保健和医疗&#xff0c;这两者是保障大家健康的基础…

通过maven基于springboot项目构建脚手架archetype

1、引入脚手架构建的插件依赖 <!--构建脚手架archetype--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-archetype-plugin</artifactId><version>3.2.1</version></plugin><plugin><…

【Visual Studio】Visual Studio使用技巧及报错解决合集

目录 目录 一.概述 二.Visual Studio报错问题及解决方法 三.Visual Studio操作过程中遇到的问题及解决方法 四.Visual Studio编译优化选项 五.Visual Studio快捷键 一.概述 持续更新Visual Studio报错及解决方法&#xff0c;包括Visual Studio报错问题及解决方法、Visua…

土壤品质检测仪:守护大地之母的科技卫士

土壤&#xff0c;作为地球生命之源&#xff0c;承载着万物的生长与繁衍。然而&#xff0c;随着现代农业的快速发展&#xff0c;土壤品质问题日益凸显&#xff0c;对农作物的生长和人们的健康构成了潜在威胁。 随着环保意识的增强和农业可持续发展的需求&#xff0c;土壤品质检测…

实验8 视图创建与管理实验

一、实验目的 理解视图的概念。掌握创建、更改、删除视图的方法。掌握使用视图来访问数据的方法。 二、实验内容 在job数据库中&#xff0c;有聘任人员信息表&#xff1a;Work_lnfo表&#xff0c;其表结构如下表所示&#xff1a; 其中表中练习数据如下&#xff1a; 1.‘张明…

中霖教育:2024年中级经济师备考还来得及吗?

【中霖教育怎么样】【中霖教育口碑】 2024年的中级经济师考试还未开始报名&#xff0c;考试时间在11月16日-11月17日进行&#xff0c;考生目前距离考试还有半年的准备时间。不同的考生人群针对性的复习方法不同&#xff0c;以下内容可以作为大家的参考。 1、零基础考生&#…

Python类与对象02:继承

1、什么是继承 上一节中我们介绍了类与对象的用法。类可以看作是一种程序内的设计图纸&#xff0c;而对象则是基于这个图纸制造出来的实体。这个过程类似于现实中的设计表格和填写表格。 现在假设你是一名设计师&#xff0c;需要迭代一款新产品。你有两个选择&#xff1a; 在…

我的六天C++外出学习记

第一天 7月7日 星期日 早晨&#xff0c;我早早起来了&#xff0c;穿好衣服吃完饭就出发了。 我从家到学校用了1H&#xff0c;迟到了&#xff01;我急急忙忙去报到。 我们中午和晚上的饭菜虽说有点贵&#xff0c;但实在太美味了&#xff0c;和我们原本初中的饭菜相比&#…

手机怎么用代理ip上网

在数字化时代&#xff0c;网络已经成为我们生活中不可或缺的一部分。然而&#xff0c;有时候出于安全、隐私或访问特定网络资源的需要&#xff0c;我们可能需要使用代理IP来上网。那么&#xff0c;什么是代理IP&#xff1f;如何在手机上设置并使用它呢&#xff1f;本文将为您详…

AI绘画,Stable Diffusion姓氏头像副业拆解,操作简单,小白也能轻松上手!

前言 嗨&#xff0c;我是月月。 说时候&#xff0c;现在副业项目层出不穷&#xff0c;但要找到一个操作简单、收益可观且具有长期潜力的项目并不容易。 今天&#xff0c;我为大家推荐一个当下非常火爆的副业项目——AI定制百家姓氏头像。这不仅是一个热门项目&#xff0c;更…

云动态摘要 2024-07-12

给您带来云厂商的最新动态,最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起! [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造,可通过对话方式生成可视化…

Vue3 markRaw的使用

markRaw 作用:将一个对象标记为不可以被转化为代理对象。返回该对象本身。 应用场景: 1.有些值不应被设置成响应式时,例如复杂的第三方类库等 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能 3.在动态渲染组件的时候我们就可以使用 markRaw 包裹。markRaw 的…

Chromium实用技巧篇-如何下载指定版本的Chromium(一)

1. 引言 随着互联网技术的不断发展&#xff0c;网络浏览器已经成为我们日常生活中不可或缺的一部分。作为一款开源浏览器项目&#xff0c;Chromium不仅为Google Chrome提供了坚实的基础&#xff0c;也为其他许多浏览器提供了技术支持。如果您希望体验最前沿的浏览器功能或参与开…

一个很变态但是有用的变现手段:用AI技术搞电商模特图,接单接到手软~

前言 今天带大家拆解一个特别有趣的项目&#xff0c;必须得跟大家分享一下&#xff1a;用AI技术搞电商模特图。 是不是感觉挺科幻的&#xff1f;但这真不是科幻小说里的情节&#xff0c;而是咱们现实生活中已经实现的事情。 想想看&#xff0c;咱们平常在网上看到的那些漂亮…

网络基础知识--网络硬件设备介绍(含eNSP模拟器命令使用)

华为 eNSP 模拟器安装教程可参考&#xff1a;华为 eNSP 模拟器安装教程&#xff08;内含下载地址&#xff09;_ensp下载-CSDN博客 华为eNSP&#xff08;Enterprise Network Simulation Platform&#xff09;模拟器是一款由华为提供的免费网络仿真平台&#xff0c;主要用于模拟和…

虚拟机vmware网络设置

一、网络分类 打开vmware workstation网络编辑器可以知道有三种网络类型&#xff0c;分别是&#xff1a;桥接模式、nat模式、仅主机模式。 1、桥接模式 桥接模式是将主机网卡与虚拟机虚拟的网卡利用虚拟网桥进行通信。在桥接的作用下, 类似于把物理主机虚拟为一个交换机, 所有设…

【Wamp】局域网设备访问WampServer | 使用域名访问Wamp | Wamp配置HTTPS

局域网设备访问WampServer 参考&#xff1a;https://www.jianshu.com/p/d431a845e5cb 修改Apache的httpd.conf文件 D:\Academic\Wamp\program\bin\apache\apache2.4.54.2\conf\httpd.conf 搜索 Require local 和Require all denied&#xff0c;改为Require all granted <…

分布式系统中雪花ID的使用及前后台精度解决

本文介绍了雪花ID的应用场景&#xff0c;以及针对雪花id生成精度过大导致数据缺失的解决方案。 一、概念 雪花 ID是一种分布式 ID 生成策略&#xff0c;保证全局唯一&#xff0c;位数组成中含有时间戳&#xff0c;相比UUID,故也能保证自增。 二、应用场景 分库、分表、分片、…