shm_attach() 引起 No space left on device

本文探讨了在使用shm_attach函数时遇到Nospaceleftondevice错误的情况,通过ipcs命令分析已分配和最大共享内存容量,以及检查占用共享内存的进程。提供了重启服务器或使用ipcrm命令释放特定共享内存段的方法来解决问题。

今天在调用shm_attach函数时返回No space left on device,查看官方文档说是共享内存已经被占光了。

用命令ipcs -mu查看已经分配的共享内存数和ipcs -ml查看最大共享内存数量,发现segments allocated和max number of segments大小相等。

[root@localhost ~]# ipcs -ml

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 4194303
max total shared memory (kbytes) = 1073741824
min seg size (bytes) = 1

[root@localhost ~]# ipcs -mu

------ Shared Memory Status --------
segments allocated 4096
pages allocated 1440
pages resident  684
pages swapped   0
Swap performance: 0 attempts     0 successes

[root@localhost ~]#

用命令ipcs -m -p检查占用共享内存的进程

[root@localhost ~]# ipcs -m -p

------ Shared Memory Creator/Last-op --------
shmid      owner      cpid       lpid
98304      root       1962       1984
131073     root       1950       1709
163842     root       1979       2271
196611     root       1999       1709
229380     root       2022       1709
262149     root       2244       1709
294918     root       1986       1709
327687     root       2230       1709
360456     root       2019       1709
393225     root       1986       1709
425994     root       1979       2271
458763     root       1998       1709
491532     root       2271       2273
524301     root       1974       1709
557070     root       1974       1709

返回格式为:

shmid      owner      cpid       lpid

一般cpid和lpid是一致的,在ps中查找,如果cpid和lpid都不存在,那么说明这块共享内存是没有被释放的


你可以重启服务器释放共享内存,也可以通过命令释放 ipcrm -m <shmid> 


#include <stdio.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/wait.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <signal.h> // ✅ 新增:用于 SIGTERM #define SHMKEY_BASE 1024 // 共享内存基 key #define SEMKEY 2048 // 信号量 key #define DATASIZE 256 // 每个缓冲区的数据 int 数量 #define BUFFERNUM 10 // 缓冲区个数 #define BLOCK_SIZE (DATASIZE * sizeof(int)) // 实际字节数 // 共享缓冲区结构 typedef struct ShareBuffer { int data_size; // 实际写入的字节数 int data[DATASIZE]; // 数据区(总共 BLOCK_SIZE 字节) } ShareBuffer; // ✅ 全局变量声明必须放在所有函数之前! int sid; // 信号量 ID int shmid[BUFFERNUM]; // 共享内存 ID 数组 ← 必须提前声明! // 信号量操作封装 void P(int semid, int index) { struct sembuf sb = {index, -1, 0}; semop(semid, &sb, 1); } void V(int semid, int index) { struct sembuf sb = {index, 1, 0}; semop(semid, &sb, 1); } void printHelp() { fprintf(stderr, "Usage: %s <source_file> <dest_file>\n", "a.out"); } // 生产者:读文件入共享内存 void read_buf(int src_fd) { ShareBuffer* buffers[BUFFERNUM]; // 一次性 attach 所有共享内存 for (int i = 0; i < BUFFERNUM; ++i) { buffers[i] = (ShareBuffer*)shmat(shmid[i], NULL, 0); if (buffers[i] == (void*)-1) { perror("shmat in reader"); exit(1); } } int j = 0; ssize_t n; while ((n = read(src_fd, buffers[j]->data, BLOCK_SIZE)) > 0) { // ✅ 使用 -> 而不是 . P(sid, 2); // 请求一个空缓冲区 P(sid, 0); // 获取互斥锁 printf("Reading block %d: %ld bytes\n", j, n); buffers[j]->data_size = n; // ✅ 正确:使用 -> 访问指针成员 V(sid, 0); // 释放互斥锁 V(sid, 1); // 增加满缓冲区计数 j = (j + 1) % BUFFERNUM; } if (n < 0) { perror("read error"); } // 标记结束:最后一个块 data_size 设为 0 P(sid, 2); P(sid, 0); buffers[j]->data_size = 0; // 终止标志 V(sid, 0); V(sid, 1); close(src_fd); for (int i = 0; i < BUFFERNUM; ++i) shmdt(buffers[i]); // 解除映射 exit(0); } // 消费者:从共享内存写文件 void write_buf(int dst_fd) { ShareBuffer* buffers[BUFFERNUM]; // attach 所有共享内存 for (int i = 0; i < BUFFERNUM; ++i) { buffers[i] = (ShareBuffer*)shmat(shmid[i], NULL, 0); if (buffers[i] == (void*)-1) { perror("shmat in writer"); exit(1); } } int j = 0; while (1) { P(sid, 1); // 等待一个满缓冲区 P(sid, 0); // 获取互斥锁 int size = buffers[j]->data_size; if (size == 0) { // 收到终止信号 V(sid, 0); // 释放锁 V(sid, 2); // 归还空槽 break; } printf("Writing block %d: %d bytes\n", j, size); if (write(dst_fd, buffers[j]->data, size) != size) { perror("write error"); break; } V(sid, 0); // 释放互斥锁 V(sid, 2); // 空缓冲区+1 j = (j + 1) % BUFFERNUM; } close(dst_fd); for (int i = 0; i < BUFFERNUM; ++i) shmdt(buffers[i]); exit(0); } int main(int argc, char* argv[]) { if (argc != 3) { fprintf(stderr, "参数错误!\n"); printHelp(); return 1; } // 打开原始文件(使用系统调用) int src_fd = open(argv[1], O_RDONLY); if (src_fd < 0) { perror("无法打开源文件"); return 1; } int dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); if (dst_fd < 0) { perror("无法创建目标文件"); close(src_fd); return 1; } // 创建信号量集(3个) sid = semget((key_t)SEMKEY, 3, IPC_CREAT | 0666); if (sid == -1) { perror("semget failed"); close(src_fd); close(dst_fd); return 1; } // 初始化信号量 semctl(sid, 0, SETVAL, 1); // mutex = 1 semctl(sid, 1, SETVAL, 0); // full = 0 semctl(sid, 2, SETVAL, BUFFERNUM); // empty = BUFFERNUM // 创建共享内存段 for (int i = 0; i < BUFFERNUM; ++i) { shmid[i] = shmget((SHMKEY_BASE + i), sizeof(ShareBuffer), IPC_CREAT | 0666); if (shmid[i] == -1) { perror("shmget failed"); // 清理已创建的资源 for (int k = 0; k < i; ++k) shmctl(shmid[k], IPC_RMID, NULL); semctl(sid, 0, IPC_RMID); return 1; } } // 创建两个子进程 pid_t p1 = fork(); if (p1 == 0) { read_buf(src_fd); // 子进程执行读取 } else if (p1 < 0) { perror("fork reader failed"); return 1; } pid_t p2 = fork(); if (p2 == 0) { write_buf(dst_fd); // 子进程执行写入 } else if (p2 < 0) { perror("fork writer failed"); kill(p1, SIGTERM); // ✅ 现在可用:因为包含了 <signal.h> return 1; } // 父进程等待 waitpid(p1, NULL, 0); waitpid(p2, NULL, 0); // 清理资源 semctl(sid, 0, IPC_RMID); // 删除信号量集 for (int i = 0; i < BUFFERNUM; ++i) { shmctl(shmid[i], IPC_RMID, NULL); // 删除共享内存 } printf("文件复制完成。\n"); return 0; } 怎么运行
最新发布
11-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值