关于Semaphore客户端代码信号量锁定后仍能通信的疑问
信号量逻辑疑惑解答
我在练习信号量概念时写了一套服务器与客户端代码,现在有个疑惑:客户端代码中,执行scanf后调用lockSem(semidClnt)将信号量置为0的情况下,客户端与服务器仍能正常通信,是不是我对这段代码的逻辑存在理解误区?
服务器代码
#include "MySem.h" #include "MyShm.h" #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/types.h> void signalHandler(int signum); int shmid = 0; int *shmaddr = 0; int semidServ = 0; int semidClnt = 0; int main(int argc, char const *argv[]) { int req = 0; int res = 0; union semun semunBuf; shmid = createShm(MY_SHM_KEY); shmaddr = shmat(shmid, NULL, 0); semidServ = creatSem(MY_SEM_SERV_KEY); semunBuf.val = 0; semctl(semidServ, 0, SETVAL, semunBuf); semidClnt = creatSem(MY_SEM_CLNT_KEY); semunBuf.val = 1; semctl(semidClnt, 0, SETVAL, semunBuf); signal(SIGINT, signalHandler); while (1) { puts("Wait ..."); lockSem(semidServ); memcpy(&req, shmaddr, sizeof(int)); res = req + 1; memcpy(shmaddr, &res, sizeof(int)); unlockSem(semidClnt); printf("Request: %d\n", req); printf("Response: %d\n", res); fflush(stdout); } return 0; }
客户端代码
#include "MySem.h" #include "MyShm.h" #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/types.h> void signalHandler(int signum); int shmid = 0; int *shmaddr = NULL; int semidServ = 0; int semidClnt = 0; int main(int argc, char const *argv[]) { int req = 0; int res; shmid = openShm(MY_SHM_KEY); shmaddr = shmat(shmid, NULL, 0); semidServ = openSem(MY_SEM_SERV_KEY); semidClnt = openSem(MY_SEM_CLNT_KEY); signal(SIGINT, signalHandler); while (1) { printf("<<"); scanf("%d", &req); fflush(stdout); fflush(stdin); lockSem(semidClnt); memcpy(shmaddr, &req, sizeof(int)); unlockSem(semidServ); lockSem(semidClnt); memcpy(&res, shmaddr, sizeof(int)); unlockSem(semidClnt); printf(">> %d\n", res); } return 0; }
逻辑解析
首先明确:通常lockSem对应信号量的P操作(值减1),unlockSem对应V操作(值加1)。结合代码的初始化和执行流程,就能解开你的疑惑:
初始状态
semidServ被初始化为0:表示服务器没有待处理的请求,处于阻塞等待状态semidClnt被初始化为1:表示共享内存当前可被客户端写入
完整通信流程
客户端写入请求:
- 输入
req后,调用lockSem(semidClnt):P操作将semidClnt从1变为0,客户端获得共享内存的独占访问权,写入请求数据 - 调用
unlockSem(semidServ):V操作将semidServ从0变为1,唤醒阻塞的服务器 - 此时客户端调用第二个
lockSem(semidClnt):由于semidClnt当前是0,客户端会进入阻塞,等待信号量变为1
- 输入
服务器处理请求:
- 服务器之前因
lockSem(semidServ)阻塞,现在semidServ变为1,P操作后semidServ回到0,服务器继续执行 - 读取共享内存中的
req,计算res = req + 1并写回共享内存 - 调用
unlockSem(semidClnt):V操作将semidClnt从0变为1,唤醒阻塞的客户端
- 服务器之前因
客户端读取响应:
- 客户端被唤醒后,
lockSem(semidClnt)执行成功(semidClnt变为0),读取共享内存中的res - 调用
unlockSem(semidClnt):V操作将semidClnt变回1,释放共享内存 - 打印响应结果,进入下一次循环
- 客户端被唤醒后,
你的理解误区
你误以为客户端连续两次调用lockSem(semidClnt)之间没有解锁操作,会导致客户端一直阻塞,但实际上服务器在处理完请求后,会主动调用unlockSem(semidClnt)将信号量恢复为1,所以客户端的第二个lockSem能顺利获取信号量,通信可以正常进行。
潜在问题提示
客户端最后一次unlockSem(semidClnt)是多余的——因为下一次循环开始时,客户端会再次调用lockSem(semidClnt),而当前逻辑下semidClnt在服务器处理完后已经是1,这个多余的解锁会导致信号量值变为2,若扩展为多客户端场景,可能引发共享内存被同时写入的并发问题。
内容的提问来源于stack exchange,提问作者ᄑᄀᄀᄉ
相关产品推荐
相关产品推荐

