UCIe协议实战:手把手教你理解PCIe、CXL与Streaming的三种协议选择与协商机制
2026/5/28 2:50:58
内存映射(mmap)是一种将文件或设备映射到进程地址空间的机制。通过内存映射,进程可以像访问普通内存一样访问文件,也可以用于进程间通信。mmap提供了高效的文件I/O和进程间通信方式。
内存映射的特点:
创建映射:
mmap()将文件映射到进程地址空间访问数据:
同步:
msync():手动同步映射区域到文件munmap():取消映射,自动同步数据流向:
进程A ──┐ ├──> [共享映射区域] <──> [文件/设备] 进程B ──┤ 进程C ──┘创建匿名映射:
mmap()创建匿名映射(不关联文件)共享机制:
#include<sys/mman.h>void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);addr:建议的映射地址(通常为NULL,让系统选择)length:映射长度(字节)prot:保护标志(PROT_READ可读,PROT_WRITE可写,PROT_EXEC可执行)flags:映射标志(MAP_SHARED共享,MAP_PRIVATE私有,MAP_ANONYMOUS匿名等)fd:文件描述符(匿名映射时为-1)offset:文件偏移量(通常为0)intmunmap(void*addr,size_tlength);addr:映射地址length:映射长度intmsync(void*addr,size_tlength,intflags);addr:映射地址length:同步长度flags:同步标志(MS_SYNC同步写,MS_ASYNC异步写,MS_INVALIDATE使缓存无效)#include<stdio.h>#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<string.h>intmain(){constchar*path="demo.txt";constchar*msg="hello mmap\n";intfd=open(path,O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return1;}// 确保文件大小 >= 要写入的长度size_tlen=strlen(msg);if(ftruncate(fd,len)==-1){perror("ftruncate");close(fd);return1;}// 建立映射: 读写, 共享写回文件void*addr=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(addr==MAP_FAILED){perror("mmap");close(fd);return1;}// 写入数据memcpy(addr,msg,len);// 同步到文件if(msync(addr,len,MS_SYNC)==-1){perror("msync");}// 读取验证write(STDOUT_FILENO,addr,len);// 清理munmap(addr,len);close(fd);return0;}#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<string.h>#defineTHREADS4#definePER_THREAD100000structshared{pthread_mutex_tlock;intcounter;};void*worker(void*arg){structshared*sh=(structshared*)arg;for(inti=0;i<PER_THREAD;i++){pthread_mutex_lock(&sh->lock);sh->counter++;pthread_mutex_unlock(&sh->lock);}returnNULL;}intmain(){constchar*path="shared.dat";intfd=open(path,O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return1;}// 准备文件长度if(ftruncate(fd,sizeof(structshared))==-1){perror("ftruncate");close(fd);return1;}// 建立映射structshared*sh=mmap(NULL,sizeof(structshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(sh==MAP_FAILED){perror("mmap");close(fd);return1;}// 初始化锁和计数器pthread_mutexattr_tattr;pthread_mutexattr_init(&attr);pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_PRIVATE);// 同一进程线程间共享pthread_mutex_init(&sh->lock,&attr);sh->counter=0;// 创建线程pthread_tth[THREADS];for(inti=0;i<THREADS;i++){pthread_create(&th[i],NULL,worker,sh);}for(inti=0;i<THREADS;i++){pthread_join(th[i],NULL);}printf("Final counter = %d (expect %d)\n",sh->counter,THREADS*PER_THREAD);// 清理pthread_mutex_destroy(&sh->lock);munmap(sh,sizeof(structshared));close(fd);return0;}说明:
PTHREAD_PROCESS_PRIVATE改为PTHREAD_PROCESS_SHARED, 并确保映射为 MAP_SHARED 且 mutex 存放于共享内存中#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/mman.h>#include<sys/stat.h>#include<sys/wait.h>#include<fcntl.h>#include<pthread.h>#include<string.h>#include<errno.h>#definePROCESSES4#definePER_PROCESS100000// 共享数据结构, 包含互斥锁和计数器structshared{pthread_mutex_tlock;// 进程间共享的互斥锁intcounter;// 共享计数器intinitialized;// 初始化标志, 确保只初始化一次};intmain(){constchar*path="shared_mmap.dat";intfd;structshared*sh;pid_tpids[PROCESSES];inti;// 创建或打开共享文件fd=open(path,O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return1;}// 调整文件大小以容纳共享数据结构if(ftruncate(fd,sizeof(structshared))==-1){perror("ftruncate");close(fd);return1;}// 建立共享内存映射// MAP_SHARED 确保多个进程共享同一块物理内存sh=(structshared*)mmap(NULL,sizeof(structshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(sh==MAP_FAILED){perror("mmap");close(fd);return1;}// 父进程负责初始化共享数据结构if(sh->initialized==0){// 初始化进程间共享的互斥锁属性pthread_mutexattr_tattr;pthread_mutexattr_init(&attr);// 关键: 设置为进程间共享, 这样多个进程可以使用同一个互斥锁pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);// 初始化互斥锁if(pthread_mutex_init(&sh->lock,&attr)!=0){perror("pthread_mutex_init");munmap(sh,sizeof(structshared));close(fd);return1;}pthread_mutexattr_destroy(&attr);// 初始化计数器sh->counter=0;// 设置初始化标志, 防止子进程重复初始化sh->initialized=1;printf("Parent: Initialized shared memory\n");}// 创建多个子进程for(i=0;i<PROCESSES;i++){pids[i]=fork();if(pids[i]<0){perror("fork");// 清理已创建的子进程for(intj=0;j<i;j++){kill(pids[j],SIGTERM);}munmap(sh,sizeof(structshared));close(fd);return1;}elseif(pids[i]==0){// 子进程: 执行计数器递增操作printf("Child %d (PID %d): Starting increments\n",i,getpid());for(intj=0;j<PER_PROCESS;j++){// 加锁保护临界区pthread_mutex_lock(&sh->lock);sh->counter++;pthread_mutex_unlock(&sh->lock);}printf("Child %d (PID %d): Completed increments\n",i,getpid());// 子进程退出, munmap 会自动清理映射exit(0);}}// 父进程等待所有子进程完成printf("Parent: Waiting for all children to complete...\n");for(i=0;i<PROCESSES;i++){intstatus;waitpid(pids[i],&status,0);if(WIFEXITED(status)){printf("Parent: Child %d (PID %d) exited with status %d\n",i,pids[i],WEXITSTATUS(status));}}// 验证最终结果printf("Parent: Final counter = %d (expected %d)\n",sh->counter,PROCESSES*PER_PROCESS);if(sh->counter==PROCESSES*PER_PROCESS){printf("Parent: Success! Counter is correct.\n");}else{printf("Parent: Error! Counter mismatch (race condition detected).\n");}// 清理资源pthread_mutex_destroy(&sh->lock);munmap(sh,sizeof(structshared));close(fd);// 可选: 删除共享文件// unlink(path);return0;}说明:
MAP_SHARED标志创建共享内存映射, 多个进程可以访问同一块物理内存PTHREAD_PROCESS_SHARED属性的互斥锁, 确保多个进程可以正确同步fork()共享父进程的映射区域, 无需额外文件操作// writer.c: 写入进程#include<stdio.h>#include<stdlib.h>#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<pthread.h>#include<string.h>structshared{pthread_mutex_tlock;charmessage[256];intready;};intmain(){constchar*path="shared_mmap.dat";intfd=open(path,O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return1;}if(ftruncate(fd,sizeof(structshared))==-1){perror("ftruncate");close(fd);return1;}structshared*sh=(structshared*)mmap(NULL,sizeof(structshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(sh==MAP_FAILED){perror("mmap");close(fd);return1;}// 初始化互斥锁(仅第一次)if(sh->ready==0){pthread_mutexattr_tattr;pthread_mutexattr_init(&attr);pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(&sh->lock,&attr);pthread_mutexattr_destroy(&attr);sh->ready=1;}// 写入数据pthread_mutex_lock(&sh->lock);snprintf(sh->message,sizeof(sh->message),"Hello from writer (PID %d)",getpid());printf("Writer: Wrote message\n");pthread_mutex_unlock(&sh->lock);sleep(2);// 等待读取进程读取munmap(sh,sizeof(structshared));close(fd);return0;}// reader.c: 读取进程#include<stdio.h>#include<stdlib.h>#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<pthread.h>structshared{pthread_mutex_tlock;charmessage[256];intready;};intmain(){constchar*path="shared_mmap.dat";intfd=open(path,O_RDWR,0666);if(fd<0){perror("open");return1;}structshared*sh=(structshared*)mmap(NULL,sizeof(structshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(sh==MAP_FAILED){perror("mmap");close(fd);return1;}// 等待写入进程初始化while(sh->ready==0){usleep(100000);// 100ms}// 读取数据pthread_mutex_lock(&sh->lock);printf("Reader: Read message: %s\n",sh->message);pthread_mutex_unlock(&sh->lock);munmap(sh,sizeof(structshared));close(fd);return0;}说明:
man 2 mmapman 2 munmapman 2 msyncman 7 shm_overview