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

Linux 进程间通信(IPC)详解

进程间通信(IPC)深入解析

一、进程间通信概述

在操作系统里,不同进程间常常需要进行数据交换、同步协调等操作,进程间通信(Inter - Process Communication,IPC)机制应运而生。在Linux系统中,常见的IPC机制有管道、信号量、共享内存、消息队列和套接字。这些机制各自具备独特的特性,适用于不同的应用场景,并且在各类系统和应用程序开发中得到广泛应用,也是技术面试中的重点考查内容。

思考问题1:为什么需要进程间通信?

在多进程的环境下,各个进程通常是独立运行的,但有些情况下,它们需要协同工作。比如,一个进程负责收集用户输入,另一个进程负责对这些输入进行处理,这就需要两个进程之间进行数据传递。另外,当多个进程需要访问同一资源时,为了避免冲突,就需要通过进程间通信来进行同步和协调。

思考问题2:不同的IPC机制适用于哪些场景?

不同的IPC机制有不同的特点和适用场景。例如,管道适合简单的父子进程间的数据传递;信号量主要用于进程间的同步和互斥;共享内存适合大量数据的快速传输;消息队列适用于需要按消息类型分类处理数据的场景;套接字则常用于网络环境下的进程间通信。

二、管道

2.1 管道分类

管道分为有名(命名)管道和无名管道。

有名管道

有名管道也叫命名管道,它以文件的形式存在于文件系统中,不同进程可以通过这个文件进行通信,即便这些进程没有亲缘关系。

mkfifo fifo		# 创建一个叫做fifo的管道

创建完成后,使用ls -l命令查看文件类型,会看到文件类型为p,这代表该文件是一个管道文件。

无名管道

无名管道是通过系统调用pipe创建的,它没有对应的文件系统实体,只能用于具有亲缘关系的进程(如父子进程)之间的通信。

2.2 管道的特点和阻塞行为

数据存储

管道大小在文件系统层面显示为零,但数据实际上是存储在内存中的。管道本质上是内核中的一块缓冲区,用于临时存储要传输的数据。

打开条件

读打开和写打开的进程必须同时打开管道。若只有读进程打开管道,读操作会阻塞,直到有写进程打开并写入数据;若只有写进程打开管道,写操作也会阻塞,直到有读进程打开管道读取数据。

读阻塞

读打开的进程在管道没有数据时会阻塞,直到有数据被写入管道。当所有写端关闭且管道中的数据都被读完后,读操作会返回0,表示已到达文件末尾。

写关闭处理

写打开的进程关闭管道时,读打开的进程会返回零。此时读进程知道写进程已经停止写入数据,可以结束读取操作。

2.3 如何使用管道在两个进程之间传递数据?

第一步,创建一个管道文件

使用mkfifo命令创建一个命名管道文件,例如:

mkfifo fifo
第二步,创建两个进程,a.cb.c
//a.c用来往管道里面写数据:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int fd = open("./fifo", O_WRONLY);if (fd == -1){perror("open");exit(1);}printf("fd=%d\n", fd);while (1){printf("input:\n");char buff[128] = {0};fgets(buff, 128, stdin);if (strncmp(buff, "end", 3) == 0){break;}write(fd, buff, strlen(buff));}close(fd);exit(0);
}
//b.c用来在管道里面收数据:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int fd = open("./fifo", O_RDONLY);if (fd == -1){perror("open");exit(1);}printf("fd=%d\n", fd);while (1){char buff[128] = {0};int n = read(fd, buff, 127);if (n == 0){break;}printf("buff = %s\n", buff);}close(fd);exit(0);
}
第三步,同时打开两个文件,进行通信

当使用管道进行数据传输时,必须有两个进程同时打开这个管道文件,一个负责读,一个负责写,否则不能正常打开。在打开文件后,当写进程没有进行写操作前,读进程将会阻塞。

管道的特点是读打开和写打开的进程可以循环读写数据。当管道文件被关闭后,读操作返回值为0,可以作为读进程结束的条件。

2.4 无名管道

无名管道通过pipe来创建。其实现原理是需要提供一个整型数组,数组的两个元素分别作为读端和写端的文件描述符。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int main()
{int fd[2];if (pipe(fd) == -1){perror("pipe");exit(1);}// 父进程写,子进程读pid_t pid = fork();if (pid == -1){perror("fork");exit(1);}if (pid == 0){close(fd[1]);while (1){char buff[128] = {0};if (read(fd[0], buff, 127) == 0){break;}printf("child read:%s\n", buff);}close(fd[0]);}else{close(fd[0]);while (1){printf("input:\n");char buff[128] = {0};fgets(buff, 128, stdin);if (strncmp(buff, "end", 3) == 0){break;}write(fd[1], buff, strlen(buff));}close(fd[1]);}exit(0);
}

2.5 有名管道和无名管道的区别

  • 通信范围:无名管道只能在父子进程之间通信,而有名管道可以在任意两个进程之间通信。
  • 存在形式:无名管道没有对应的文件系统实体,而有名管道以文件的形式存在于文件系统中。

2.6 管道的通信方式

管道的通信方式是半双工,即能发送和接收数据,但不能同时进行发送和接收操作。

2.7 写入管道的数据位置

写入管道的数据存储在内存中。不使用文件进行数据传递是因为使用文件进行传递涉及到I/O操作,效率较低,而管道直接在内存中操作,数据传输速度更快。

2.8 管道的实现原理

假设管道有一个分配的内存空间,将其划分为一个字节一个字节的单元,有两个操作这块空间的指针。用size来表示管道的总大小,设一个头指针和一个尾指针指向管道的起始位置。写入数据时,头指针往后移动,指向待写入的下一个位置;读数据时,读掉尾指针所在位置的数据,然后尾指针往后移动。只要尾指针赶上头指针,说明管道中的数据已读完。等到头指针指到内存最末尾时,会循环到起始地址。管道的内存是有限的,在没读掉的位置不能写入数据,就像一个循环队列一样。

思考问题3:管道的缓冲区大小有限制吗?如果数据量超过缓冲区大小会怎样?

管道的缓冲区大小是有限制的,不同的系统可能有不同的默认值,通常为几KB到几十KB不等。当数据量超过缓冲区大小时,写操作会阻塞,直到有足够的空间可以继续写入数据。这是为了防止数据溢出,保证数据的有序传输。

思考问题4:管道在多进程环境下可能会出现哪些问题?如何解决?

在多进程环境下,管道可能会出现数据竞争、死锁等问题。例如,多个写进程同时向管道写入数据可能会导致数据混乱;如果读进程和写进程的操作不协调,可能会出现死锁。解决这些问题可以使用同步机制,如信号量、互斥锁等,来协调进程对管道的访问。

2.9 写端关闭和读端关闭的处理

当写端关闭时,读端read()会返回0;当读关闭时,写端write()会异常终止(触发SIGPIPE信号)。

三、信号量

3.1 PV操作

信号量通常是一个正数值,一般代表可用资源的数目。对信号量的操作主要有PV操作。

  • P操作:获取资源,对信号量的值减一。如果减一后信号量的值小于0,进程会进入阻塞状态,等待其他进程释放资源。
  • V操作:释放资源,对信号量的值加一。如果加一后信号量的值小于等于0,说明有进程在等待该资源,会唤醒一个等待的进程。

3.2 相关概念

  • 临界资源:一次仅允许一个进程使用的共享资源,如打印机、共享内存区域等。
  • 临界区:访问临界资源的代码段。为了保证临界资源的正确使用,需要对临界区进行保护,防止多个进程同时访问。

3.3 信号量的操作步骤

  1. 创建 初始化:使用semget函数创建信号量集,并使用semctl函数进行初始化。
  2. P操作,获取资源:使用semop函数执行P操作,获取对临界资源的访问权限。
  3. V操作,释放资源:使用semop函数执行V操作,释放对临界资源的访问权限。PV操作没有严格的先后顺序,要根据当时的使用需求来决定。
  4. 删除信号量:使用semctl函数删除信号量集。

3.4 信号量的操作和接口

信号量的主要操作函数有semget(创建)、semctl(控制,初始化)和semop(PV操作)。

//sem.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>union semun 
{int val;    
};void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
//sem.c
#include "sem.h"
static int semid = -1;void sem_init()
{semid = semget((key_t)1234, 1, IPC_CREAT | IPC_EXCL | 0600);if (semid == -1){semid = semget((key_t)1234, 1, IPC_CREAT | 0600);if (semid == -1){perror("semget");return;}}else{union semun a;a.val = 1;if (semctl(semid, 0, SETVAL, a) == -1) // SETVAL表示初始化值{perror("semctl setval");}}
}void sem_p()
{struct sembuf buf;buf.sem_num = 0; // 信号量的下标buf.sem_op = -1;buf.sem_flg = SEM_UNDO; // 这个标志位表示着当操作发生异常时由内核释放资源,避免资源一直占用if (semop(semid, &buf, 1) == -1){perror("semop p");}
}void sem_v()
{struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1;buf.sem_flg = SEM_UNDO; // 这个标志位表示着当操作发生异常时由内核释放资源,避免资源一直占用if (semop(semid, &buf, 1) == -1){perror("semop v");}
}void sem_destroy()
{if (semctl(semid, 0, IPC_RMID) == -1) // IPC_RMID表示删除{perror("semctl destroy");}    
}

3.5 创建两个进程使用信号量进行资源调用

//a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sem.h"int main()
{sem_init();for (int i = 0; i < 5; i++){sem_p();printf("A");fflush(stdout);int n = rand() % 3;sleep(n);printf("A");fflush(stdout);n = rand() % 3;sleep(n);sem_v();}return 0;
}
//b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sem.h"int main()
{sem_init();for (int i = 0; i < 5; i++){sem_p();printf("B");fflush(stdout);int n = rand() % 3;sleep(n);printf("B");fflush(stdout);n = rand() % 3;sleep(n);sem_v();}sleep(10);sem_destroy();return 0;
}

思考问题5:信号量的值可以为负数吗?负数代表什么含义?

信号量的值可以为负数。当信号量的值为负数时,其绝对值表示正在等待该资源的进程数量。例如,信号量的值为 -2,表示有两个进程正在等待该资源的释放。

思考问题6:如果在使用信号量时忘记释放资源(即没有执行V操作)会怎样?

如果忘记执行V操作,信号量的值不会增加,其他等待该资源的进程将一直处于阻塞状态,无法获取资源,从而导致死锁或资源饥饿问题。因此,在使用信号量时,必须确保在适当的时候执行V操作,释放资源。

四、共享内存

4.1 共享内存的原理和优势

共享内存是一种高效的进程间通信方式,它允许不同进程直接访问同一块物理内存区域,避免了数据的多次拷贝,从而提高了数据传输的效率。
在这里插入图片描述

//a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>int main()
{// 创建共享内存int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid == -1){perror("shmget");exit(1);}// 获取共享内存char* s = (char*)shmat(shmid, NULL, 0);if (s == (char*)-1){perror("shmat");exit(1);}while (1){char buff[128] = {0};fgets(buff, 128, stdin);strcpy(s, buff);if (strncmp(buff, "end", 3) == 0){break;}}// 分离共享内存shmdt(s);exit(0);
}
//b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>int main()
{// 创建共享内存int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid == -1){perror("shmget");exit(1);}// 获取共享内存char* s = (char*)shmat(shmid, NULL, 0);if (s == (char*)-1){perror("shmat");exit(1);}while (1){if (strncmp(s, "end", 3) == 0){break;}printf("s=%s", s);sleep(1);}// 分离共享内存shmdt(s);// 销毁共享内存shmctl(shmid, IPC_RMID, NULL);exit(0);
}

4.2 存在问题及改进方案

上述代码存在问题:当没有输入值的时候,b.c会循环打印当时的共享内存中的值。改进方案是使用信号量,让两个程序不能同时访问临界资源。

4.3 共享内存和信号量的使用

需要使用信号量的两个文件sem.csem.h,跟之前不同的是之前使用了一个信号量,这次需要使用两个。

//sem.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>union semun 
{int val;    
};void sem_init();
void sem_p(int index);
void sem_v(int index);
void sem_destroy();
//sem.c
#include "sem.h"
static int semid = -1;void sem_init()
{semid = semget((key_t)1234, 2, IPC_CREAT | IPC_EXCL | 0600);if (semid == -1){semid = semget((key_t)1234, 2, IPC_CREAT | 0600);if (semid == -1){perror("semget");return;}}else{union semun a;int arr[2] = {1, 0};for (int i = 0; i < 2; i++){a.val = arr[i];if (semctl(semid, i, SETVAL, a) == -1) // SETVAL表示初始化值{perror("semctl setval");}            }        }
}void sem_p(int index)
{struct sembuf buf;buf.sem_num = index; // 信号量的下标buf.sem_op = -1;buf.sem_flg = SEM_UNDO; // 这个标志位表示着当操作发生异常时由内核释放资源,避免资源一直占用if (semop(semid, &buf, 1) == -1){perror("semop p");}
}void sem_v(int index)
{struct sembuf buf;buf.sem_num = index;buf.sem_op = 1;buf.sem_flg = SEM_UNDO; // 这个标志位表示着当操作发生异常时由内核释放资源,避免资源一直占用if (semop(semid, &buf, 1) == -1){perror("semop v");}
}void sem_destroy()
{if (semctl(semid, 0, IPC_RMID) == -1) // IPC_RMID表示删除{perror("semctl destroy");}    
}
//a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include "sem.h"int main()
{// 创建共享内存int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid == -1){perror("shmget");exit(1);}// 映射共享内存char* s = (char*)shmat(shmid, NULL, 0);if (s == (char*)-1){perror("shmat");exit(1);}sem_init();while (1){printf("input:\n");char buff[128] = {0};fgets(buff, 128, stdin);sem_p(0); // s1,第一个信号量,初始值为1strcpy(s, buff);sem_v(1); // s2,第二个信号量,初始值为0if (strncmp(buff, "end", 3) == 0){break;}}// 分离共享内存shmdt(s);exit(0);
}
//b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include "sem.h"int main()
{// 创建共享内存int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0600);if (shmid == -1){perror("shmget");exit(1);}// 获取共享内存char* s = (char*)shmat(shmid, NULL, 0);if (s == (char*)-1){perror("shmat");exit(1);}sem_init();while (1){sem_p(1);if (strncmp(s, "end", 3) == 0){break;}printf("s=%s", s);sem_v(0);}sem_destroy();// 断开共享内存映射shmdt(s);// 销毁shmctl(shmid, IPC_RMID, NULL);exit(0);
}

4.4 共享内存和管道的区别

共享内存对物理内存进行操作,管道对内核缓冲区进行操作,二者存在以下不同:

内存位置与管理方式
  • 共享内存:共享内存是在物理内存中开辟一块特定的区域,通过操作系统的内存管理机制,将其映射到不同进程的虚拟地址空间中。这样,多个进程可以直接访问同一块物理内存,实现数据共享。操作系统负责管理共享内存的分配与回收,进程通过系统调用如shmgetshmat等来请求和使用共享内存。例如,在多个进程需要频繁共享大量数据,如数据库系统中多个查询进程可能需要共享数据缓存时,就可以使用共享内存。
  • 管道:管道的内核缓冲区是由操作系统在内核空间中分配的一块内存区域,用于暂存管道两端进程间传输的数据。它的大小通常有一定限制,并且由操作系统自动管理其数据的进出和空间利用。当进程向管道写入数据时,数据被复制到内核缓冲区;读进程从管道读取数据时,再从内核缓冲区复制到用户空间。例如,在ls | grep这样的命令组合中,ls命令的输出通过管道传输到grep命令,中间的数据就是暂存在管道的内核缓冲区中。
数据访问特性
  • 共享内存:进程可以直接对共享内存进行读写操作,就像访问自己的内存一样,速度非常快,因为不需要进行数据在用户空间和内核空间之间的复制。但是,由于多个进程可以同时访问共享内存,为了保证数据的一致性和完整性,需要使用同步机制,如信号量、互斥锁等,来协调进程对共享内存的访问。否则,可能会出现数据竞争等问题。
  • 管道:管道的读写是有方向的,数据只能从写端流向读端。写进程将数据写入内核缓冲区,读进程从内核缓冲区读取数据。当管道满时,写进程会被阻塞,直到有数据被读走,腾出空间;当管道空时,读进程会被阻塞,直到有数据写入。这种机制保证了数据的有序传输,但也意味着数据的读写是顺序进行的,不能像共享内存那样随机访问。
数据可见性与持续性
  • 共享内存:一旦数据被写入共享内存,只要其他进程有访问权限,就可以立即看到更新后的数据,数据在共享内存中的存在是持续性的,直到被显式修改或删除。即使所有使用共享内存的进程都暂时退出,共享内存中的数据仍然存在于物理内存中,只要没有被操作系统回收或其他进程修改,下次进程再访问时,数据依然保持原来的状态。
  • 管道:管道中的数据具有临时性和一次性的特点。当数据被读进程从内核缓冲区读取后,数据就从管道中消失了,其他进程无法再次读取到相同的数据。而且,当所有与管道相关的文件描述符都被关闭后,管道所占用的内核缓冲区资源会被操作系统自动释放,其中的数据也会被清除。

思考问题7:共享内存可能会带来哪些安全隐患?如何防范?

共享内存可能带来的安全隐患包括数据泄露、数据被恶意篡改等。由于多个进程可以直接访问共享内存,若没有适当的权限控制和加密机制,敏感数据可能会被其他进程获取或修改。防范措施包括设置合理的访问权限,对共享内存中的数据进行加密处理,以及使用同步机制确保数据的一致性和完整性。

思考问题8:如果多个进程同时对共享内存进行写操作会怎样?

如果多个进程同时对共享内存进行写操作,会导致数据竞争问题,可能会使共享内存中的数据变得混乱,出现数据不一致的情况。为了避免这种情况,需要使用同步机制,如信号量、互斥锁等,来保证同一时间只有一个进程可以对共享内存进行写操作。

五、消息队列

5.1 消息队列的特点

添加消息和读取消息是消息队列的基本操作。消息是一个结构体,结构体名字由自己定义,特殊的地方是第一个成员是长整型(代表消息的类型),且该类型的值至少为1。

为什么消息类型必须大于零?

长整型如果是0号,就是不区分消息类型,在函数调用中,要是传入0的话,无论是什么消息类型都能读到。这样可以根据不同的消息类型对消息进行分类处理,提高消息处理的灵活性和效率。

5.2 消息队列的接口函数

  1. msgget():创建或获取消息队列。
int msgget(key_t key, int msgflg);

key是消息队列的键值,用于唯一标识一个消息队列;msgflg是标志位,用于指定创建方式和权限等。

  1. msgrcv():读取消息。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msqid是消息队列的标识符;msgp是用于存储接收到的消息的缓冲区;msgsz是缓冲区的大小;msgtyp是期望接收的消息类型;msgflg是标志位,用于指定操作方式。

  1. msgsnd():发送消息。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数含义与msgrcv类似。

5.3 示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/msg.h>struct mess
{long type;char buff[128];
};int main()
{int msgid = msgget((key_t)1234, IPC_CREAT | 0600);if (msgid == -1){perror("msgget");exit(1);}struct mess m;m.type = 1;strcpy(m.buff, "hello");if (msgsnd(msgid, &m, sizeof(m.buff), 0) == -1){perror("msgsnd");exit(1);}exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/msg.h>struct mess
{long type;char buff[128];
};int main()
{int msgid = msgget((key_t)1234, IPC_CREAT | 0600);if (msgid == -1){perror("msgget");exit(1);}struct mess m;if (msgrcv(msgid, &m, sizeof(m.buff), 1, 0) == -1){perror("msgrcv");exit(1);}printf("read:%s\n", m.buff);exit(0);
}

思考问题9:消息队列的大小有限制吗?如果消息队列满了会怎样?

消息队列的大小是有限制的,不同的系统可能有不同的默认值。当消息队列满了时,后续的msgsnd操作会阻塞,直到队列中有空间可以容纳新的消息。这是为了防止消息队列溢出,保证消息的有序存储和处理。

思考问题10:消息队列在分布式系统中有哪些应用场景?

在分布式系统中,消息队列可以用于异步通信、任务调度、解耦服务等场景。例如,在一个电商系统中,订单服务在处理订单时可以将订单信息发送到消息队列中,库存服务、物流服务等可以从消息队列中获取订单信息并进行相应的处理,这样可以提高系统的并发处理能力和可扩展性。

六、IPC管理命令

6.1 ipcs

ipcs命令可以查看消息队列、共享内存、信号量的使用情况。例如:

  • ipcs -m:查看共享内存的使用信息。
  • ipcs -q:查看消息队列的使用信息。
  • ipcs -s:查看信号量的使用信息。

6.2 ipcrm

使用ipcrm命令可以进行删除操作。手动移除的命令格式为ipcrm -s/m/q +id,分别用于删除信号量、共享内存、消息队列。例如:

  • ipcrm -m 1234:删除ID为1234的共享内存段。
  • ipcrm -q 5678:删除ID为5678的消息队列。
  • ipcrm -s 9012:删除ID为9012的信号量集。

通过这些命令,系统管理员可以方便地管理系统中的IPC资源,确保系统的稳定运行。

思考问题11:在使用ipcrm命令删除IPC资源时需要注意什么?

在使用ipcrm命令删除IPC资源时,需要确保该资源不再被其他进程使用。如果在其他进程还在使用该资源时删除,可能会导致这些进程出现异常,甚至崩溃。因此,在删除之前,需要先确认相关进程已经停止使用该资源,或者采取适当的同步机制来确保安全删除。

思考问题12:如何定期清理系统中的IPC资源,以避免资源浪费?

可以编写脚本,结合ipcs命令查看IPC资源的使用情况,根据一定的规则(如资源的使用时间、是否有进程关联等)筛选出不再使用的资源,然后使用ipcrm命令进行删除。还可以设置定时任务,定期执行该脚本,以确保系统中的IPC资源得到及时清理。

相关文章:

Linux 进程间通信(IPC)详解

进程间通信&#xff08;IPC&#xff09;深入解析 一、进程间通信概述 在操作系统里&#xff0c;不同进程间常常需要进行数据交换、同步协调等操作&#xff0c;进程间通信&#xff08;Inter - Process Communication&#xff0c;IPC&#xff09;机制应运而生。在Linux系统中&a…...

第3章 Python 3 基础语法001

文章目录 一、缩进规则1. 基本规则2. 示例3. 多级缩进4. 常见错误二、注释规则1. 单行注释2. 多行注释3. 特殊注释4. 注释规范三、代码块规则1. 控制结构2. 函数定义3. 类定义4. 上下文管理器四、总结与最佳实践五、调试技巧以下是 Python 3 基础语法规则的详细说明,涵盖 缩进…...

数据库介绍以及windows下mysql安装

文章目录 1. 前言2. MySQL概述2.1 相关概念2.2 DBMS的分类2.3 数据库交互图2.4 MySQL 介绍 3. MySQL 安装 数据库介绍以及windows下mysql安装 1. 前言 我们浏览的淘宝商品页面详情、刷视频网站的一个个视频&#xff0c;这些数据其实都是存储在公司的存储系统中的。想象一下&…...

list的两种设计

1. 内存布局对比 (1) MSVC 的实现 cpp class _List_node {_List_node* _Next; // 指向下一个节点_List_node* _Prev; // 指向前一个节点_Value_type _Value; // 存储的数据 }; 特点&#xff1a; 每个节点包含两个指针和一个数据成员。 Debug 模式&#xff1a;可能添加迭代…...

【C#】一个类中的接口方法使用static和不使用static的区别

在C#中&#xff0c;类中的接口方法是否使用 static 修饰符会带来显著的区别。这是因为接口方法的实现和调用方式与普通方法不同&#xff0c;而 static 关键字的使用进一步改变了这些行为。 以下是两者的区别&#xff1a; 1. 不使用 static 的接口方法 在这种情况下&#xff0…...

共铸价值:RWA 联合曲线价值模型,撬动现实资产生态

摘要 本文提出了一种针对真实资产&#xff08;RWA&#xff09;产业的联合曲线激励模型&#xff0c;将劳动与数据贡献映射为曲线价值&#xff0c;并基于固定档位与指数衰减奖励发放总计 2.1亿积分。该模型结合了去中心化定价与平滑递减机制&#xff0c;不仅为早期贡献者提供更高…...

【libuv】基于libuv的exe链接错误

vs2017构建 基于libuv的exe链接错误 1>libuv.lib(util.obj) : error LNK2019: unresolved external symbol __imp__GetAdaptersAddresses20 referenced in function _uv_interface_addresses 1>libuv.lib(util.obj) : error LNK2019: unresolved external symbol __imp__…...

什么是生成式 AI (GenAI)?

在科技飞速发展的今天,人工智能(AI)已不再是一个遥远的概念,而是悄然融入了我们的日常生活。从智能语音助手到自动驾驶汽车,从个性化推荐系统到医疗诊断辅助,AI正以前所未有的速度改变着世界。然而,在AI的广阔领域中,有一个分支正逐渐崭露头角,成为推动未来创新的关键…...

爬虫准备前工作

1.Pycham的下载 网址&#xff1a;PyCharm: The only Python IDE you need 2.Python的下载 网址&#xff1a;python.org&#xff08;python3.9版本之后都可以&#xff09; 3.node.js的下载 网址&#xff1a;Node.js — 在任何地方运行 JavaScript&#xff08;版本使用18就可…...

JVM——JVM 是如何处理异常的?

JVM 是如何处理异常的&#xff1f; 在 Java 编程语言中&#xff0c;异常处理是一种强大的机制&#xff0c;用于应对程序运行时出现的错误和意外情况。而 Java 虚拟机&#xff08;JVM&#xff09;作为 Java 程序运行的核心环境&#xff0c;在异常处理过程中扮演着至关重要的角色…...

网络基础-----C语言经典题目(12)

一、MTU&#xff0c;IP 协议头中 TTL是什么&#xff1f; MTU 指的是网络层能够接收的最大数据包大小&#xff0c;单位为字节。主要作用是限制数据链路层一次能够传输的数据量。 IP 协议头中的 TTL 是 IP 数据头部的一个 8 位字段&#xff0c;最初它的设计目的是限制数据包在网络…...

【第十六届蓝桥杯省赛】比赛心得与经验分享(PythonA 组)

文章目录 一、我的成绩二、我的备赛经历三、如何备赛&#xff08;个人观点&#xff09;1. 基础语法2. 数据结构3. 算法4. 数学 四、做题技巧与注意事项五、我的题解试题A 偏蓝 &#x1f3c6;100%试题B IPV6 &#x1f3c6;0%试题C 2025图形 &#x1f3c6;100%试题D 最大数字 &am…...

解决Maven项目中报错“java不支持版本6即更高的版本 7”

错误背景 当Maven项目编译或运行时出现错误提示 Java不支持版本6即更高的版本7&#xff0c;通常是由于项目配置的JDK版本与当前环境或编译器设置不一致导致的。例如&#xff1a; 项目配置的Java版本为6或7&#xff0c;但实际使用的是JDK 17。Maven或IDE的编译器未正确指定目标…...

MySQL--索引入门

MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构。 Mysql在存储数据之外&#xff0c;数据库系统各种还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种引用&#xff08;指向&#xff09;表中的数据…...

【网络原理】深入理解HTTPS协议

本篇博客给大家带来的是网络原理的知识点, 由于时间有限, 分三天来写, 本篇为线程第三篇,也是最后一篇. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动…...

利用Elixir中的原子特性 + 错误消息泄露 -- Atom Bomb

题目信息: This new atom bomb early warning system is quite strange… 题目使用 elixir 语言 一开始,我们会访问 /page.html <!DOCTYPE html> <!-- 设定文档语言为英语 --> <html lang"en"> <head><!-- 设定字符编码为UTF-8 --><…...

机器人--STM32

STM32启动模式 1,从主闪存存储启动器启动(默认) 2,从系统存储启动器启动 下载程序时需要使用的启动方式。 3&#xff0c;从内置的SRAM启动...

LVGL -文本显示 英文、中文

1 文本 在 LVGL 中,文本控件(Label)是一种基本的 UI 组件,用于显示文本信息。文本控件可以用于各种场景,如显示状态信息、提示消息、标题等。在图形用户界面(GUI)开发中,文本是传达信息和指导用户的重要组成部分。为了有效地展示文本,以下是与文本相关的几个关键方面…...

Java面试资源获取

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 **1. GitHub开源项目****2. 技术博客与社区*…...

探索 Spring AI 的 ChatClient API:构建智能对话应用的利器

探索 Spring AI 的 ChatClient API&#xff1a;构建智能对话应用的利器 前言 在当今人工智能蓬勃发展的时代&#xff0c;智能对话系统成为了众多应用的核心组成部分。无论是客服机器人、智能助手还是聊天应用&#xff0c;都离不开高效、灵活的对话处理能力。Spring AI 作为 S…...

Java大师成长计划之第11天:Java Memory Model与Volatile关键字

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4o-mini模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 在多线程编程中&#xff0c;线程的执…...

java学习之数据结构:一、数组

主要是对数组所有的东西进行总结&#xff0c;整理 适合小白~ 目录 1.什么是数组 1.1数组定义 1.2数组创建 1&#xff09;静态创建 2&#xff09;动态创建 1.3数组遍历 1&#xff09;for和while遍历 2&#xff09;foreach遍历 2.数组越界问题及解决 2.1数组越界问题 2…...

Oracle OCP认证考试考点详解083系列04

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 16. 第16题&#xff1a; 题目 解析及答案&#xff1a; 关于使用恢复管理器&#xff08;RMAN&#xff09;恢复表&#xff0c;以下哪三项是…...

MARM:推荐系统中的记忆增强突破

文章目录 1. 背景1.1 模型规模与推荐系统的挑战1.2 缓存技术在推荐系统中的潜力1.3 推荐系统中的数据与计算需求1.4 复杂度对比1.5 MARM模型的创新性 2. 方法2.1 流程2.1.1 序列生成器2.1.2 外部缓存查找2.1.3 多目标注意力机制2.1.4 发结果到缓存 **2.2 MARM与SIM**2.2.1 SIM的…...

INP指标

什么是INP&#xff08;Interaction to Next Paint&#xff09; 参考网站&#xff1a;webVital-INP文档 定义与核心目标 INP 是一项稳定的 Core Web Vitals 指标&#xff0c;通过统计用户访问期间所有符合条件的互动约定时间&#xff0c;评估网页对用户操作的总体响应能力。最…...

Flink 的状态机制

在实时流处理领域&#xff0c;状态管理是构建复杂业务逻辑的核心能力。Apache Flink 通过统一的状态抽象和高效的容错机制&#xff0c;为开发者提供了从毫秒级窗口聚合到 TB 级历史数据关联的全场景支持。本文将深入剖析 Flink 状态机制的底层原理&#xff0c;结合实际案例展示…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】1.1 数据库核心概念与PostgreSQL技术优势

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 深度解析PostgreSQL核心架构与技术优势&#xff1a;从数据库原理到实战场景1.1 数据库核心概念与PostgreSQL技术优势1.1.1 关系型数据库核心架构解析1.1.1.1 数据库系统的底…...

linux下,ollama会把模型文件保存在哪里?

文章目录 运行ollama,有两种形式,估计得分开讨论首先是使用自动启动的ollama:先跑个“小一点的大模型”但是现在模型文件存在哪儿呢?运行ollama,有两种形式,估计得分开讨论 我们用两种方式,来运行ollama。 首先是使用自动启动的ollama: ps -aux | grep ollama系统自…...

EMMC存储性能测试方法

记于 2022 年 9 月 15 日 EMMC存储性能测试方法 - Wesley’s Blog 参考Android-emmc性能测试 | 一叶知秋进行实践操作 dd 命令 页面缓存 为了测试 emmc 的真实读写性能&#xff0c;我们需要先把页面缓存给清理&#xff1a; echo 1 > /proc/sys/vm/drop_caches console:…...

19. LangChain安全与伦理:如何避免模型“幻觉“与数据泄露?

引言&#xff1a;当AI成为企业"数字员工"时的责任边界 2025年某金融机构因AI客服泄露用户信用卡信息被罚款2300万美元。本文将基于LangChain的安全架构与Deepseek-R1的合规实践&#xff0c;揭示如何构建既强大又安全的AI系统。 一、AI安全风险矩阵 1.1 2025年最新威…...

5月3日日记

上午睡到自然醒&#xff08;其实六点多被我爸叫起来抢火车票&#xff0c;发现明天中午的软卧候补上了&#xff0c;挺好的&#xff09;然后继续睡到快10点。 中午吃的什么来着&#xff0c;好像是西红柿炒鸡蛋和藜麦饭&#xff0c;有个鱼不是很想吃就没吃 中午打了两把吃鸡&…...

C++类_构造函数

在 C11 里&#xff0c;类的构造函数有多种类型&#xff0c;下面为你详细介绍各类构造函数并给出示例代码。 1. 默认构造函数 默认构造函数是没有参数的构造函数&#xff0c;要是没有为类定义任何构造函数&#xff0c;编译器会自动生成一个默认构造函数。 2. 带参数的构造函数…...

【React】Hooks useReducer 详解,让状态管理更可预测、更高效

1.背景 useReducer是React提供的一个高级Hook,没有它我们也可以正常开发&#xff0c;但是useReducer可以使我们的代码具有更好的可读性&#xff0c;可维护性。 useReducer 跟 useState 一样的都是帮我们管理组件的状态的&#xff0c;但是呢与useState不同的是 useReducer 是集…...

Runnable组件重试机制降低程序错误率

一、LangChain 重试机制深度解析 当构建生产级AI应用时&#xff0c;with_retry() 机制可有效提升系统容错性&#xff0c;典型应用场景包括&#xff1a; API调用频率限制时的自动恢复模型服务临时不可用的故障转移网络波动导致的瞬时异常处理 参数详解与配置策略 1. 参数配置…...

纹理过滤方式和纹理包裹方式

纹理过滤方式 纹理过滤方式有临近过滤&#xff08;Nearest&#xff09;和双线性插值过滤&#xff08;Linear&#xff09;&#xff0c;什么时候用什么过滤方式其实看个人选择&#xff0c;区别就是临近过滤是当需要的像素大于图片像素时候&#xff0c;一些像素点需要复用与他相近…...

55.[前端开发-前端工程化]Day02-包管理工具npm等

包管理工具详解 npm、yarn、cnpm、npx、pnpm 1 npm包管理工具 代码共享方案 包管理工具npm 2 package配置文件 npm的配置文件 方式二 常见的配置文件 常见的属性 常见的属性 常见的属性 依赖的版本管理 常见属性 npm install 命令 项目安装 3 npm install原理 npm instal…...

Maven安装配置以及Idea中的配置教程

一、下载Maven 我使用的是3.9.9的版本&#xff1a; 下载地址&#xff1a;Download Apache Maven – Maven 二、安装 将下载好的Maven压缩包解压到一个路径不包含中文的文件夹&#xff1a; 三、配置环境变量 以win11系统为例&#xff1a; 1.鼠标右键此电脑->属性->…...

【JavaScript】性能优化:打造高效前端应用

文章目录 一、执行效率优化(关键路径优化)1.1 算法时间复杂度控制1.2 Web Workers多线程计算二、内存管理(避免内存泄漏)2.1 定时器清理2.2 DOM引用释放三、DOM操作优化(渲染性能)3.1 批量DOM更新3.2 读写分离策略四、网络传输优化(加载性能)4.1 代码分割(Dynamic Imp…...

【C语言练习】018. 定义和初始化结构体

018. 定义和初始化结构体 018. 定义和初始化结构体1. 定义结构体示例1:定义一个简单的结构体输出结果2. 初始化结构体示例2:在声明时初始化结构体输出结果示例3:使用指定初始化器初始化结构体(C99及以上标准支持)输出结果3. 结构体数组示例4:定义和初始化结构体数组输出结…...

Three.js支持模型格式区别、建议

在 Three.js 中,3D 模型的种类和格式非常多样,每种格式都有其适用场景和优缺点。以下是常见的 Three.js 支持的模型格式、它们的区别、使用建议及推荐。 在这里推荐免费的blender工具,免费、占用空间不大,而且好用,前端打开模型时使用不错,或者有自己想做的模型也可以用它…...

JavaScript基础-流程控制概念

在JavaScript编程中&#xff0c;掌握如何控制程序的执行流程是编写功能强大、逻辑清晰代码的关键。流程控制语句允许我们根据不同的条件执行不同的代码块&#xff0c;或者重复执行某些操作&#xff0c;从而实现复杂的功能逻辑。本文将详细介绍JavaScript中的几种主要流程控制结…...

PowerBI企业运营分析——多维度日期指标分析

PowerBI企业运营分析——多维度日期指标分析 欢迎来到Powerbi小课堂&#xff0c;在竞争激烈的市场环境中&#xff0c;企业运营分析平台成为提升竞争力的核心工具。 通过整合多源数据、实时监控关键指标&#xff0c;该平台能够精准分析业务表现&#xff0c;快速识别问题与机会…...

施磊老师rpc(三)

文章目录 mprpc框架项目动态库编译框架生成动态库框架初始化函数-文件读取1. 为什么要传入 argc, argv2. 读取参数逻辑3. 配置文件设计 init部分实现 mprpc配置文件加载(一)配置文件加载类成员变量主要方法**src/include/mprpcconfig.h** 配置文件**bin/test.conf** 实现配置文…...

k8s 探针

Kubernetes 中的探针&#xff08;Probes&#xff09;用于检测容器的健康状态或就绪状态&#xff0c;确保应用在运行时的可靠性。Kubernetes 提供三种探针类型&#xff0c;它们的核心区别在于用途和失败后的处理逻辑。以下是它们的详细说明和对比&#xff1a; 1. 启动探针&…...

MIT6.S081-lab8

MIT6.S081-lab8 1. Large files 从 lecture 我们可以知道&#xff0c;我们目前的单个文件的最大大小很小&#xff0c;这是因为我们能够索引的索引块范围很小&#xff0c;实际上&#xff0c;目前的索引只有直接索引和一级索引&#xff0c;而这个实验就是需要我们去实现二级索引…...

【RabbitMQ】 RabbitMQ快速上手

文章目录 一、RabbitMQ 核心概念1.1 Producer和Consumer2.2 Connection和Channel2.3 Virtual host2.4 Queue2.5 Exchange2.6 RabbitMQ工作流程 二、AMQP协议三 、web界面操作4.1 用户相关操作4.2 虚拟主机相关操作 四、RabbitMQ快速入门4.1 引入依赖4.2 编写生产者代码4.2.1 创…...

使用Rust + WebAssembly提升前端渲染性能:从原理到落地

一、问题背景&#xff1a;为什么选择WebAssembly&#xff1f; 最近在开发数据可视化大屏项目时&#xff0c;我们遇到了一个棘手的问题&#xff1a;前端需要实时渲染10万数据点的动态散点图&#xff0c;使用纯JavaScript Canvas方案在低端设备上帧率不足15FPS。经过性能分析&a…...

【quantity】9 长度单位模块(length.rs)

代码是用Rust语言定义的一组长度单位类型&#xff0c;利用了泛型和类型别名来创建带不同SI前缀的长度量。下面是详细解释&#xff1a; 基础结构&#xff1a; 使用了Quantity<V, P, Meter>作为基础类型&#xff0c;表示一个带有值类型V、前缀P和单位Meter的量。 Meter是…...

网络通信领域的基础或流行协议

一、TCP(传输控制协议) 1. 宏观介绍 TCP:全称“Transmission Control Protocol”——传输控制协议,是互联网最基础的传输协议之一。传输层协议,提供面向连接、可靠的字节流传输服务。它通过三次握手建立连接、四次挥手断开连接,确保数据有序、完整地传输作用:让两个设备…...

STM32——GPIO

1、GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口 可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff0c;部分引脚可容忍5V 输出模式下可控制端口输出高低电平&#xff0c;用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 …...