如何为外部启动的Python实例提供C程序的FITS图像数据访问权限?
跨平台实现C与Python共享内存处理图像的方案
核心思路
要让Python进程直接访问C程序中float* img的内存,跨平台的核心是共享内存机制,同时传递图像尺寸等元数据给Python脚本。下面分通用方案和分平台实现两种方式说明,针对你提到的“将每个像素除以2”的场景给出具体代码。
一、通用跨平台方案:共享内存+命令行传参
这种方式依赖Python 3.8+的multiprocessing.shared_memory,C侧通过系统调用创建共享内存区域,拷贝图像数据后启动Python进程,通过命令行传递共享内存名称、图像尺寸等参数。
C语言端实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef _WIN32 #include <windows.h> #include <process.h> #else #include <unistd.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #endif // 跨平台创建共享内存 void* create_shared_memory(size_t size, const char* name, int* fd_or_handle) { #ifdef _WIN32 HANDLE hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, name); if (hMapFile == NULL) return NULL; void* ptr = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, size); *fd_or_handle = (int)hMapFile; return ptr; #else int fd = shm_open(name, O_CREAT | O_RDWR, 0666); if (fd == -1) return NULL; ftruncate(fd, size); void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); *fd_or_handle = fd; return ptr; #endif } // 跨平台释放共享内存 void release_shared_memory(void* ptr, size_t size, int fd_or_handle, const char* name) { #ifdef _WIN32 UnmapViewOfFile(ptr); CloseHandle((HANDLE)fd_or_handle); DeleteFileA(name); #else munmap(ptr, size); close(fd_or_handle); shm_unlink(name); #endif } int main() { // 模拟图像数据 int rx = 100, ry = 100, nchans = 3; size_t img_size = rx * ry * nchans * sizeof(float); float* img = malloc(img_size); // 初始化图像数据 for (size_t i = 0; i < rx*ry*nchans; i++) { img[i] = i * 1.0f; } // 创建共享内存 const char* shm_name = "FITS_IMAGE_SHM"; int shm_handle; void* shm_ptr = create_shared_memory(img_size, shm_name, &shm_handle); if (!shm_ptr) { perror("Failed to create shared memory"); free(img); return 1; } // 拷贝图像到共享内存 memcpy(shm_ptr, img, img_size); // 构造Python执行命令 char cmd[512]; snprintf(cmd, sizeof(cmd), "python image_processor.py %s %d %d %d", shm_name, rx, ry, nchans); // 执行脚本并等待完成 int ret = system(cmd); if (ret != 0) { fprintf(stderr, "Python script execution failed\n"); release_shared_memory(shm_ptr, img_size, shm_handle, shm_name); free(img); return 1; } // 读取处理后的图像数据 memcpy(img, shm_ptr, img_size); // 释放资源 release_shared_memory(shm_ptr, img_size, shm_handle, shm_name); free(img); return 0; }
Python脚本端(image_processor.py)
import sys import numpy as np from multiprocessing import shared_memory def main(): if len(sys.argv) != 5: print("Usage: python image_processor.py <shm_name> <rx> <ry> <nchans>") sys.exit(1) shm_name = sys.argv[1] rx = int(sys.argv[2]) ry = int(sys.argv[3]) nchans = int(sys.argv[4]) total_elements = rx * ry * nchans # 连接共享内存 shm = shared_memory.SharedMemory(name=shm_name) # 创建numpy数组直接映射共享内存(无拷贝) img_array = np.ndarray((rx, ry, nchans), dtype=np.float32, buffer=shm.buf) # 执行处理:每个元素除以2 img_array /= 2.0 # 关闭共享内存 shm.close() if __name__ == "__main__": main()
二、分平台优化方案
Linux/macOS:直接传递内存指针
类Unix系统中父子进程共享内存空间,可通过fork启动Python进程并直接传递指针地址,无需拷贝数据。
C端示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main() { int rx = 100, ry = 100, nchans = 3; size_t img_size = rx * ry * nchans * sizeof(float); float* img = malloc(img_size); for (size_t i = 0; i < rx*ry*nchans; i++) { img[i] = i * 1.0f; } pid_t pid = fork(); if (pid == 0) { // 子进程启动Python脚本 char ptr_str[64], rx_str[16], ry_str[16], nchans_str[16]; snprintf(ptr_str, sizeof(ptr_str), "%p", img); snprintf(rx_str, sizeof(rx_str), "%d", rx); snprintf(ry_str, sizeof(ry_str), "%d", ry); snprintf(nchans_str, sizeof(nchans_str), "%d", nchans); execlp("python", "python", "image_processor_unix.py", ptr_str, rx_str, ry_str, nchans_str, NULL); perror("execlp failed"); exit(1); } else if (pid > 0) { // 父进程等待子进程完成 wait(NULL); printf("First element after processing: %f\n", img[0]); free(img); } else { perror("fork failed"); free(img); return 1; } return 0; }
Python脚本(image_processor_unix.py)
import sys import numpy as np import ctypes def main(): if len(sys.argv) != 5: print("Usage: python image_processor_unix.py <ptr_addr> <rx> <ry> <nchans>") sys.exit(1) ptr_addr = int(sys.argv[1], 16) rx = int(sys.argv[2]) ry = int(sys.argv[3]) nchans = int(sys.argv[4]) total_elements = rx * ry * nchans # 将地址转为float数组指针 img_ptr = ctypes.cast(ptr_addr, ctypes.POINTER(ctypes.c_float * total_elements)) # 转为numpy数组直接映射内存 img_array = np.ctypeslib.as_array(img_ptr.contents).reshape((rx, ry, nchans)) # 处理:除以2 img_array /= 2.0 if __name__ == "__main__": main()
Windows:命名管道传递内存映射句柄
Windows下可通过命名管道传递内存映射文件的句柄,Python用ctypes获取句柄并映射内存,逻辑与通用方案一致,可直接复用通用方案代码。
注意事项
- Python版本要求:通用方案需Python 3.8及以上,因为
multiprocessing.shared_memory是3.8版本引入的 - 内存同步:必须等待Python进程处理完成后,C程序再读取共享内存,避免竞态条件
- 权限问题:Windows下共享内存名称需加
Global\前缀以全局可见,Linux/macOS下名称长度不能超过限制 - astropy集成:Python脚本中可直接用
astropy.io.fits读写FITS文件,或基于映射的numpy数组做进一步处理
内容的提问来源于stack exchange,提问作者Adrian K-B.
相关产品推荐
相关产品推荐

