我正在使用客户端程序实现服务器。通信与共享内存一起使用。要控制对公共资源的访问,我正在使用信号量。如果客户端是服务器的新用户,则服务器会为客户端生成一个id。客户端保存它的id并将在进一步的请求中发送此id。 我正在使用以下代码在服务器和客户端之间进行通信。此解决方案仅适用于一个服务器和一个客户端。
服务器:
#include <semaphore.h>
sem_t* server = sem_open(SEM_1, O_CREAT | O_EXCL, 0600, 1);
sem_t* client = sem_open(SEM_2, O_CREAT | O_EXCL, 0600, 0);
//other stuff
while (!want_quit)
{
sem_wait(client)
//get id of client from shared memory or generate id for client
//get the command from the client via shared memory
//process command
//write result back to shared memory
sem_post(server)
}
客户端:
#include <semaphore.h>
sem_t* s1 = sem_open(SEM_1, 0);
sem_t* s2 = sem_open(SEM_2, 0);
do
{
//wait for the server
sem_wait(s1);
//get result from last command from shared memory
//send new request to server by writing command into shared memory
sem_post(s2);
} while (command from shm != CLOSE);
服务器应该工作/管理多个客户端。我以为我可以通过第三个信号量解决这个问题,但是我遇到了死锁问题,或者客户端处理了另一个客户端的结果。
我使用第三个信号量的解决方案如下所示:
服务器:
sem_wait(clients);
sem_wait(client);
sem_post(server);
客户端:
sem_wait(s1);
sem_post(clients);
sem_post(server);
我该如何解决这个挑战?
答案 0 :(得分:0)
您对信号量的使用并不完全正确。信号量旨在保护一个或多个共享资源,通常使用表示可用资源数量的信号量计数。对于共享内存段,您只有一个资源(内存块),因此您应该使用一个计数为1的信号量来保护它。
此单个信号量协调所有客户端和服务器。客户端获取信号量,写入其命令,然后释放信号量。服务器获取此信号量,读取命令并执行所需的任何处理,将结果写回共享内存,然后释放信号量。
但是,正如您所发现的,这不会协调每个客户端。没有什么可以阻止客户端读取另一个客户端的响应。所以你可以在这里使用另一个信号量,这可以被认为是保护服务器的“通信通道”。同样,这是一个单一资源,因此它应该有一个计数为1的信号量。
所以你的完整设计将使用2个信号量,看起来像:
请注意,服务器永远不会获取通信信道信号量。这纯粹是为了协调2个或更多客户。
我还想指出这个解决方案非常混乱。有许多活动部件,以及潜在的死锁的许多地方。这是人们使用管道,套接字和消息队列等IPC的原因的一个重要原因。您不必担心锁定,因为协调被烘焙到通信通道的设计中(默认情况下是读/写块),并且每个客户端都与服务器具有单独的通信通道。如果你担心性能,你应该看看this SO answer,它表明各种IPC机制在Linux上的速度大致相同。您可能会在其他类型的系统上看到不同的结果。