Raspbian Buster与Ubuntu 20.04中shm_open调用触发段错误排查
问题描述
我在树莓派4(Raspbian Buster)和普通PC(Ubuntu 20.04)上用C语言编写增量编码器数据采集进程时,遇到shm_open()触发段错误的问题,程序崩溃后/dev/shm目录中仍能看到该共享内存段的名称。GCC版本分别为10.2.1(Raspbian)和9.4.0(Ubuntu),添加额外编译标志无效。
编码器进程代码
#include <signal.h> #include <stdlib.h> #include <time.h> #include "stdio.h" #include <string.h> #include <unistd.h> #include "encbuffer.h" #define ENNAME "/test" int mode = 4; int pulses_amount_gpio_4 = 0; int encoder_buffer_fd = 0; double local_angle = 1; struct encoder_buffer *enc_buffer; void* shm_enc_buffer; void Handler(int signo) { mode = 2; } int main(void) { // the issue is here encoder_buffer_fd = shm_open(ENNAME, O_CREAT|O_RDWR|O_TRUNC, 0666); if(encoder_buffer_fd < 0) { perror("shm_open()"); return 1; } printf("passed buffer opening"); ftruncate(encoder_buffer_fd, sizeof(struct encoder_buffer)); shm_enc_buffer = mmap(0, sizeof(struct encoder_buffer), PROT_WRITE|PROT_READ, MAP_SHARED, encoder_buffer_fd, 0); // Exception handling:ctrl + c signal(SIGINT, Handler); if(sem_init(&(enc_buffer->semaphore), 1, 1) < 0) { return 3; } while(mode != 2) { /*printf("Angle: %f ", local_angle); sleep(1);*/ } munmap(0, sizeof(struct encoder_buffer)); shm_unlink(ENNAME); return 0; }
编码器缓冲区头文件(encbuffer.h)
#ifndef _ENCBUFFER_H_ #define _ENCBUFFER_H_ #include <stdlib.h> #include <signal.h> #include "stdio.h" #include <semaphore.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/mman.h> #include <assert.h> #include <sys/stat.h> #include <fcntl.h> struct encoder_buffer { double angle; double previous_angle; sem_t semaphore; }; #endif
Makefile
CC = gcc MSG = -g -O0 -Wall -I. -lrt -pthread CFLAGS += $(MSG) DEPS = encbuffer.h OBJ = anglecollect.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) angle: $(OBJ) $(CC) -o $@ $^ $(CFLAGS)
原因分析
野指针访问导致段错误
代码中声明了struct encoder_buffer *enc_buffer;但从未对其赋值,直接在sem_init(&(enc_buffer->semaphore), 1, 1)中访问其成员,属于非法内存访问,直接触发段错误。你错误地认为shm_enc_buffer和enc_buffer有关联,但两者没有任何绑定,enc_buffer始终是未初始化的野指针。shm_open()并非段错误触发点
程序能打印passed buffer opening说明shm_open()执行完全成功。段错误实际发生在后续访问野指针的代码处。而/dev/shm中残留共享内存是因为程序崩溃时还没执行到shm_unlink(ENNAME),POSIX共享内存对象不会随进程退出自动销毁,必须显式调用shm_unlink才能删除。munmap()参数错误munmap()的第一个参数必须是mmap()返回的内存地址(即shm_enc_buffer),而不是0。这个错误会导致内存释放失败,但并非本次段错误的直接原因。
修复方案
绑定共享内存指针
在mmap()之后,将返回的地址赋值给enc_buffer,建立指针与共享内存的关联:shm_enc_buffer = mmap(0, sizeof(struct encoder_buffer), PROT_WRITE|PROT_READ, MAP_SHARED, encoder_buffer_fd, 0); enc_buffer = (struct encoder_buffer*)shm_enc_buffer; // 新增该行修正
munmap()参数
将munmap(0, sizeof(struct encoder_buffer))改为:munmap(shm_enc_buffer, sizeof(struct encoder_buffer));调试输出优化(可选)
为printf添加换行,确保输出及时刷新到终端,方便调试:printf("passed buffer opening\n");异常退出时清理资源
在信号处理函数中添加资源清理逻辑,避免Ctrl+C退出时残留共享内存:void Handler(int signo) { mode = 2; // 清理共享内存 if (shm_enc_buffer != NULL) { munmap(shm_enc_buffer, sizeof(struct encoder_buffer)); } shm_unlink(ENNAME); exit(0); // 确保进程退出 }
内容的提问来源于stack exchange,提问作者V8Interceptor

