You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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:表示共享内存当前可被客户端写入

完整通信流程

  1. 客户端写入请求:

    • 输入req后,调用lockSem(semidClnt):P操作将semidClnt从1变为0,客户端获得共享内存的独占访问权,写入请求数据
    • 调用unlockSem(semidServ):V操作将semidServ从0变为1,唤醒阻塞的服务器
    • 此时客户端调用第二个lockSem(semidClnt):由于semidClnt当前是0,客户端会进入阻塞,等待信号量变为1
  2. 服务器处理请求:

    • 服务器之前因lockSem(semidServ)阻塞,现在semidServ变为1,P操作后semidServ回到0,服务器继续执行
    • 读取共享内存中的req,计算res = req + 1并写回共享内存
    • 调用unlockSem(semidClnt):V操作将semidClnt从0变为1,唤醒阻塞的客户端
  3. 客户端读取响应:

    • 客户端被唤醒后,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,提问作者ᄑᄀᄀᄉ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:52:19