【Linux】进程间通信IPC机制
目录
一、无名管道
二、有名管道
三、共享内存
四、信号量
五、消息队列
六、套接字
一、无名管道
1.只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程)。
2.是一个单工的通信模式,具有固定的读端和写端。
3.管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()、write()等函数。但是它不属于任何文件系统,并且只存在于内存中。
#include <unistd.h>
int pipe(int filedes[2]);
在管道中,文件描述符数组的第一个元素(索引为0)用于读取,第二个元素(索引为1)用于写入。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define BUFFER_SIZE 25
#define READ_END 0
#define WRITE_END 1int main()
{char write_msg[BUFFER_SIZE] = "Hello, child!";char read_msg[BUFFER_SIZE];int fd[2];pid_t pid;// 创建管道if (pipe(fd) == -1) {fprintf(stderr, "Pipe failed");return 1;}// 创建子进程pid = fork();if (pid < 0) {fprintf(stderr, "Fork failed");return 1;}// 父进程if (pid > 0) {// 关闭写入端,因为父进程不会写入close(fd[WRITE_END]);// 从管道中读取消息read(fd[READ_END], read_msg, BUFFER_SIZE);printf("Parent received message from child: %s\n", read_msg);// 关闭读取端close(fd[READ_END]);} // 子进程else { // 关闭读取端,因为子进程不会读取close(fd[READ_END]);// 写入消息到管道write(fd[WRITE_END], write_msg, BUFFER_SIZE);// 关闭写入端close(fd[WRITE_END]);}return 0;
}
./a.out
执行结果:Parent received message from child: Hello, child!
二、有名管道
mkfifo(const char *pathname, mode_t mode):
- 这个函数用于创建一个有名管道。
- 参数
pathname是要创建的管道的路径名,mode是指定管道权限的位掩码。- 返回值:成功时返回0,失败时返回-1,并设置
errno变量表示错误类型。open(const char *pathname, int flags, mode_t mode):
- 这个函数用于打开文件或管道。
- 参数
pathname是要打开的文件或管道的路径名,flags指定打开文件的方式(如只读、只写、读写等),mode是当创建新文件时指定的权限。- 返回值:成功时返回文件描述符,失败时返回-1,并设置
errno变量表示错误类型。write(int fd, const void *buf, size_t count):
- 这个函数用于向文件描述符指定的文件或管道中写入数据。
- 参数
fd是打开文件或管道时返回的文件描述符,buf是要写入的数据缓冲区,count是要写入的字节数。- 返回值:成功时返回实际写入的字节数,失败时返回-1,并设置
errno变量表示错误类型。read(int fd, void *buf, size_t count):
- 这个函数用于从文件描述符指定的文件或管道中读取数据。
- 参数
fd是打开文件或管道时返回的文件描述符,buf是用于接收数据的缓冲区,count是要读取的最大字节数。- 返回值:成功时返回实际读取的字节数,失败时返回-1,并设置
errno变量表示错误类型。close(int fd):
- 这个函数用于关闭打开的文件描述符。
- 参数
fd是要关闭的文件描述符。- 返回值:成功时返回0,失败时返回-1,并设置
errno变量表示错误类型。unlink(const char *pathname):
- 这个函数用于删除文件或链接。
- 参数
pathname是要删除的文件或链接的路径名。- 返回值:成功时返回0,失败时返回-1,并设置
errno变量表示错误类型。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>#define BUFFER_SIZE 25int main()
{char *fifo_name = "./name";char write_msg[BUFFER_SIZE] = "Hello, world!";char read_msg[BUFFER_SIZE];// 创建有名管道mkfifo(fifo_name, 0666);pid_t pid = fork();if (pid == 0)// 子进程负责写入{// 打开管道进行写入int fd_write = open(fifo_name, O_WRONLY);write(fd_write, write_msg, BUFFER_SIZE);close(fd_write);} else if (pid > 0) // 父进程负责读取{ // 打开管道进行读取int fd_read = open(fifo_name, O_RDONLY);read(fd_read, read_msg, BUFFER_SIZE);printf("Received message: %s\n", read_msg);close(fd_read);} else // fork失败{fprintf(stderr, "Fork failed");return 1;}// 删除管道unlink(fifo_name);return 0;
}
三、共享内存
共享内存是最高效的,因为避免了数据在用户空间和内核空间的来回拷贝
ftok(const char *pathname, int proj_id):
ftok()函数用于生成一个唯一的key,用于创建或访问共享内存。- 参数
pathname是一个路径名,proj_id是一个整数,用于生成key。- 返回值:如果成功,返回一个唯一的key,如果失败,返回-1。
shmget(key_t key, size_t size, int shmflg):
shmget()函数用于创建共享内存段或获取共享内存段的标识符。- 参数
key是由ftok()生成的唯一key,size是共享内存的大小,shmflg是标志位,用于指定权限和行为。- 返回值:如果成功,返回共享内存段的标识符,如果失败,返回-1。
shmat(int shmid, const void *shmaddr, int shmflg):
shmat()函数用于将共享内存连接到当前进程的地址空间。- 参数
shmid是共享内存段的标识符,shmaddr通常为NULL(表示系统自动选择地址),shmflg是标志位,通常为0。- 返回值:如果成功,返回指向共享内存段的指针,如果失败,返回(void *)-1。
shmdt(const void *shmaddr):
shmdt()函数用于将共享内存从当前进程的地址空间分离。- 参数
shmaddr是指向共享内存段的指针。- 返回值:如果成功,返回0,如果失败,返回-1。
shmctl(int shmid, int cmd, struct shmid_ds *buf):
shmctl()函数用于对共享内存进行控制操作,如删除共享内存段。- 参数
shmid是共享内存段的标识符,cmd是控制命令,buf是一个指向shmid_ds结构体的指针,用于获取共享内存的状态信息。- 返回值:如果成功,返回0,如果失败,返回-1。
读端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>#define SHM_SIZE 1024int main()
{key_t key = ftok("/", 'R'); // 生成共享内存的key// 创建共享内存段int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget");exit(1);}// 将共享内存连接到当前进程的地址空间char *shmaddr = shmat(shmid, NULL, 0);if (shmaddr == (void *)-1) {perror("shmat");exit(1);}// 写入数据到共享内存strcpy(shmaddr, "Hello, shared memory!");// 分离共享内存if (shmdt(shmaddr) == -1) {perror("shmdt");exit(1);}printf("数据已写入共享内存\n");return 0;
}
写端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define SHM_SIZE 1024int main()
{key_t key = ftok("/", 'R'); // 生成共享内存的key// 获取共享内存段int shmid = shmget(key, SHM_SIZE, 0666);if (shmid == -1) {perror("shmget");exit(1);}// 将共享内存连接到当前进程的地址空间char *shmaddr = shmat(shmid, NULL, 0);if (shmaddr == (void *)-1){perror("shmat");exit(1);}printf("从共享内存中读取到的消息:%s\n", shmaddr);// 分离共享内存if (shmdt(shmaddr) == -1) {perror("shmdt");exit(1);}return 0;
}
写端执行后:
数据已写入共享内存
读端执行后:
从共享内存中读取到的消息:Hello, shared memory!
四、信号量
1.信号量实现互斥
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define KEY 1234 // 信号量的键值// 定义一个联合体,用于semctl初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};// 创建一个二值信号量并初始化为1
int create_semaphore()
{// 创建一个信号量集,包含1个信号量int semid = semget(KEY, 1, IPC_CREAT | 0666); if (semid == -1) {perror("semget");exit(1);
}union semun arg;arg.val = 1; // 初始值为1,表示资源可用if (semctl(semid, 0, SETVAL, arg) == -1) // 初始化信号量{perror("semctl");exit(1);}return semid;
}// P操作(等待资源)
void P(int semid)
{struct sembuf op;op.sem_num = 0; // 信号量集中的第一个信号量op.sem_op = -1; // 对信号量执行P操作op.sem_flg = 0;if (semop(semid, &op, 1) == -1) {perror("semop");exit(1);}
}// V操作(释放资源)
void V(int semid)
{struct sembuf op;op.sem_num = 0; // 信号量集中的第一个信号量op.sem_op = 1; // 对信号量执行V操作op.sem_flg = 0;if (semop(semid, &op, 1) == -1) {perror("semop");exit(1);}
}int main()
{int semid = create_semaphore(); // 创建信号量pid_t pid = fork(); // 创建子进程if (pid == -1) {perror("fork");exit(1);}if (pid == 0) // 子进程{P(semid); // 等待资源printf("Child process: Counter decremented by 1.\n");V(semid); // 释放资源} else // 父进程{sleep(1); // 让子进程有机会先运行P(semid); // 等待资源printf("Parent process: Counter incremented by 1.\n");V(semid); // 释放资源}return 0;
}
semget(key_t key, int nsems, int semflg):
- 这个函数用于创建一个新的信号量集或获取一个现有信号量集的标识符。
key是一个用于唯一标识信号量集的键值。nsems指定了信号量集中的信号量数量。semflg是一组标志,用于指定创建信号量集的权限和行为。- 返回值:成功时返回信号量集的标识符,失败时返回-1。
semctl(int semid, int semnum, int cmd, union semun arg):
- 这个函数用于对信号量集进行控制操作,如初始化、设置值、获取值等。
semid是信号量集的标识符。semnum是指定的信号量在集合中的索引,通常为0。cmd是指定要执行的控制命令。arg是一个联合体,用于传递控制命令的参数。- 返回值:根据控制命令不同而不同。
semop(int semid, struct sembuf *sops, size_t nsops):
- 这个函数用于执行一组信号量操作,如等待资源(P操作)和释放资源(V操作)。
semid是信号量集的标识符。sops是一个指向信号量操作结构体数组的指针。nsops是指定的信号量操作结构体数组的大小。- 返回值:成功时返回0,失败时返回-1。
struct sembuf:
- 这是一个结构体,用于描述信号量操作。
- 它包含了三个字段:
sem_num:信号量集中的信号量索引。sem_op:信号量操作,通常是-1(P操作,等待资源)或1(V操作,释放资源)。sem_flg:信号量操作的标志位,通常为0。
union semun:
- 这是一个联合体,用于传递给
semctl()函数的参数。- 它包含了多个字段,其中的一个字段可以根据不同的控制命令来使用,比如用于设置或获取信号量值时使用的
val字段,用于获取信号量状态信息时使用的buf字段等
create_semaphore():
- 这个函数用于创建一个二值信号量,并将其初始化为1。
- 首先,它调用
semget()函数创建一个包含一个信号量的信号量集,如果创建失败则会打印错误信息并退出程序。- 然后,它使用
semctl()函数将信号量的值初始化为1,表示资源可用。- 最后,它返回创建的信号量集的标识符。
P(int semid):
- 这个函数用于执行 P 操作,即等待资源。
- 首先,它定义了一个
struct sembuf结构体op,用于描述信号量操作。- 然后,它将
op.sem_num设置为0,表示信号量集中的第一个信号量。- 接着,它将
op.sem_op设置为-1,表示对信号量执行 P 操作。- 最后,它调用
semop()函数执行信号量操作,如果操作失败则会打印错误信息并退出程序。
V(int semid):
- 这个函数用于执行 V 操作,即释放资源。
- 它的实现与
P()函数类似,只是将op.sem_op设置为1,表示对信号量执行 V 操作。
2.信号量实现同步互斥
交替实现奇偶数打印,打印一个奇数后必须是一个偶数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define KEY 1234 // 信号量的键值// 定义一个联合体,用于semctl初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};// 创建一个信号量并初始化为1
int create_semaphore()
{int semid = semget(KEY, 1, IPC_CREAT | 0666); // 创建一个信号量集,包含1个信号量if (semid == -1) {perror("semget");exit(1);}union semun arg;arg.val = 0; // 初始值为0if (semctl(semid, 0, SETVAL, arg) == -1) // 初始化信号量{ perror("semctl");exit(1);}return semid;
}// P操作(等待资源)
void P(int semid)
{struct sembuf op;op.sem_num = 0; // 信号量集中的第一个信号量op.sem_op = -1; // 对信号量执行P操作op.sem_flg = 0;if (semop(semid, &op, 1) == -1) {perror("semop");exit(1);}
}// V操作(释放资源)
void V(int semid)
{struct sembuf op;op.sem_num = 0; // 信号量集中的第一个信号量op.sem_op = 1; // 对信号量执行V操作op.sem_flg = 0;if (semop(semid, &op, 1) == -1) {perror("semop");exit(1);}
}int main()
{int i = 1;int j = 2;int semid = create_semaphore(); // 创建信号量pid_t pid = fork(); // 创建子进程if (pid == -1) {perror("fork");exit(1);}if (pid == 0) // 子进程打印奇数{while(1){ printf("i = %d\n",i);i += 2;V(semid); // 释放资源sleep(1);} } else // 父进程打印偶数{while(1){P(semid); // 等待资源printf("j = %d\n",j);j += 2;} }return 0;
}
执行结果
i = 1
j = 2
i = 3
j = 4
i = 5
j = 6
i = 7
j = 8
i = 9
j = 10
i = 11
j = 12
i = 13
j = 14
i = 15
j = 16
五、消息队列
发送进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MSG_KEY 1234 // 消息队列的键值// 定义消息结构体
struct msg_buffer
{long msg_type; // 消息类型char msg_text[100]; // 消息内容
};int main()
{int msgid;struct msg_buffer message_send;// 创建消息队列msgid = msgget(MSG_KEY, IPC_CREAT | 0666);if (msgid == -1) {perror("msgget");exit(1);}// 准备发送的消息strcpy(message_send.msg_text, "Hello, Message Queue!");message_send.msg_type = 1; // 消息类型为1// 发送消息if (msgsnd(msgid, &message_send, sizeof(message_send) - sizeof(long), 0) == -1) {perror("msgsnd");exit(1);}printf("Parent process sent message: %s\n", message_send.msg_text);return 0;
}
接收进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MSG_KEY 1234 // 消息队列的键值// 定义消息结构体
struct msg_buffer {long msg_type; // 消息类型char msg_text[100]; // 消息内容
};int main()
{int msgid;struct msg_buffer message_rcv;// 创建消息队列msgid = msgget(MSG_KEY, IPC_CREAT | 0666);if (msgid == -1) {perror("msgget");exit(1);}// 接收消息if (msgrcv(msgid, &message_rcv, sizeof(message_rcv) - sizeof(long), 1, 0) == -1) {perror("msgrcv");exit(1);}printf("Child process received message: %s\n", message_rcv.msg_text);return 0;
}
执行结果:
./s
Parent process sent message: Hello, Message Queue!
./r
Child process received message: Hello, Message Queue!
在这两个示例代码中,主要使用了以下系统调用:
msgget: 用于创建或打开一个消息队列。它接受一个参数,即消息队列的键值(一个唯一的标识符),并返回一个消息队列的标识符(msgid)。
msgsnd: 用于向消息队列发送消息。它接受四个参数:消息队列的标识符(msgid)、指向要发送的消息的指针、消息的大小(不包括消息类型字段的大小)、消息的标志(通常为0)。
msgrcv: 用于从消息队列接收消息。它接受五个参数:消息队列的标识符(msgid)、指向用于接收消息的缓冲区的指针、缓冲区大小(不包括消息类型字段的大小)、消息类型(通常为正整数)、接收消息的标志(通常为0)。
msgctl: 用于控制消息队列的属性,如删除消息队列。在这个示例中,我们使用它来删除消息队列。它接受三个参数:消息队列的标识符(msgid)、命令(IPC_RMID 表示删除消息队列)、指向用于控制消息队列属性的结构体的指针(在这个例子中不需要)。
六、套接字
网络部分的内容
相关文章:
【Linux】进程间通信IPC机制
目录 一、无名管道 二、有名管道 三、共享内存 四、信号量 五、消息队列 六、套接字 一、无名管道 1.只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程)。 2.是一个单工的通信模式,具有固定的读端和写端。 3.管道也可以看成是一种特殊的文件…...
【如此简单!数据库入门系列】之效率基石 -- 磁盘空间管理
文章目录 1 前言2 磁盘空间管理3 磁盘空间管理的实现4 存储对象关系5 总结6 系列文章 1 前言 如何将表中的记录存储在物理磁盘上呢? 概念模式中,记录(Record)表示表中的一行数据,由多个列(字段或者属性&…...
专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(五)
本系列课程,将重点讲解Phpsploit-Framework框架软件的基础使用! 本文章仅提供学习,切勿将其用于不法手段! 继续接上一篇文章内容,讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 在下面的图片中&#…...
5月7日监控二叉树+斐波那契数
968.监控二叉树 给定一个二叉树,我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 示例 1: 输入:[0,0,null,0,0] 输出:1 解释ÿ…...
C++类的设计编程示例
一、银行账户类 【问题描述】 定义银行账户BankAccount类。 私有数据成员:余额balance(整型)。 公有成员方法: 无参构造方法BankAccount():将账户余额初始化为0; 带参构造方法BankAccount(int m)࿱…...
YOLOv5 V7.0 - rknn模型的验证 输出精度(P)、召回率(R)、mAP50、mAP50-95
1.简介 RKNN官方没有提供YOLOv5模型的验证工具,而YOLOv5自带的验证工具只能验证pytorch、ONNX等常见格式的模型性能,无法运行rknn格式。考虑到YOLOv5模型转换为rknn会有一定的精度损失,但是需要具体数值才能进行评估,所以需要一个…...
CUDA、CUDNN、Pytorch三者之间的关系
这个东西嘛,我一开始真的是一头雾水,安装起来真是麻烦死了。但是随着要复现的项目越来越多,我也不得不去学会他们是什么,以及他们之间的关系。 首先,一台电脑里面允许有多种版本的cuda存在,然后cuda分为run…...
vue-cli2,vue-cli3,vite 生产环境去掉console.log
console.log一般都是在开发环境下使用的,在生产环境下需要去除 ,如果手动删除未免也太累了,我们可以用插件对于具体环境全局处理。 vue-cli2 项目build 下面webpack.prod.config.js 文件中: plugins: [new webpack.DefinePlugin({process.en…...
Docker-Compose编排LNMP并部署WordPress
前言 随着云计算和容器化技术的快速发展,使用 Docker Compose 编排 LNMP 环境已经成为快速部署 Web 应用程序的一种流行方式。LNMP 环境由 Linux、Nginx、MySQL 和 PHP 组成,为运行 Web 应用提供了稳定的基础。本文将介绍如何通过 Docker Compose 编排 …...
附录C:招聘流程
< 回到目录 附录C:招聘流程 _xxx_公司的招聘 使命 只雇佣顶级人才。 他们是能够胜任工作,并与 _(你的公司名称)_ 的企业文化相匹配的超级明星。 方法 记分卡。招聘经理创建一份文件,详细描述此职位的工作内容…...
1688快速获取整店铺列表 采集接口php Python
在电子商务的浪潮中,1688平台作为中国领先的批发交易平台,为广大商家提供了一个展示和销售商品的广阔舞台;然而,要在众多店铺中脱颖而出,快速获取商品列表并进行有效营销是关键。 竞争对手分析 价格比较:…...
CTF-WEB(MISC)
安全攻防知识——CTF之MISC - 知乎 CTF之MISC杂项从入门到放弃_ctf杂项 你的名字-CSDN博客 CTF MICS笔记总结_archpr 掩码攻击-CSDN博客 一、图片隐写 CTF杂项---文件类型识别、分离、合并、隐写_ctf图片分离-CSDN博客 EXIF(Exchangeable Image File)是…...
Ubuntu如何更换 PyTorch 版本
环境: Ubuntu22.04 WLS2 问题描述: Ubuntu如何更换 PyTorch 版本考虑安装一个为 CUDA 11.5 编译的 PyTorch 版本。如何安装旧版本 解决方案: 决定不升级CUDA版本,而是使用一个与CUDA 11.5兼容的PyTorch版本,您可…...
python flask css样式无效
解释: Flask是一个Python的轻量级Web框架,它没有为CSS提供任何内置的支持。如果你在Flask项目中引入了CSS文件,但是这个CSS没有生效,可能的原因有: 路径不正确:你的CSS文件没有放在正确的目录下࿰…...
大数据学习笔记14-Hive基础2
一、数据字段类型 数据类型 :LanguageManual Types - Apache Hive - Apache Software Foundation 基本数据类型 数值相关类型 整数 tinyint smallint int bigint 小数 float double decimal 精度最高 日期类型 date 日期 timestamps 日期时间 字符串类型 s…...
vue3 下载图片(包括多图片下载)
单图片下载 //使用 download(https://img1.baidu.com/it/u1493209339,2544178769&fm253&app138&sizew931&n0&fJPEG&fmtauto?sec1715101200&t854f3434686cfd2cba9d6a528597d15c)//下载逻辑 const download async (modelUrl) > {const respons…...
LabVIEW如何通过子VI更改主VI控件属性?
在LabVIEW中,可以通过使用Local Variable或Property Node来实现主VI控件属性的更改。这些方法可以在主VI和子VI之间传递数据和控件属性。 Local Variable: 使用Local Variable可以在子VI中直接访问并修改主VI中的控件属性。在子VI中创建Local Variable,并…...
关于MS-DOS时代的回忆
目录 一、MS-DOS是什么? 二、MS-DOS的主要功能有哪些? 三、MS-DOS的怎么运行的? 四、微软开源MS-DOS源代码 五、高手与漂亮女同学 一、MS-DOS是什么? MS-DOS(Microsoft Disk Operating System)是微软公…...
数据库索引(Mysql)
简述:数据库索引是加速数据检索,提高查询效率的一种数据结构 语法规则 创建索引 --通用语法规则 --[内容] 可选参数 --UNIQUE: 可选关键字,用于创建唯一索引,确保索引列的值是唯一的 CREATE [UNIQUE] INDEX 索引名 ON 表名(字段名,...) [ASC | DESC];…...
异常-Exception
异常介绍 基本概念 Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)执行过程中所发生的异常事件可分为两大类 1,Error(错误):Java虚拟机无法…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
