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

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

1 概述

Linux 进程间通信 (Inter-Process Communication, IPC) 是不同进程之间交换数据与同步操作的机制。现代 Linux 内核提供了多种 IPC 方式,从传统的管道和 System V IPC 到现代的套接字和 D-Bus,每种机制都有其特定的应用场景和性能特征。

根据 IPC 的发展历程和特性,可以将其分为三大类:传统 UNIX IPC (管道、信号)、System V IPC (消息队列、信号量、共享内存) 和 网络 IPC (套接字)。此外,还有一些高级封装机制,如 D-Bus。

2 IPC 机制的工作原理与实现机制

下面通过一个表格快速了解各种 IPC 机制的特点:

机制通信类型血缘关系要求内核持久性主要用途
匿名管道单向字节流父子进程间数据流动
命名管道 (FIFO)单向字节流是 (文件系统)任意进程间数据流动
消息队列结构化消息结构化消息传递
共享内存共享内存区域大规模数据共享
信号量同步原语进程同步与互斥
信号异步事件通知事件通知、进程控制
套接字字节流/数据报网络/本地进程通信
D-Bus结构化消息总线桌面环境与服务通信

2.1 管道(Pipe)

管道是 UNIX 中最古老的 IPC 形式,分为匿名管道命名管道 (FIFO)。

工作原理
管道本质上是一个内核缓冲区,操作方式如同文件(拥有读端和写端),但不像普通文件那样拥有外部路径。匿名管道只能用于具有血缘关系的进程间通信,而命名管道通过文件系统中的一个节点(FIFO 文件)标识,允许无亲缘关系的进程通信。

管道的数据流是单向的。要实现双向通信,通常需要创建两个管道。其生命周期依赖于进程:当所有引用它的进程都终止时,管道资源会被内核回收。

核心数据结构
管道在内核中的核心是 struct pipe_inode_info,它管理着缓冲区的状态、读写位置等信息。

// 管道内核结构示意(简化)
struct pipe_inode_info {struct task_struct *reader;    // 读进程struct task_struct *writer;    // 写进程struct file *files[2];         // 读、写文件指针unsigned int head;             // 缓冲区头指针unsigned int tail;             // 缓冲区尾指针unsigned int max_usage;        // 缓冲区最大使用量unsigned int ring_size;        // 环形缓冲区大小// ... 其他字段如锁、等待队列等
};

代码示例

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>int main() {int pipefd[2];pid_t pid;char buf[256];// 1. 创建管道if (pipe(pipefd) == -1) {perror("pipe");return 1;}// 2. 创建子进程pid = fork();if (pid == -1) {perror("fork");return 1;}if (pid == 0) {     // 子进程 - 作为读取者close(pipefd[1]); // 关闭写端ssize_t num_bytes = read(pipefd[0], buf, sizeof(buf) - 1);if (num_bytes > 0) {buf[num_bytes] = '\0';printf("Child process received: %s\n", buf);} else if (num_bytes == 0) {printf("Child: Pipe closed by parent.\n");} else {perror("read");}close(pipefd[0]); // 关闭读端_exit(0);} else {            // 父进程 - 作为写入者close(pipefd[0]); // 关闭读端const char *msg = "Hello from parent process!";write(pipefd[1], msg, strlen(msg));printf("Parent process sent: %s\n", msg);close(pipefd[1]); // 关闭写端,读者将看到EOFwait(NULL);       // 等待子进程结束}return 0;
}

2.2 System V IPC(消息队列、信号量、共享内存)

System V IPC 包含三种机制:消息队列 (Message Queues)、信号量 (Semaphores) 和共享内存 (Shared Memory)。它们有一些共同特点:都由一个唯一的 IPC 标识符(非负整数)标识,都使用 IPC key 来获取或创建对象,并且都可以设置权限和持久性(内核持续,除非显式删除或系统重启)。

2.2.1 消息队列 (Message Queues)

工作原理
消息队列允许进程以消息(格式化数据块)为单位进行交换。每个消息都有一个类型字段,允许接收者选择性地读取特定类型的消息,从而实现某种程度的优先级处理。

核心数据结构

// 消息队列内核结构示意 (基于搜索结果)
struct msqid_ds {struct ipc_perm msg_perm;   // IPC权限结构time_t          msg_stime;  // 最后发送时间time_t          msg_rtime;  // 最后接收时间time_t          msg_ctime;  // 最后修改时间unsigned long   __msg_cbytes; // 队列中当前字节数msgqnum_t       msg_qnum;   // 队列中消息数msglen_t        msg_qbytes; // 队列最大字节数pid_t           msg_lspid;  // 最后发送消息的PIDpid_t           msg_lrpid;  // 最后接收消息的PID// ... 内部可能链接消息结构
};// 用户态发送消息结构
struct msgbuf {long mtype;       // 消息类型,必须 > 0char mtext[1];    // 消息数据,柔性数组
};

代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MAX_MSG_SIZE 128// 定义消息结构
struct message {long msg_type;char msg_text[MAX_MSG_SIZE];
};int main() {key_t key;int msgid;struct message msg;// 1. 生成唯一的Key (通常使用ftok,这里使用IPC_PRIVATE创建私有队列)// key = ftok("somefile", 'A');// if (key == -1) { ... }// 2. 创建消息队列msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);if (msgid == -1) {perror("msgget");exit(1);}// 3. 写入消息 (子进程)if (fork() == 0) {msg.msg_type = 1; // 设置消息类型strcpy(msg.msg_text, "Hello from Message Queue!");if (msgsnd(msgid, &msg, sizeof(msg.msg_text), 0) == -1) {perror("msgsnd");exit(1);}printf("Child: Message sent.\n");exit(0);}// 4. 读取消息 (父进程)else {wait(NULL); // 等待子进程发送完毕if (msgrcv(msgid, &msg, sizeof(msg.msg_text), 1, 0) == -1) { // 接收类型为1的消息perror("msgrcv");exit(1);}printf("Parent Received: %s\n", msg.msg_text);// 5. 销毁消息队列if (msgctl(msgid, IPC_RMID, NULL) == -1) {perror("msgctl");exit(1);}}return 0;
}
2.2.2 共享内存 (Shared Memory)

工作原理
共享内存是最快的 IPC 形式,因为它允许多个进程将同一块物理内存页映射到它们各自的地址空间。进程可以直接通过对该内存区域的读写进行通信,无需内核在用户态和内核态之间拷贝数据。但其本身不提供同步机制,通常需要与信号量或互斥锁配合使用。

核心数据结构

// 共享内存内核结构示意 (基于搜索结果)
struct shmid_ds {struct ipc_perm shm_perm;    // IPC权限结构size_t          shm_segsz;   // 段大小(字节)time_t          shm_atime;   // 最后附加时间time_t          shm_dtime;   // 最后分离时间time_t          shm_ctime;   // 最后修改时间pid_t           shm_cpid;    // 创建者PIDpid_t           shm_lpid;    // 最后操作PIDshmatt_t        shm_nattch;  // 当前附加数量// ... 其他内部字段
};

代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>#define SHM_SIZE 1024int main() {int shmid;key_t key = IPC_PRIVATE; // 使用私有内存,仅限父子进程char *shm_addr;// 1. 创建共享内存段shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget");exit(1);}// 2. 将共享内存附加到进程地址空间shm_addr = (char *)shmat(shmid, NULL, 0);if (shm_addr == (char *)(-1)) {perror("shmat");exit(1);}// 3. 父子进程通过共享内存通信if (fork() == 0) { // Child Processprintf("Child writing to shared memory...\n");strcpy(shm_addr, "Hello from Shared Memory!");shmdt(shm_addr); // 分离共享内存exit(0);} else {            // Parent Processwait(NULL);     // Wait for child to writeprintf("Parent reads: %s\n", shm_addr);// 分离共享内存shmdt(shm_addr);// 标记删除共享内存段。// 当所有进程都分离后,内核才会真正销毁它。shmctl(shmid, IPC_RMID, NULL);}return 0;
}

下面是共享内存的工作原理示意图,展示了多个进程如何映射到同一块物理内存:

物理内存
通过页表映射
通过页表映射
通过页表映射
映射到
映射到
映射到
共享内存段
进程 1
虚拟地址空间 1
进程 2
虚拟地址空间 2
进程 3
虚拟地址空间 3
2.2.3 信号量 (Semaphores)

工作原理
System V 信号量是一个内核维护的计数器,用于为多个进程提供对共享资源的互斥访问或同步。其值始终大于或等于 0。P 操作(等待)尝试减少信号量的值,如果值大于 0 则立即减少,否则进程阻塞。V 操作(发送信号)增加信号量的值,并唤醒等待的进程。System V 信号量集允许同时操作多个信号量。

核心数据结构

// 信号量内核结构示意 (基于搜索结果)
struct semid_ds {struct ipc_perm sem_perm;  // IPC权限结构time_t          sem_otime; // 最后操作时间time_t          sem_ctime; // 最后修改时间unsigned short  sem_nsems; // 集合中信号量数量// ... 指向信号量集合的指针等内部字段
};// 单个信号量的内部结构示意
struct sem {int semval;  // 信号量的当前值pid_t sempid; // 最后操作该信号量的PID// ... 可能包含等待队列等
};

代码示例(生产者-消费者问题简化模型)

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>// 联合体,用于semctl初始化
union semun {int val;struct semid_ds *buf;unsigned short *array;
};// P操作 - 等待信号量
void P(int semid, int sem_num) {struct sembuf op;op.sem_num = sem_num;op.sem_op = -1; // 减1op.sem_flg = SEM_UNDO; // 防止进程异常终止导致死锁if (semop(semid, &op, 1) == -1) {perror("semop P");exit(1);}
}// V操作 - 发送信号量
void V(int semid, int sem_num) {struct sembuf op;op.sem_num = sem_num;op.sem_op = 1; // 加1op.sem_flg = SEM_UNDO;if (semop(semid, &op, 1) == -1) {perror("semop V");exit(1);}
}int main() {int shmid, semid;key_t key = IPC_PRIVATE;int *buffer;union semun arg;// 1. 创建共享缓冲区(一个整数)shmid = shmget(key, sizeof(int), IPC_CREAT | 0666);buffer = (int *)shmat(shmid, NULL, 0);*buffer = 0; // 初始化缓冲区// 2. 创建包含一个信号量的集合semid = semget(key, 1, IPC_CREAT | 0666);// 3. 初始化信号量值为1(互斥锁)arg.val = 1;if (semctl(semid, 0, SETVAL, arg) == -1) {perror("semctl SETVAL");exit(1);}// 4. 创建子进程(消费者)if (fork() == 0) {for (int i = 0; i < 5; i++) {P(semid, 0); // 获取锁printf("Consumer reads: %d\n", (*buffer));(*buffer)--;V(semid, 0); // 释放锁sleep(1);}shmdt(buffer);exit(0);}// 父进程(生产者)else {for (int i = 0; i < 5; i++) {P(semid, 0); // 获取锁(*buffer)++;printf("Producer writes: %d\n", (*buffer));V(semid, 0); // 释放锁sleep(1);}wait(NULL);// 清理资源shmdt(buffer);shmctl(shmid, IPC_RMID, NULL);semctl(semid, 0, IPC_RMID);}return 0;
}

2.3 信号 (Signal)

工作原理
信号是进程间通信机制中唯一的异步机制。用于通知目标进程某个特定事件已经发生(如 SIGKILLSIGTERM 用于终止进程,SIGSEGV 用于段错误)。进程可以忽略(部分信号不可忽略)、捕获(注册信号处理函数)或使用默认行为处理信号。

代码示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>// 信号处理函数
void sigusr1_handler(int sig) {write(STDOUT_FILENO, "Child received SIGUSR1!\n", 24);
}int main() {pid_t pid;pid = fork();if (pid == -1) {perror("fork");exit(1);}if (pid == 0) {     // 子进程signal(SIGUSR1, sigusr1_handler); // 注册信号处理函数pause(); // 暂停,等待信号printf("Child exiting.\n");exit(0);} else {            // 父进程sleep(1);       // 给子进程时间注册处理函数printf("Parent sending SIGUSR1 to child...\n");kill(pid, SIGUSR1); // 向子进程发送信号wait(NULL);     // 等待子进程结束}return 0;
}

2.4 套接字 (Socket)

工作原理
套接字最初是为网络通信设计的,但也支持本地进程间通信(Unix Domain Socket, UDP)。它提供了一个标准接口,支持面向连接的流(TCP/SOCK_STREAM)和无连接的数据报(UDP/SOCK_DGRAM)模式。Unix Domain Socket 通过文件系统路径名标识,效率高于网络套接字。

代码示例(流式套接字 - TCP)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h> // For Unix domain sockets
#include <sys/wait.h>
#include <unistd.h>#define SOCK_PATH "/tmp/example_socket"int main() {int server_fd, client_fd;struct sockaddr_un server_addr, client_addr;socklen_t client_len;char buf[256];// 1. 创建套接字server_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd == -1) {perror("socket");exit(1);}// 2. 绑定地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;strncpy(server_addr.sun_path, SOCK_PATH, sizeof(server_addr.sun_path) - 1);unlink(SOCK_PATH); // 确保路径可用if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind");exit(1);}// 3. 监听if (listen(server_fd, 5) == -1) {perror("listen");exit(1);}if (fork() == 0) { // Client (Child)sleep(1); // Give server time to set upint sockfd = socket(AF_UNIX, SOCK_STREAM, 0);struct sockaddr_un addr;memset(&addr, 0, sizeof(addr));addr.sun_family = AF_UNIX;strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("client connect");exit(1);}const char *msg = "Hello over Unix Socket!";write(sockfd, msg, strlen(msg));printf("Client sent: %s\n", msg);close(sockfd);exit(0);} else {        // Server (Parent)client_len = sizeof(client_addr);client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);if (client_fd == -1) {perror("accept");exit(1);}ssize_t num_bytes = read(client_fd, buf, sizeof(buf) - 1);if (num_bytes > 0) {buf[num_bytes] = '\0';printf("Server received: %s\n", buf);}close(client_fd);close(server_fd);unlink(SOCK_PATH); // Clean upwait(NULL);}return 0;
}

2.5 D-Bus

工作原理
D-Bus 是一个高级的消息总线系统,主要用于桌面环境(如 Linux)中应用程序之间的通信和系统服务的暴露。它提供基于消息的通信模式,支持方法调用、信号发射和属性访问等特性。D-Bus 通常运行在两个层级:系统总线(用于系统范围的服务)和会话总线(用于单个用户会话中的应用)。

核心数据结构
D-Bus 的核心是 DBusMessage 结构,它封装了通信的消息。

// D-Bus 消息结构示意 (基于搜索结果)
struct DBusMessage {DBusHeader        header;     // 消息头(类型、路径、接口等)DBusString        body;       // 消息体(数据负载)DBusList         *arguments;  // 解析后的参数列表(可选)dbus_uint32_t     serial;     // 消息序列号(唯一标识)// ... 其他内部字段(如内存池、引用计数等)
};

3 IPC 机制的内核实现框架

Linux 内核为了管理 IPC 资源,引入了命名空间 (ipc_namespace) 和权限控制等机制。

3.1 IPC 命名空间 (ipc_namespace)

struct ipc_namespace 隔离了不同命名空间中的 IPC 资源,使得容器技术成为可能。它包含了各种 IPC 机制(信号量、消息队列、共享内存)的状态信息。

// IPC命名空间结构示意 (基于搜索结果)
struct ipc_namespace {atomic_t count;         // 引用计数struct ipc_ids ids[3];  // 分别管理信号量、消息队列、共享内存// ... 其他每种IPC特有的控制参数(如大小限制等)
};

3.2 通用的 IPC ID 管理 (ipc_ids)

struct ipc_ids 用于管理某一类 IPC 对象的集合。

// IPC ID 管理结构示意 (基于搜索结果)
struct ipc_ids {int in_use;                       // 当前使用的对象数量unsigned short seq, seq_max;      // 序列号,用于生成用户可见的IDstruct rw_semaphore rw_mutex;     // 保护内部结构的读写信号量struct idr ipcs_idr;              // 用于ID和内核IPC对象指针映射的radix树
};

3.3 通用的 IPC 对象权限 (kern_ipc_perm)

每个 IPC 对象(信号量、消息队列、共享内存)都包含一个 struct kern_ipc_perm 结构,用于存储权限和所有权信息。

// IPC 权限结构示意 (基于搜索结果)
struct kern_ipc_perm {key_t key;              // 用户提供的键,用于标识IPC对象uid_t uid;              // 所有者的用户IDgid_t gid;              // 所有者的组IDuid_t cuid;             // 创建者的用户IDgid_t cgid;             // 创建者的组IDumode_t mode;           // 权限模式(读写执行)unsigned long seq;      // 序列号// ... 其他内部字段
};

下面是 IPC 内核管理框架的简化图示,展示了核心数据结构之间的关系:

IPC对象实例
管理
管理
管理
包含
包含
包含
kern_ipc_perm
for 信号量
信号量对象
kern_ipc_perm
for 消息队列
消息队列对象
kern_ipc_perm
for 共享内存
共享内存对象
ipc_namespace
ipc_ids array
key, uid, gid, mode...

4 调试工具和手段

4.1 命令行工具

  • ipcs:查看系统当前的 IPC 资源状态。

    ipcs -a   # 显示所有IPC信息
    ipcs -m   # 显示共享内存信息
    ipcs -q   # 显示消息队列信息
    ipcs -s   # 显示信号量信息
    
  • ipcrm:删除指定的 IPC 资源。

    ipcrm -m <shmid>  # 删除共享内存段
    ipcrm -q <msqid>  # 删除消息队列
    ipcrm -s <semid>  # 删除信号量集
    
  • lsof:列出当前系统打开的文件,可用于查看套接字、管道等。

    lsof | grep pipe  # 查找管道文件
    lsof | grep <socket_path> # 查找Unix域套接字
    
  • nc (netcat):用于调试套接字通信,尤其是网络套接字和 Unix 域套接字。

    # 连接到Unix域套接字 (Stream类型)
    nc -U /path/to/socket# 监听Unix域套接字 (Datagram类型)
    nc -lU /path/to/socket
    
  • strace / truss:跟踪进程执行时的系统调用和信号,非常适合诊断 IPC 问题。

    strace -f -e trace=ipc,pipeline,read,write <command> # 跟踪IPC相关的系统调用
    strace -p <PID>                                      # 跟踪一个正在运行的进程
    

4.2 实战调试技巧

  1. 消息队列阻塞:使用 ipcs -q 查看消息队列状态,注意 CBYTES(当前队列中的字节数)和 QNUM(消息数量)。如果数字很大,可能有进程没有及时接收消息。

  2. 共享内存同步问题:如果数据出现不一致,首先怀疑同步机制(信号量/锁)是否正确。使用 strace 跟踪相关进程的 semop 调用,观察 P/V 操作是否成对出现。

  3. 信号未处理:使用 kill -l 查看信号列表。用 strace 跟踪进程可以看到它收到和处理的信号。

  4. 管道破裂 (SIGPIPE):当向一个读端已关闭的管道写入时,内核会向写入进程发送 SIGPIPE 信号(默认终止进程)。确保处理或忽略此信号,或者检查 write 的返回值。

  5. 资源泄漏:定期使用 ipcs 检查未被清理的 IPC 资源。在程序结束时(如 atexit 或析构函数中)或在信号处理函数中确保释放资源。

5 总结

Linux 提供了一整套丰富而强大的进程间通信机制,每种机制都有其独特的优势和适用场景:

  • 简单数据流传输:优先考虑管道(有亲缘关系)或FIFO(无亲缘关系)。
  • 高性能大规模数据共享共享内存是不二之选,但必须精心设计同步机制(信号量、文件锁等)。
  • 结构化消息传递消息队列D-Bus(在桌面环境中)更为合适。
  • 进程同步与控制信号量用于同步,信号用于异步事件通知。
  • 网络通信或本地跨语言通信套接字提供了最广泛的支持和标准化接口。

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

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

相关文章

TensorFlow-GPU版本安装

前言&#xff1a; &#xff08;1&#xff09;因项目需求&#xff0c;需要安装TensorFlow-GPU版本&#xff0c;故本文在此记录安装过程。 &#xff08;2&#xff09;有注释&#xff0c;优先看注释 &#xff08;3&#xff09;本文所使用的GPU为NVIDIA GeForce RTX 5080 Laptop GP…

Elasticsearch 索引字段删除,除了 Reindex 重建索引还有没有别的解决方案?

unsetunset1、问题来源unsetunset在生产环境维护 Elasticsearch 集群的过程中&#xff0c;经常会遇到这样的场景&#xff1a;业务需求变更导致某些字段不再使用&#xff0c;或者早期设计时添加了一些冗余字段&#xff0c;现在需要清理掉。最近球友在公司的一个项目中就遇到了这…

Ubuntu虚拟机磁盘空间扩展指南

这是一份详细且易于理解的 Ubuntu 虚拟机磁盘空间扩展指南。本指南涵盖了两种主流虚拟机软件&#xff08;VirtualBox 和 VMware&#xff09;的操作步骤&#xff0c;并分为 “扩展虚拟磁盘” 和 “在 Ubuntu 内部分配新空间” 两大部分。重要提示&#xff1a;在进行任何磁盘操作…

教程1:用vscode->ptvsd-创建和调试一个UI(python)-转载官方翻译(有修正)

vscode用python开发maya联动调试设置 3dsMax Python开发环境搭建 3文联动之debugpy调试max‘python. 3文联动之socket插槽注入max‘python 本教程是max主动接收创建代码的方式&#xff08;预先运行界面&#xff0c;通过按钮主动读取py脚本&#xff0c;执行断点&#xff09;&…

龙迅#LT7621GX适用于两路HDMI2.1/DP1.4A转HDMI2.1混切应用,分辨率高达8K60HZ!

1. 描述LT7621GX是一款高性能两路HDMI2.1/DP1.4转HDMI2.1混合开关芯片&#xff0c;用于显示应用。 HDCP RX作为HDCP中继器的上游&#xff0c;可以与其他芯片的HDCP TX配合&#xff0c;实现中继器功能。 对于HDMI2.1输入&#xff0c;LT7621GX可以配置为3/4通道。自适应均衡使其适…

【Ruoyi 解密 - 12. JDK17的新特性】------ 从Java 8 到 Java 17:向Scala看齐的“简洁革命”,同宗JVM下的效率狂飙

从Java 8到Java 17&#xff1a;抄作业Scala&#xff1f;JVM同宗下的Ruoyi开发效率狂飙&#xff01; 上一篇我们聊到JDK 17对Python的柔性借鉴&#xff0c;可深入用下来才发现——这哪够&#xff01;对Ruoyi开发者来说&#xff0c;JDK 17真正的“王炸”&#xff0c;是把同根JVM的…

大模型 “轻量化” 之战:从千亿参数到端侧部署,AI 如何走进消费电子?

一、大模型 “轻量化” 的行业背景在 AI 技术蓬勃发展的当下&#xff0c;大模型已然成为行业焦点。从 GPT-4 突破万亿级参数量&#xff0c;到 DeepSeek-R1 迈向千亿参数规模&#xff0c;大模型的参数扩张趋势显著。然而&#xff0c;这种规模的增长也带来了诸多挑战。以 GPT-4 为…

香港电讯与Microsoft香港推出新世代“Teams Phone” 解决方案

香港电讯成为香港首家提供 “Microsoft Operator Connect”的本地电讯营运商1 香港电讯&#xff08;股份代号&#xff1a;6823&#xff09;【香港 • 2025年2月11日】 – 香港电讯宣布与 Microsoft 香港合作推出 “Operator Connect”&#xff0c;成为全港首家为企业客户提供全…

PlantUML描述《分析模式》第3章观察和测量(2)

lantUML描述《分析模式》第2章“当责”&#xff08;1&#xff09; PlantUML描述《分析模式》第2章“当责”&#xff08;2&#xff09; PlantUML描述《分析模式》第3章观察和测量&#xff08;1&#xff09; 原图3.8 EA绘制 图3.8 递归关系用于记录证据和评估。 PlantUML sta…

轮廓周长,面积,外界圆,外界矩形近似轮廓和模板匹配和argparse模块实现代码参数的动态配置

目录 一.轮廓操作 1.轮廓特征的引入与筛选 2.轮廓排序和精准定位 3.外接圆与外接矩形的计算与绘制 二.轮廓近似 1.轮廓近似的基本概念 2.轮廓近似的实现方法和核心步骤 3. 近似精度参数的设定逻辑 4.轮廓定位方法 三.模板匹配 1.模板匹配技术原理与实现流程 2.技术要…

【第三方网站测评:会话管理漏洞的测试与加固】

会话管理是Web应用安全的用于在无状态的HTTP协议上维持用户状态。漏洞主要源于会话令牌(Session Token)的生成、传输、验证和销毁过程中的缺陷。攻击者利用这些缺陷可劫持用户会话,未经授权访问敏感数据或执行特权操作,属于OWASP TOP 10中身份验证失效的高频风险。 会话管…

理想汽车智驾方案介绍专题 3 MoE+Sparse Attention 高效结构解析

一、前言 【理想汽车智驾方案介绍专题 -1】端到端VLM 方案介绍 【理想汽车智驾方案介绍专题 -2】MindVLA 方案详解 在上述两篇系列帖子中&#xff0c;笔者已对理想汽车 VLM 和 VLA 方案的框架进行了全面介绍&#xff0c;但对于其中的前沿技术仅做了初步探讨&#xff0c;未进…

如何将yolo训练图像数据库的某个分类的图像取出来

COCO 数据集 - Ultralytics YOLO 文档 比如我只想从数据集中取手机的图像&#xff0c;来用于我的训练&#xff0c;懒得自己一张一张标注&#xff0c;方法如下 # -*- coding: utf-8 -*- import json import os import shutil from pathlib import Path from tqdm import tqdm i…

【WPF】WPF 自定义控件实战:从零打造一个可复用的 StatusIconTextButton (含避坑指南)

&#x1f527; WPF 自定义控件实战&#xff1a;从零打造一个可复用的 StatusIconTextButton&#xff08;含避坑指南&#xff09;发布于&#xff1a;2025年8月29日 标签&#xff1a;WPF、C#、自定义控件、MVVM、Generic.xaml、属性绑定、TemplateBinding&#x1f4cc; 引言 在 W…

中国国际商会副秘书长徐梁一行到访国联股份

2025年08月27日&#xff0c;中国国际商会副秘书长徐梁等一行到访国联股份&#xff0c;国联股份创始人、CEO/总裁钱晓钧&#xff0c;国联股份副总裁、卫多多/纸多多CEO黄莎莎等热情招待来访一行&#xff0c;并展开深入交流。来访一行首先参观了国联股份数字经济展厅&#xff0c;…

换公司如何快速切入软件项目工程

一、前言 作为程序员&#xff0c;根据自身职业发展&#xff0c;会通过跳槽谋求更进一步的发展&#xff0c;这时进入新公司&#xff0c;接触全新的项目工程和业务&#xff0c;如何快速的切入&#xff0c;形成认识呢&#xff1f;就算不跳槽&#xff0c;公司业务调整&#xff0c;也…

Linux系统——EXT2 文件系统

磁盘文件 文件属性 文件内容文件内容 —— 数据块&#xff0c;文件属性 —— inodeLinux 文件在磁盘中的存储&#xff0c;是将 属性 与 内容 分开存储的内存&#xff1a;掉电易失&#xff0c;磁盘&#xff1a;永久性存储介质图片来自百度磁盘访问的基本单元&#xff1a;扇区 …

Qt中的锁(1)

Qt中的锁&#xff08;1&#xff09; 加锁&#xff0c;把多个要访问的公共资源通过锁保护起来&#xff0c;把并行执行变成串行执行&#xff0c; 多个线程执行加锁的对象得是同一个对象&#xff0c;不同对象不会互斥 代码&#xff1a;//添加一个static成员static int num;//创建锁…

数据结构 02(线性:顺序表)

目录 线性表 顺序表 概念与结构 动态顺序表的实现 头文件的创建 顺序表初始化 顺序表的扩容 尾插功能 头插功能 尾删功能 头删功能 查找功能 任意位置前插入 任意位置前删除 销毁 动态顺序表整体呈现 SeqList.h SeqList.c 线性表 线性表是n个具有相同特性的数…

自助餐厅:自主取餐的平衡术

自助餐厅&#xff0c;本质是通过 “固定客单价 自主取餐” 的模式&#xff0c;把 “吃什么、吃多少” 的选择权还给用户&#xff0c;同时用运营设计平衡 “用户体验” 与 “餐厅成本”—— 它不是 “让用户吃垮餐厅” 的游戏&#xff0c;而是餐饮行业里 “效率与体验结合” 的…