共享内存是两个或多个进程共享同一块内存区域,并通过该内存区域实现数据交换的进程间通信。虽然共享内存是进程间通信的最快速的机制,但是进程间的同步问题靠自身难以解决,于是就需要信号量机制,信号量能很好的解决互斥资源的同步问题。这些牵涉到操作系统里的知识,要好好研究一番同步互斥问题才能继续。
共享内存的工作模式一般是:
1. int shmget(key_t key, int size, int shmflg);
创建或取得一块共享内存
2. void *shmat(int shmid, const void *shmaddr, int shmflg);
将shmid所指共享内存和当前进程连接(attach)
3. 要做的事。。。。
4. int shmdt(const void *shmaddr);
将先前用shmat连接好的共享内存分离(detach)当前的进程
5. int shmctl(int shmid ,int cmd, struct shmid_ds *buf)
把cmd设成IPC_RMID删除共享内存及其数据结构
附加说明:
1. 在经过fork()后,子进程将继承已连接的共享内存地址
2. 在经过exec()后,已连接的共享内存地址会自动detach
3. 在结束进程后,已连接的共享内存地址会自动detach
而信号量大致是如此的:
1. int semget(key_t key, int nsems, int semflg);
创建或获取信号队列
2. int semctl(int semid, int semnum, int cmd, union semun arg);
控制信号队列,如cmd=SETVAL设置信号量的值; 或者cmd=IPC_STAT获得 semid_ds结构
3. int semop(int semid, struct sembuf *sop, unsigned nsops);
信号处理
4. 完后将semctl的cmd设置为IPC_RMID将信号队列删除
/*一个例子程序,来自《linux环境下C编程指南》,有小改动: *简单的服务器和客户端程序,启动不带参数运行服务器,带参数则是客户端 *服务器启动后,创建信号量和共享内存,并将共享内存的引用ID显示出来, * 将信号量的引用ID放在共享内存中,利用服务器端提供的共享内存引用ID *将共享内存附加到地址段,读取信号量以实现两个进程之间的同步,之后,这两个进程就可 *利用共享内存进行进程间通信,客户端输入的信息将在服务器端显示出来 */ #include #include #include #include #include #include #include #define SHMDATASZ 1000 #define BUFSZ #define SN_EMPTY 0 #define SN_FULL 1 int delete_semid = 0; union semun { int val; struct semid_ds ushort array; }; void server(void); void client(int shmid); void delete(void); void sigdelete(int signum); void locksem(int semid, void unlocksem(int semid, void clientwrite(int shmid, int main(int argc, { if(argc server(); else client(atoi(argv[1])); return 0; } void server(void) { union semun sunion; int semid, shmid; void *shmdata; char *buffer; //创建2个信号量 semid = semget(IPC_PRIVATE, 2, SHM_R|SHM_W); if(semid perror("semget"); delete_semid = semid; // 当接收到SIGINT信号时删除信号量并终止程序 atexit(&delete); signal(SIGINT, //设EN_EMPYT的信号值为1 sunion.val = 1; if(semctl(semid, SN_EMPTY, SETVAL, sunion) perror("semctl"); sunion.val = 0; //设EN_FULL的信号值为0,说明没有一个资源可用 if(semctl(semid, SN_FULL, SETVAL, sunion) perror("semctl"); //创建一块共享内存 shmid = shmget(IPC_PRIVATE, SHMDATASZ, IPC_CREAT|SHM_R|SHM_W); if(shmid perror("shmget"); //附加到shmdata shmdata = shmat(shmid, 0, 0); if(shmdata perror("shmat"); //删除共享内存,刚刚创建还没用呢,就删了,不明白 // if(shmctl(shmid, IPC_RMID, NULL) == -1) // perror("shmctl"); //把信号标识符放在首地址 *(int*)shmdata buffer = shmdata printf("Server is running with SHM id ** %d **\n", shmid); while(1) { printf("waiting until full..."); fflush(stdout); //申请一个资源 locksem(semid, SN_FULL); printf("done\n"); printf("message received :%s\n", buffer); //释放用完的资源 unlocksem(semid, SN_EMPTY); } } void client(int shmid) { int semid; void *shmdata; char *buffer; //把server创建的共享内存附加到shmdata shmdata = shmat(shmid, 0, 0); //取出信号标识符 semid = *(int*)shmdata; //再找到缓冲区 buffer = shmdata+sizeof(int); printf("client operational: shm id is %d, sem id is %d\n", shmid, semid); while(1) { char input[3]; printf("\n\nMenu\n1.Send a message\n"); printf("2.Exit\n"); fgets(input, switch(input[0]) { case ‘1‘:clientwrite(shmid, semid, buffer); break; case ‘2‘:exit(0); break; } } } void clientwrite(int shmid, { printf("waiting until empty.."); fflush(stdout); //申请EMPTY资源 locksem(semid, SN_EMPTY); printf("Enter Message:"); fgets(buffer, BUFSZ, //释放资源 unlocksem(semid, SN_FULL); } void delete() { printf("\nMaster exiting; deleting semaphore %d\n", delete_semid); if(semctl(delete_semid, IPC_RMID, 0) perror("release semaphore"); } void sigdelete(int num) { exit(0); } void locksem(int semid, { struct sembuf sb; sb.sem_num = semnum; sb.sem_op = sb.sem_flg = SEM_UNDO; semop(semid, } void unlocksem(int semid, { struct sembuf sb; sb.sem_num = semnum; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; semop(semid, } |
参考资料《linux环境下C编程指南》《Linux C函数库参考手册》
IPC(shm+sem)