news 2026/1/10 8:06:23

应用——Linux进程通信与信号处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
应用——Linux进程通信与信号处理

Linux进程通信与信号处理

一、命名管道(FIFO)通信

1.1 FIFO通信机制概述

FIFO(命名管道)是一种特殊的文件类型,它允许无亲缘关系的进程间进行通信。FIFO在文件系统中有一个路径名,进程通过打开这个文件来进行读写操作。

1.2 FIFO实现双向聊天程序

🔹 FIFO写端程序(A进程)
/* A进程(写端) */
#include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /* 线程1:向B进程发送消息 */ void* th1(void* arg) { int fd = *(int*)arg; // 获取FIFO1的写端文件描述符 while (1) { char buf[100] = {0}; printf("to B:"); // 提示输入要发送给B的消息 fgets(buf, sizeof(buf), stdin); // 从标准输入读取消息 /* write参数说明: * fd: FIFO文件描述符 * buf: 要发送的数据缓冲区 * strlen(buf) + 1: 发送字符串长度+1(包含'\0') * 发送'\0'确保对方能正确识别字符串结束 */ write(fd, buf, strlen(buf) + 1); /* 检查退出条件: * strcmp比较输入是否为"#quit\n" * 注意:fgets会包含换行符'\n' */ if (0 == strcmp(buf, "#quit\n")) { exit(0); // 退出整个进程 } } return NULL; } /* 线程2:从B进程接收消息 */ void* th2(void* arg) { int fd = *(int*)arg; // 获取FIFO2的读端文件描述符 while (1) { char buf[100] = {0}; /* read参数说明: * fd: FIFO文件描述符 * buf: 接收数据的缓冲区 * sizeof(buf): 缓冲区大小 * 返回值:实际读取的字节数 */ read(fd, buf, sizeof(buf)); /* 检查退出条件: * 如果收到"#quit\n"消息,则退出程序 */ if (0 == strcmp(buf, "#quit\n")) { exit(0); } printf("from B:%s", buf); // 显示从B接收到的消息 fflush(stdout); // 强制刷新输出缓冲区,确保及时显示 } return NULL; } /* 主函数 */ int main(int argc, char** argv) { /* 创建两个FIFO文件: * myfifo1: A写 -> B读 * myfifo2: B写 -> A读 * 权限0666: rw-rw-rw- */ int ret1 = mkfifo("myfifo1", 0666); int ret2 = mkfifo("myfifo2", 0666); /* 错误处理: * EEXIST错误:FIFO已存在,可以继续使用 * 其他错误:创建失败,退出程序 */ if (-1 == ret1 || -1 == ret2) { if (EEXIST == errno) // FIFO已存在,可以继续使用 { } else // 其他错误 { perror("mkfifo"); return 1; } } /* 打开FIFO文件: * O_WRONLY: 只写方式打开myfifo1(A写) * O_RDONLY: 只读方式打开myfifo2(A读) * 注意:open会阻塞,直到另一端也被打开 */ int fd_w = open("myfifo1", O_WRONLY); if (-1 == fd_w) { perror("open fd_w"); return 1; } int fd_r = open("myfifo2", O_RDONLY); if (-1 == fd_r) { perror("open fd_r"); return 1; } /* 创建两个线程: * tid1: 负责发送消息(th1) * tid2: 负责接收消息(th2) * 通过arg参数传递文件描述符 */ pthread_t tid1, tid2; pthread_create(&tid1, NULL, th1, &fd_w); pthread_create(&tid2, NULL, th2, &fd_r); /* 等待线程结束: * pthread_join会阻塞,直到线程结束 */ pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }
🔹 FIFO读端程序(B进程)
/*- B进程(读端) */
#include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /* 线程1:向A进程发送消息 */ void* th1(void* arg) { int fd = *(int*)arg; // 获取FIFO2的写端文件描述符 while (1) { char buf[100] = {0}; printf("to A:"); // 提示输入要发送给A的消息 fgets(buf, sizeof(buf), stdin); write(fd, buf, strlen(buf) + 1); // 向A发送消息 if (0 == strcmp(buf, "#quit\n")) { exit(0); } } return NULL; } /* 线程2:从A进程接收消息 */ void* th2(void* arg) { int fd = *(int*)arg; // 获取FIFO1的读端文件描述符 while (1) { char buf[100] = {0}; read(fd, buf, sizeof(buf)); // 从A接收消息 if (0 == strcmp(buf, "#quit\n")) { exit(0); } printf("from A:%s", buf); // 显示从A接收到的消息 fflush(stdout); } return NULL; } /* 主函数 */ int main(int argc, char** argv) { /* 创建FIFO文件: * 虽然A进程已经创建,但这里也创建一次确保存在 * 如果已存在,EEXIST错误被忽略 */ int ret1 = mkfifo("myfifo1", 0666); int ret2 = mkfifo("myfifo2", 0666); if (-1 == ret1 || -1 == ret2) { if (EEXIST == errno) { } else { perror("mkfifo"); return 1; } } /* 打开FIFO文件: * O_RDONLY: 只读方式打开myfifo1(B读) * O_WRONLY: 只写方式打开myfifo2(B写) * 注意:打开顺序与A进程相反 */ int fd_r = open("myfifo1", O_RDONLY); if (-1 == fd_r) { perror("open fd_r"); return 1; } int fd_w = open("myfifo2", O_WRONLY); if (-1 == fd_w) { perror("open fd_w"); return 1; } /* 创建线程 */ pthread_t tid1, tid2; pthread_create(&tid1, NULL, th1, &fd_w); // th1使用写端 pthread_create(&tid2, NULL, th2, &fd_r); // th2使用读端 /* 等待线程结束 */ pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }

1.3 FIFO通信要点总结

项目说明
FIFO创建使用mkfifo("name", mode)创建,在文件系统中可见
打开方式O_RDONLY(只读)、O_WRONLY(只写)
阻塞特性open会阻塞直到另一端也被打开
通信方向单向通信,需要两个FIFO实现双向
进程关系无亲缘关系的进程也可通信
退出机制通过"#quit"消息协调退出

二、共享内存通信

2.1 共享内存通信机制

共享内存是最快的IPC方式,因为它直接在内存中操作,不需要内核缓冲区的复制。

2.2 共享内存写端程序

/* 共享内存写端 */
#include <sys/types.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/shm.h> int main(int argc, char *argv[]) { /* ftok函数:生成System V IPC键值 * 参数1:路径名(必须存在且可访问) * 参数2:项目ID(低8位有效) * 返回值:生成的key_t类型键值 * * 注意:两个进程使用相同的参数才能得到相同的key */ key_t key = ftok("./",'!'); if(-1 == key) { perror("ftok"); return 1; } printf("0x%x\n",key); // 打印生成的键值(16进制) /* shmget函数:创建/获取共享内存段 * 参数1:IPC键值 * 参数2:共享内存大小(字节) * 参数3:标志位 * IPC_CREAT: 如果不存在则创建 * 0666: 权限(rw-rw-rw-) * 返回值:共享内存标识符 */ int shmid = shmget(key,4096,IPC_CREAT|0666); if(-1 == shmid) { perror("shmget"); return 1; } printf("shmid is %d\n",shmid); /* shmat函数:将共享内存映射到进程地址空间 * 参数1:共享内存标识符 * 参数2:指定映射地址(NULL表示系统自动分配) * 参数3:标志位 * !SHM_RDONLY: 0,表示可读写 * SHM_RDONLY: 只读映射 * 返回值:映射后的内存地址 */ void* p = shmat(shmid,NULL,!SHM_RDONLY); if((void *) -1 == p) { perror("shmat"); return 1; } /* 向共享内存写入数据 */ char buf[1024]="hello"; /* strcpy和memcpy的区别: * strcpy: 复制字符串,遇到'\0'停止 * memcpy: 按字节复制,指定长度 * 这里使用memcpy确保完全复制 */ //strcpy((char*)p,buf); // 方法1:使用strcpy memcpy(p,buf,strlen(buf)+1); // 方法2:使用memcpy(包含'\0') /* shmdt函数:解除共享内存映射 * 参数:映射地址 * 注意:只解除映射,不删除共享内存 */ shmdt(p); return 0; }

2.3 共享内存读端程序

/*共享内存读端 */
#include <sys/types.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/shm.h> int main(int argc, char *argv[]) { /* 生成与写端相同的键值 */ key_t key = ftok("./",'!'); if(-1 == key) { perror("ftok"); return 1; } printf("0x%x\n",key); /* 获取已存在的共享内存段 * 注意:这里不需要IPC_CREAT,因为写端已经创建 * 但如果写端未创建,会失败 */ int shmid = shmget(key,4096,IPC_CREAT|0666); if(-1 == shmid) { perror("shmget"); return 1; } printf("shmid is %d\n",shmid); /* 映射共享内存(可读写方式) */ void* p = shmat(shmid,NULL,!SHM_RDONLY); if((void *) -1 == p) { perror("shmat"); return 1; } /* 从共享内存读取数据并打印 */ printf("mem:%s\n",(char*)p); /* 解除映射 */ shmdt(p); /* 删除共享内存段(可选) * IPC_RMID: 删除共享内存 * 注意:实际使用时可能需要延迟删除 */ // shmctl(shmid,IPC_RMID,NULL); return 0; }#include <sys/types.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/shm.h> int main(int argc, char *argv[]) { /* 生成与写端相同的键值 */ key_t key = ftok("./",'!'); if(-1 == key) { perror("ftok"); return 1; } printf("0x%x\n",key); /* 获取已存在的共享内存段 * 注意:这里不需要IPC_CREAT,因为写端已经创建 * 但如果写端未创建,会失败 */ int shmid = shmget(key,4096,IPC_CREAT|0666); if(-1 == shmid) { perror("shmget"); return 1; } printf("shmid is %d\n",shmid); /* 映射共享内存(可读写方式) */ void* p = shmat(shmid,NULL,!SHM_RDONLY); if((void *) -1 == p) { perror("shmat"); return 1; } /* 从共享内存读取数据并打印 */ printf("mem:%s\n",(char*)p); /* 解除映射 */ shmdt(p); /* 删除共享内存段(可选) * IPC_RMID: 删除共享内存 * 注意:实际使用时可能需要延迟删除 */ // shmctl(shmid,IPC_RMID,NULL); return 0; }#include <sys/types.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/shm.h> int main(int argc, char *argv[]) { /* 生成与写端相同的键值 */ key_t key = ftok("./",'!'); if(-1 == key) { perror("ftok"); return 1; } printf("0x%x\n",key); /* 获取已存在的共享内存段 * 注意:这里不需要IPC_CREAT,因为写端已经创建 * 但如果写端未创建,会失败 */ int shmid = shmget(key,4096,IPC_CREAT|0666); if(-1 == shmid) { perror("shmget"); return 1; } printf("shmid is %d\n",shmid); /* 映射共享内存(可读写方式) */ void* p = shmat(shmid,NULL,!SHM_RDONLY); if((void *) -1 == p) { perror("shmat"); return 1; } /* 从共享内存读取数据并打印 */ printf("mem:%s\n",(char*)p); /* 解除映射 */ shmdt(p); /* 删除共享内存段(可选) * IPC_RMID: 删除共享内存 * 注意:实际使用时可能需要延迟删除 */ // shmctl(shmid,IPC_RMID,NULL); return 0; }

2.4 共享内存操作流程

图表

2.5共享内存函数对比

函数功能参数说明返回值
ftok生成IPC键值(路径, 项目ID)key_t类型键值
shmget创建/获取共享内存(key, 大小, 标志)共享内存ID
shmat映射共享内存(shmid, 地址, 标志)映射地址
shmdt解除映射(映射地址)成功0/失败-1
shmctl控制操作(shmid, cmd, buf)成功0/失败-1

三、管道通信

3.1 父子进程字典查询程序

/* 管道实现字典查询 */
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define MAXLINE 19661 // 字典文件最大行数 int main(int argc, char **argv) { /* 创建管道: * fd[0]: 读端 * fd[1]: 写端 * 数据从fd[1]写入,从fd[0]读出 */ int fd[2] = {0}; int ret = pipe(fd); if (-1 == ret) { perror("pipe error\n"); return 1; } /* 创建子进程 */ pid_t pid = fork(); if (pid > 0) /* 父进程:字典数据提供者 */ { close(fd[0]); // 父进程关闭读端 /* 打开字典文件 */ int fd_dict = open("/home/linux/dict.txt", O_RDONLY); if (-1 == fd_dict) { perror("open dict"); return 1; } /* 循环读取字典文件并写入管道 */ while (1) { while (1) { char buf[1024] = {0}; int rd_ret = read(fd_dict, buf, sizeof(buf)); if (0 == rd_ret) // 读到文件末尾 { break; } write(fd[1], buf, rd_ret); // 写入管道 } lseek(fd_dict, 0, SEEK_SET); // 文件指针回到开头,循环发送 } close(fd_dict); close(fd[1]); // 关闭写端 } else if (0 == pid) /* 子进程:字典查询客户端 */ { close(fd[1]); // 子进程关闭写端 /* 将管道读端转换为FILE*,方便使用标准I/O函数 */ FILE *fp = fdopen(fd[0], "r"); if (NULL == fp) { perror("fdopen"); return 0; } /* 查询循环 */ while (1) { char want_word[100] = {0}; printf("input want_word"); fgets(want_word, sizeof(want_word), stdin); // 读取用户输入 /* 去除换行符 */ want_word[strlen(want_word) - 1] = '\0'; /* 退出条件检查 */ if(0 == strcmp(want_word,"#quit")) { break; } /* 在字典中查找单词 */ int num = 0; while (1) { char line_buf[1024] = {0}; fgets(line_buf, sizeof(line_buf), fp); // 从管道读取一行 /* 解析字典行: * 格式:单词 解释\r * strtok分割字符串 */ char *word = strtok(line_buf, " "); // 获取单词 char *mean = strtok(NULL, "\r"); // 获取解释 if (0 == strcmp(word, want_word)) // 找到单词 { printf("%s %s\n", word, mean); break; } num++; if (num > MAXLINE) // 超过字典行数,未找到 { printf("cant find wantword:%s\n", want_word); break; } } } close(fd[0]); // 关闭读端 } else /* fork失败 */ { perror("fork"); return 1; } return 0; }

3.2 管道通信要点

特性描述
创建方式pipe(fd)创建匿名管道
数据流向单向,fd[1]写 → fd[0]读
进程关系只适用于有亲缘关系的进程
缓冲区内核维护,通常4KB
读写行为读空阻塞,写满阻塞
关闭规则进程关闭不需要的端口

四、信号处理机制

4.1 信号基础概念

信号是进程间通信的一种异步通知机制,用于通知进程发生了某种事件。

4.2 信号发送程序

/* 信号发送工具 */
#include <sys/types.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { /* 参数检查:需要进程ID和信号编号 */ if(argc<3) { fprintf(stderr,"usage:./a.out pid sig_num\n"); return 1; } // 用法:./a.out 1234 9 // argv[1]: 目标进程ID // argv[2]: 信号编号 pid_t pid = atoi(argv[1]); // 目标进程ID int num = atoi(argv[2]); // 信号编号 /* kill函数:向指定进程发送信号 * 参数1:目标进程ID * >0: 发送给特定进程 * =0: 发送给同进程组的所有进程 * -1: 发送给所有有权限的进程 * <-1: 发送给进程组ID为|pid|的所有进程 * 参数2:信号编号 * 0: 检查进程是否存在 */ int ret = kill(pid,num); if(-1 == ret) { perror("kill"); return 1; } return 0; }

4.3 信号测试程序

/* 信号接收测试程序 */
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { /* 无限循环,用于接收信号测试 * 可以使用kill命令或12kill.c发送信号 * 例如:kill -9 <pid> 或 kill -SIGUSR1 <pid> */ while(1) { printf("pid :%d\n",getpid()); // 打印进程ID,方便测试 sleep(1); } return 0; }

4.4 自定义信号处理程序

/* 自定义用户信号处理 */
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <signal.h> /* SIGUSR1信号处理函数 * 特性:被调用3次后忽略该信号 */ void myhandle1(int num) { static int a = 0; // 静态变量,记录调用次数 printf("老爸叫你,去帮忙...\n"); a++; if(3 == a) { /* 信号处理方式设置: * SIG_IGN: 忽略信号 * SIG_DFL: 恢复默认处理 * 函数指针: 自定义处理函数 */ signal(SIGUSR1,SIG_IGN); // 第3次后忽略SIGUSR1 } return ; } /* SIGUSR2信号处理函数 * 特性:被调用4次后恢复默认处理 */ void myhandle2(int num) { static int a = 0; printf("老妈叫你,去帮忙...\n"); a++; if(4 == a) { signal(SIGUSR2,SIG_DFL); // 第4次后恢复默认 } return ; } int main(int argc, char *argv[]) { /* 注册信号处理函数 * signal函数:设置信号处理方式 * 参数1:信号编号 * 参数2:处理函数或宏 */ signal(SIGUSR1,myhandle1); // SIGUSR1: 用户自定义信号1 signal(SIGUSR2,myhandle2); // SIGUSR2: 用户自定义信号2 /* 主循环:模拟进程正常工作 */ while(1) { printf("i'm playing... pid:%d\n",getpid()); sleep(1); } return 0; }

4.5 统一信号处理函数版本

/* 统一处理多个信号 */
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <signal.h> /* 统一的信号处理函数 * 通过num参数区分不同信号 */ void myhandle1(int num) { if(SIGUSR1 == num) // 处理SIGUSR1 { static int a = 0; printf("老爸叫你,去帮忙...\n"); a++; if(3 == a) { signal(SIGUSR1,SIG_IGN); } } if (SIGUSR2 == num) // 处理SIGUSR2 { static int a = 0; printf("老妈叫你,去帮忙...\n"); a++; if(4 == a) { signal(SIGUSR2,SIG_DFL); } } return ; } int main(int argc, char *argv[]) { /* 两个信号都使用同一个处理函数 */ signal(SIGUSR1,myhandle1); signal(SIGUSR2,myhandle1); while(1) { printf("i'm playing... pid:%d\n",getpid()); sleep(1); } return 0; }

4.6 定时器与信号

/* 简单定时器示例 */
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { /* alarm函数:设置定时器 * 参数:秒数 * 功能:n秒后向进程发送SIGALRM信号 * 默认处理:终止进程 * 返回值:上次定时器的剩余时间 */ alarm(5); // 5秒后发送SIGALRM /* 主循环:5秒后被SIGALRM终止 */ while(1) { printf("i'm processing...\n"); sleep(1); } return 0; }
/* 自定义alarm信号处理 */
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> int flag = 0 ; // 全局标志,用于任务切换 /* SIGALRM信号处理函数 */ void myhandle(int num) { flag = 1; // 收到信号后改变标志 } int main(int argc, char *argv[]) { // 修改SIGALRM信号处理函数 signal(SIGALRM,myhandle); // 设置5秒定时器 alarm(5); /* 通过flag实现状态切换: * 前5秒:flag=0,处理任务 * 5秒后:flag=1,切换状态 */ while(1) { if(0 == flag) { printf("i'm processing...\n"); // 正常工作 } else { printf("i'm off duty....\n"); // 休息状态 } sleep(1); } return 0; }

4.7 进程挂起与恢复

/* 进程挂起示例 */
#include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { int i = 0; while(1) { printf("i'm listen music...\n"); sleep(1); i++; /* 运行3秒后挂起进程 * pause():挂起进程,直到收到信号 * 收到信号后,信号处理函数执行完毕,pause返回 */ if(3 == i) { pause(); // 挂起进程,等待信号 } } return 0; }
/* SIGCONT信号处理 */
#include <stdio.h> #include <unistd.h> #include <signal.h> /* SIGCONT信号处理函数(空函数) */ void myhandle(int num) { // 空处理函数,只用于唤醒pause } int main(int argc, char *argv[]) { int i = 0; /* 注册SIGCONT信号处理 * SIGCONT:继续执行信号(默认忽略) * 当进程被暂停(Ctrl+Z)后,SIGCONT可恢复执行 */ signal(SIGCONT,myhandle); while(1) { printf("i'm listen music...,pid:%d\n",getpid()); sleep(1); i++; /* 运行3秒后挂起,等待SIGCONT信号 */ if(3 == i) { pause(); // 挂起,可被SIGCONT唤醒 } } return 0; }

4.8 子进程回收信号

/* 子进程回收信号处理 */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> /* SIGCHLD信号处理函数 * SIGCHLD:子进程状态改变时发送给父进程 * 可用于异步回收子进程 */ void myhandle(int num) { /* wait函数: * 参数:状态信息(NULL表示不关心) * 返回值:结束的子进程ID * 功能:回收一个子进程 */ pid_t recycle = wait(NULL); printf("pid:%d recycle pid:%d\n",getpid(),recycle); } int main(int argc, char *argv[]) { /* 注册SIGCHLD信号处理 * 避免使用wait阻塞父进程 */ signal(SIGCHLD,myhandle); /* 创建子进程 */ pid_t pid = fork(); if(pid>0) /* 父进程 */ { int i =10; while(i--) { printf("father ,i'm processing... pid:%d\n",getpid()); sleep(1); } } else if(0 == pid) /* 子进程 */ { int i =3; while(i--) { printf("child ,i'm processsing... pid:%d\n",getpid()); sleep(1); } exit(0); // 子进程退出,发送SIGCHLD } else /* fork失败 */ { perror("fork"); return 1; } return 0; }

4.9 常用信号列表

信号编号信号名默认动作说明
1SIGHUP终止终端挂起或控制进程终止
2SIGINT终止中断信号(Ctrl+C)
3SIGQUIT终止+core退出信号(Ctrl+\)
9SIGKILL终止强制终止(不可捕获)
10SIGUSR1终止用户自定义信号1
12SIGUSR2终止用户自定义信号2
14SIGALRM终止定时器信号
17SIGCHLD忽略子进程状态改变
18SIGCONT继续继续执行(如果停止)
19SIGSTOP停止停止进程(不可捕获)

五、 总结要点

进程通信方式对比

方式适用关系通信方向特点
FIFO任意进程单向文件系统可见,需要两个FIFO双向
共享内存任意进程双向最快,需要同步机制
管道父子进程单向简单,内核缓冲区
信号任意进程单向异步通知,功能有限

信号处理重要函数

  1. signal- 注册信号处理函数

  2. kill- 发送信号给进程

  3. alarm- 设置定时器

  4. pause- 挂起进程等待信号

  5. sigaction- 更强大的信号处理(推荐)

编程建议

  1. 信号处理函数要简短,避免复杂操作

  2. 使用volatile防止编译器优化标志变量

  3. 注意信号可能丢失,不应用于精确计数

  4. SIGKILL和SIGSTOP不可捕获

  5. 多线程中信号处理要特别小心

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/9 9:00:49

程序突然“消失”了?C# 闪退问题排查全攻略

作为 C# 开发者&#xff0c;最令人头疼的莫过于程序在没有任何报错提示的情况下瞬间“闪退”。由于没有显式的错误弹窗&#xff0c;这种“无声的崩溃”往往让人无从下手。 本文将带你从开发环境到生产环境&#xff0c;由浅入深地掌握排查 C# 闪退问题的四大绝招。 招式一&…

作者头像 李华
网站建设 2025/12/31 22:53:54

哪些常量用枚举,哪些用类

枚举 vs 常量类的选择标准 使用枚举的情况有限且固定的值集合 如&#xff1a;[StatsTypeEnum](file://D:\Desktop\ai_wei\projects\F-XA-01\code\f-xa-01-api\f-xa-01-api-common\src\main\java\com\aiwei\common\enumeration\StatsTypeEnum.java#L7-L50)&#xff08;平均值、方…

作者头像 李华
网站建设 2025/12/20 0:04:42

数据泄露危机频发,Open-AutoGLM为何成企业最后防线?

第一章&#xff1a;数据泄露危机的现状与挑战近年来&#xff0c;随着数字化转型加速&#xff0c;企业存储和处理的数据量呈指数级增长&#xff0c;数据泄露事件频发&#xff0c;已成为全球关注的安全焦点。攻击者利用系统漏洞、社会工程或内部权限滥用等手段窃取敏感信息&#…

作者头像 李华
网站建设 2025/12/29 3:45:33

Langchain-Chatchat如何配置跨域资源共享CORS?API安全

Langchain-Chatchat 如何配置跨域资源共享&#xff08;CORS&#xff09;&#xff1f;API 安全实战解析 在企业级 AI 应用快速落地的今天&#xff0c;越来越多组织选择将大型语言模型&#xff08;LLM&#xff09;部署于本地环境&#xff0c;以保障数据隐私与合规性。Langchain-C…

作者头像 李华
网站建设 2026/1/5 7:29:39

(Open-AutoGLM操作自由化革命):解锁被屏蔽的社交自动化能力仅需这一步

第一章&#xff1a;Open-AutoGLM 社交应用操作限制解决在部署 Open-AutoGLM 用于社交平台自动化任务时&#xff0c;常因频繁请求或行为模式识别被平台施加操作限制。这类限制包括临时封禁、验证码挑战或 API 调用限流。为保障服务稳定性&#xff0c;需从请求频率控制、身份标识…

作者头像 李华
网站建设 2026/1/7 15:17:56

Langchain-Chatchat如何实现文档水印添加?版权保护机制

Langchain-Chatchat 如何实现文档水印添加&#xff1f;版权保护机制 在企业知识管理日益智能化的今天&#xff0c;基于大语言模型&#xff08;LLM&#xff09;的本地问答系统正迅速成为组织内部信息流转的核心枢纽。Langchain-Chatchat 作为开源领域中广受关注的本地知识库解决…

作者头像 李华