package shm import ( "fmt" "runtime/debug" "unsafe" "bash_go_service/config-loader/internal/models" "bash_go_service/shared/pkg/constants" "bash_go_service/shared/pkg/logger" "golang.org/x/sys/unix" ) // shmget 调用系统调用 SYS_SHMGET 创建或获取共享内存段 // key: 共享内存的键值 // size: 共享内存的大小 // shmflg: 标志位,用于指定权限和行为 func shmget(key int, size int, shmflg int) (int, error) { id, _, errno := unix.Syscall(unix.SYS_SHMGET, uintptr(key), uintptr(size), uintptr(shmflg)) if errno != 0 { return 0, errno } return int(id), nil } // shmat 调用系统调用 SYS_SHMAT 将共享内存段附加到进程的地址空间 // shmid: 共享内存段的 ID // shmaddr: 指定的附加地址(通常为 0 表示系统自动选择) // shmflg: 标志位,用于指定附加模式 func shmat(shmid int, shmaddr uintptr, shmflg int) (unsafe.Pointer, error) { r1, _, errno := unix.Syscall(unix.SYS_SHMAT, uintptr(shmid), shmaddr, uintptr(shmflg)) if errno != 0 { return nil, errno } // 通过 reflect.SliceHeader 进行转换 var dummy [1]unsafe.Pointer *(*uintptr)(unsafe.Pointer(&dummy)) = r1 return dummy[0], nil } // shmdt 调用系统调用 SYS_SHMDT 将共享内存段从进程的地址空间分离 // shmaddr: 共享内存段的地址 func shmdt(shmaddr unsafe.Pointer) error { _, _, errno := unix.Syscall(unix.SYS_SHMDT, uintptr(shmaddr), 0, 0) if errno != 0 { return errno } return nil } // WriteConfigToSharedMemory 将配置数据写入共享内存 // config: 要写入的配置数据 func WriteConfigToSharedMemory(config *models.ConfigData) (err error) { defer func() { if r := recover(); r != nil { logger.Error("Panic occurred while writing config to shared memory: %v", r) // 将panic转换为error返回 switch x := r.(type) { case string: err = fmt.Errorf("panic: %s", x) case error: err = fmt.Errorf("panic: %w", x) default: err = fmt.Errorf("panic: %v", x) } // 打印调用栈信息 debug.PrintStack() } }() logger.Debug("Starting to write configuration to shared memory") // 使用 unsafe.Alignof 获取对齐要求 align := unsafe.Alignof(*config) size := unsafe.Sizeof(*config) // 确保大小是对齐的 alignedSize := (size + align - 1) &^ (align - 1) if alignedSize > constants.ShmSize { return fmt.Errorf("config size %d exceeds shared memory size %d", alignedSize, constants.ShmSize) } // 创建共享内存 shmID, err := shmget(constants.ShmKey, constants.ShmSize, unix.IPC_CREAT|0666) if err != nil { // 获取更详细的错误信息 switch err { case unix.EACCES: logger.Error("Permission denied: %v", err) case unix.EEXIST: logger.Error("Shared memory segment already exists: %v", err) case unix.EINVAL: logger.Error("Invalid size or key: %v", err) case unix.ENOENT: logger.Error("Shared memory segment does not exist: %v", err) case unix.ENOMEM: logger.Error("No memory available: %v", err) default: logger.Error("Unknown error: %v", err) } return fmt.Errorf("shmget failed: %w", err) } // 附加共享内存 shmPtr, err := shmat(shmID, 0, 0) if err != nil { return fmt.Errorf("shmat failed: %w", err) } defer shmdt(shmPtr) // 使用内存屏障确保对齐 alignedPtr := unsafe.Pointer((uintptr(shmPtr) + align - 1) &^ (align - 1)) // 复制数据 *(*models.ConfigData)(alignedPtr) = *config logger.Info("Configuration successfully written to shared memory") return nil } // ReadConfigFromSharedMemory 从共享内存读取配置数据 func ReadConfigFromSharedMemory() (config *models.ConfigData, err error) { defer func() { if r := recover(); r != nil { logger.Error("Panic occurred while reading config from shared memory: %v", r) // 将panic转换为error返回 switch x := r.(type) { case string: err = fmt.Errorf("panic: %s", x) case error: err = fmt.Errorf("panic: %w", x) default: err = fmt.Errorf("panic: %v", x) } // 打印调用栈信息 debug.PrintStack() } }() logger.Debug("Starting to read configuration from shared memory") // 读取时使用和写入时相同的权限 shmID, err := shmget(constants.ShmKey, constants.ShmSize, 0666) if err != nil { switch err { case unix.EINVAL: logger.Error("Invalid size (%d) or key (%d)", constants.ShmSize, constants.ShmKey) case unix.EEXIST: logger.Error("Segment exists but cannot be accessed") default: logger.Error("Unexpected error: %v", err) } return nil, fmt.Errorf("shmget failed: %w", err) } // 将共享内存段附加到进程地址空间(只读模式) shmPtr, err := shmat(shmID, 0, unix.SHM_RDONLY) if err != nil { return nil, fmt.Errorf("shmat failed: %w", err) } defer shmdt(shmPtr) // 确保在函数结束时分离共享内存 // 使用与写入相同的对齐方式 align := unsafe.Alignof(models.ConfigData{}) alignedPtr := unsafe.Pointer((uintptr(shmPtr) + align - 1) &^ (align - 1)) // 直接进行结构体复制 config = new(models.ConfigData) *config = *(*models.ConfigData)(alignedPtr) return config, nil }