当前位置: 首页 > news >正文

【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! 

二、有名管道

  1. mkfifo(const char *pathname, mode_t mode):

    • 这个函数用于创建一个有名管道。
    • 参数pathname是要创建的管道的路径名,mode是指定管道权限的位掩码。
    • 返回值:成功时返回0,失败时返回-1,并设置errno变量表示错误类型。
  2. open(const char *pathname, int flags, mode_t mode):

    • 这个函数用于打开文件或管道。
    • 参数pathname是要打开的文件或管道的路径名,flags指定打开文件的方式(如只读、只写、读写等),mode是当创建新文件时指定的权限。
    • 返回值:成功时返回文件描述符,失败时返回-1,并设置errno变量表示错误类型。
  3. write(int fd, const void *buf, size_t count):

    • 这个函数用于向文件描述符指定的文件或管道中写入数据。
    • 参数fd是打开文件或管道时返回的文件描述符,buf是要写入的数据缓冲区,count是要写入的字节数。
    • 返回值:成功时返回实际写入的字节数,失败时返回-1,并设置errno变量表示错误类型。
  4. read(int fd, void *buf, size_t count):

    • 这个函数用于从文件描述符指定的文件或管道中读取数据。
    • 参数fd是打开文件或管道时返回的文件描述符,buf是用于接收数据的缓冲区,count是要读取的最大字节数。
    • 返回值:成功时返回实际读取的字节数,失败时返回-1,并设置errno变量表示错误类型。
  5. close(int fd):

    • 这个函数用于关闭打开的文件描述符。
    • 参数fd是要关闭的文件描述符。
    • 返回值:成功时返回0,失败时返回-1,并设置errno变量表示错误类型。
  6. 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;
}

三、共享内存

共享内存是最高效的,因为避免了数据在用户空间和内核空间的来回拷贝

  1. ftok(const char *pathname, int proj_id):

    • ftok()函数用于生成一个唯一的key,用于创建或访问共享内存。
    • 参数pathname是一个路径名,proj_id是一个整数,用于生成key。
    • 返回值:如果成功,返回一个唯一的key,如果失败,返回-1。
  2. shmget(key_t key, size_t size, int shmflg):

    • shmget()函数用于创建共享内存段或获取共享内存段的标识符。
    • 参数key是由ftok()生成的唯一key,size是共享内存的大小,shmflg是标志位,用于指定权限和行为。
    • 返回值:如果成功,返回共享内存段的标识符,如果失败,返回-1。
  3. shmat(int shmid, const void *shmaddr, int shmflg):

    • shmat()函数用于将共享内存连接到当前进程的地址空间。
    • 参数shmid是共享内存段的标识符,shmaddr通常为NULL(表示系统自动选择地址),shmflg是标志位,通常为0。
    • 返回值:如果成功,返回指向共享内存段的指针,如果失败,返回(void *)-1。
  4. shmdt(const void *shmaddr):

    • shmdt()函数用于将共享内存从当前进程的地址空间分离。
    • 参数shmaddr是指向共享内存段的指针。
    • 返回值:如果成功,返回0,如果失败,返回-1。
  5. 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;
}
  1. semget(key_t key, int nsems, int semflg)

    • 这个函数用于创建一个新的信号量集或获取一个现有信号量集的标识符。
    • key是一个用于唯一标识信号量集的键值。
    • nsems指定了信号量集中的信号量数量。
    • semflg是一组标志,用于指定创建信号量集的权限和行为。
    • 返回值:成功时返回信号量集的标识符,失败时返回-1。
  2. semctl(int semid, int semnum, int cmd, union semun arg)

    • 这个函数用于对信号量集进行控制操作,如初始化、设置值、获取值等。
    • semid是信号量集的标识符。
    • semnum是指定的信号量在集合中的索引,通常为0。
    • cmd是指定要执行的控制命令。
    • arg是一个联合体,用于传递控制命令的参数。
    • 返回值:根据控制命令不同而不同。
  3. semop(int semid, struct sembuf *sops, size_t nsops)

    • 这个函数用于执行一组信号量操作,如等待资源(P操作)和释放资源(V操作)。
    • semid是信号量集的标识符。
    • sops是一个指向信号量操作结构体数组的指针。
    • nsops是指定的信号量操作结构体数组的大小。
    • 返回值:成功时返回0,失败时返回-1。
  4. struct sembuf

    • 这是一个结构体,用于描述信号量操作。
    • 它包含了三个字段:
      • sem_num:信号量集中的信号量索引。
      • sem_op:信号量操作,通常是-1(P操作,等待资源)或1(V操作,释放资源)。
      • sem_flg:信号量操作的标志位,通常为0。
  5. union semun

    • 这是一个联合体,用于传递给semctl()函数的参数。
    • 它包含了多个字段,其中的一个字段可以根据不同的控制命令来使用,比如用于设置或获取信号量值时使用的val字段,用于获取信号量状态信息时使用的buf字段等

  1. create_semaphore():

    • 这个函数用于创建一个二值信号量,并将其初始化为1。
    • 首先,它调用 semget() 函数创建一个包含一个信号量的信号量集,如果创建失败则会打印错误信息并退出程序。
    • 然后,它使用 semctl() 函数将信号量的值初始化为1,表示资源可用。
    • 最后,它返回创建的信号量集的标识符。
  2. P(int semid):

    • 这个函数用于执行 P 操作,即等待资源。
    • 首先,它定义了一个 struct sembuf 结构体 op,用于描述信号量操作。
    • 然后,它将 op.sem_num 设置为0,表示信号量集中的第一个信号量。
    • 接着,它将 op.sem_op 设置为-1,表示对信号量执行 P 操作。
    • 最后,它调用 semop() 函数执行信号量操作,如果操作失败则会打印错误信息并退出程序。
  3. 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!
 

在这两个示例代码中,主要使用了以下系统调用:

  1. msgget: 用于创建或打开一个消息队列。它接受一个参数,即消息队列的键值(一个唯一的标识符),并返回一个消息队列的标识符(msgid)。

  2. msgsnd: 用于向消息队列发送消息。它接受四个参数:消息队列的标识符(msgid)、指向要发送的消息的指针、消息的大小(不包括消息类型字段的大小)、消息的标志(通常为0)。

  3. msgrcv: 用于从消息队列接收消息。它接受五个参数:消息队列的标识符(msgid)、指向用于接收消息的缓冲区的指针、缓冲区大小(不包括消息类型字段的大小)、消息类型(通常为正整数)、接收消息的标志(通常为0)。

  4. msgctl: 用于控制消息队列的属性,如删除消息队列。在这个示例中,我们使用它来删除消息队列。它接受三个参数:消息队列的标识符(msgid)、命令(IPC_RMID 表示删除消息队列)、指向用于控制消息队列属性的结构体的指针(在这个例子中不需要)。

六、套接字

网络部分的内容

相关文章:

【Linux】进程间通信IPC机制

目录 一、无名管道 二、有名管道 三、共享内存 四、信号量 五、消息队列 六、套接字 一、无名管道 1.只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程)。 2.是一个单工的通信模式&#xff0c;具有固定的读端和写端。 3.管道也可以看成是一种特殊的文件…...

【如此简单!数据库入门系列】之效率基石 -- 磁盘空间管理

文章目录 1 前言2 磁盘空间管理3 磁盘空间管理的实现4 存储对象关系5 总结6 系列文章 1 前言 如何将表中的记录存储在物理磁盘上呢&#xff1f; 概念模式中&#xff0c;记录&#xff08;Record&#xff09;表示表中的一行数据&#xff0c;由多个列&#xff08;字段或者属性&…...

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(五)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 继续接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 在下面的图片中&#…...

5月7日监控二叉树+斐波那契数

968.监控二叉树 给定一个二叉树&#xff0c;我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 示例 1&#xff1a; 输入&#xff1a;[0,0,null,0,0] 输出&#xff1a;1 解释&#xff…...

C++类的设计编程示例

一、银行账户类 【问题描述】 定义银行账户BankAccount类。 私有数据成员&#xff1a;余额balance&#xff08;整型&#xff09;。 公有成员方法&#xff1a; 无参构造方法BankAccount()&#xff1a;将账户余额初始化为0&#xff1b; 带参构造方法BankAccount(int m)&#xff1…...

YOLOv5 V7.0 - rknn模型的验证 输出精度(P)、召回率(R)、mAP50、mAP50-95

1.简介 RKNN官方没有提供YOLOv5模型的验证工具&#xff0c;而YOLOv5自带的验证工具只能验证pytorch、ONNX等常见格式的模型性能&#xff0c;无法运行rknn格式。考虑到YOLOv5模型转换为rknn会有一定的精度损失&#xff0c;但是需要具体数值才能进行评估&#xff0c;所以需要一个…...

CUDA、CUDNN、Pytorch三者之间的关系

这个东西嘛&#xff0c;我一开始真的是一头雾水&#xff0c;安装起来真是麻烦死了。但是随着要复现的项目越来越多&#xff0c;我也不得不去学会他们是什么&#xff0c;以及他们之间的关系。 首先&#xff0c;一台电脑里面允许有多种版本的cuda存在&#xff0c;然后cuda分为run…...

vue-cli2,vue-cli3,vite 生产环境去掉console.log

console.log一般都是在开发环境下使用的&#xff0c;在生产环境下需要去除 &#xff0c;如果手动删除未免也太累了&#xff0c;我们可以用插件对于具体环境全局处理。 vue-cli2 项目build 下面webpack.prod.config.js 文件中: plugins: [new webpack.DefinePlugin({process.en…...

Docker-Compose编排LNMP并部署WordPress

前言 随着云计算和容器化技术的快速发展&#xff0c;使用 Docker Compose 编排 LNMP 环境已经成为快速部署 Web 应用程序的一种流行方式。LNMP 环境由 Linux、Nginx、MySQL 和 PHP 组成&#xff0c;为运行 Web 应用提供了稳定的基础。本文将介绍如何通过 Docker Compose 编排 …...

附录C:招聘流程

< 回到目录 附录C&#xff1a;招聘流程 _xxx_公司的招聘 使命 只雇佣顶级人才。 他们是能够胜任工作&#xff0c;并与 _&#xff08;你的公司名称&#xff09;_ 的企业文化相匹配的超级明星。 方法 记分卡。招聘经理创建一份文件&#xff0c;详细描述此职位的工作内容…...

1688快速获取整店铺列表 采集接口php Python

在电子商务的浪潮中&#xff0c;1688平台作为中国领先的批发交易平台&#xff0c;为广大商家提供了一个展示和销售商品的广阔舞台&#xff1b;然而&#xff0c;要在众多店铺中脱颖而出&#xff0c;快速获取商品列表并进行有效营销是关键。 竞争对手分析 价格比较&#xff1a;…...

CTF-WEB(MISC)

安全攻防知识——CTF之MISC - 知乎 CTF之MISC杂项从入门到放弃_ctf杂项 你的名字-CSDN博客 CTF MICS笔记总结_archpr 掩码攻击-CSDN博客 一、图片隐写 CTF杂项---文件类型识别、分离、合并、隐写_ctf图片分离-CSDN博客 EXIF&#xff08;Exchangeable Image File&#xff09;是…...

Ubuntu如何更换 PyTorch 版本

环境&#xff1a; Ubuntu22.04 WLS2 问题描述&#xff1a; Ubuntu如何更换 PyTorch 版本考虑安装一个为 CUDA 11.5 编译的 PyTorch 版本。如何安装旧版本 解决方案&#xff1a; 决定不升级CUDA版本&#xff0c;而是使用一个与CUDA 11.5兼容的PyTorch版本&#xff0c;您可…...

python flask css样式无效

解释&#xff1a; Flask是一个Python的轻量级Web框架&#xff0c;它没有为CSS提供任何内置的支持。如果你在Flask项目中引入了CSS文件&#xff0c;但是这个CSS没有生效&#xff0c;可能的原因有&#xff1a; 路径不正确&#xff1a;你的CSS文件没有放在正确的目录下&#xff0…...

大数据学习笔记14-Hive基础2

一、数据字段类型 数据类型 &#xff1a;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中&#xff0c;可以通过使用Local Variable或Property Node来实现主VI控件属性的更改。这些方法可以在主VI和子VI之间传递数据和控件属性。 Local Variable: 使用Local Variable可以在子VI中直接访问并修改主VI中的控件属性。在子VI中创建Local Variable&#xff0c;并…...

关于MS-DOS时代的回忆

目录 一、MS-DOS是什么&#xff1f; 二、MS-DOS的主要功能有哪些&#xff1f; 三、MS-DOS的怎么运行的&#xff1f; 四、微软开源MS-DOS源代码 五、高手与漂亮女同学 一、MS-DOS是什么&#xff1f; MS-DOS&#xff08;Microsoft Disk Operating System&#xff09;是微软公…...

数据库索引(Mysql)

简述:数据库索引是加速数据检索,提高查询效率的一种数据结构 语法规则 创建索引 --通用语法规则 --[内容] 可选参数 --UNIQUE: 可选关键字&#xff0c;用于创建唯一索引&#xff0c;确保索引列的值是唯一的 CREATE [UNIQUE] INDEX 索引名 ON 表名(字段名,...) [ASC | DESC];…...

异常-Exception

异常介绍 基本概念 Java语言中&#xff0c;将程序执行中发生的不正常情况称为“异常”。&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09;执行过程中所发生的异常事件可分为两大类 1&#xff0c;Error&#xff08;错误&#xff09;&#xff1a;Java虚拟机无法…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

【51单片机】4. 模块化编程与LCD1602Debug

1. 什么是模块化编程 传统编程会将所有函数放在main.c中&#xff0c;如果使用的模块多&#xff0c;一个文件内会有很多代码&#xff0c;不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里&#xff0c;在.h文件里提供外部可调用函数声明&#xff0c;其他.c文…...

Java并发编程实战 Day 11:并发设计模式

【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天&#xff0c;今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案&#xff0c;它们不仅提供了优雅的设计思路&#xff0c;还能显著提升系统的性能…...