【发布时间】:2021-12-25 01:11:50
【问题描述】:
我有一个学校的任务,我需要编写一个客户端,它将使用信号(仅 SIGUSR1 为 1 或 SIGUSR2 为 0)向服务器发送字符串,然后服务器应显示该字符串。问题是服务器通常只能处理少量的符号。在其他情况下,它会在客户端暂停或仅显示奇怪的符号。我尝试使用客户端中的全局变量以不同的方式编写以从服务器确认(有和没有暂停客户端),使用usleep()(100/600/1000/10000),使用pause(),结果总是就像我一次又一次快速呼叫客户时一样。
我正在使用 VirtualBox Ubuntu 编写它。
UPD:问题解决了在客户端使用sleep (5)而不是pause()并将usleep()增加到1000
客户:
#include "minitalk.h"
int g_recieve;
void sending_bits(char c, int pid)
{
int i;
i = 128;
while(i >= 1)
{
if (g_recieve == 1)
{
if (i & c)
{
if (kill(pid, SIGUSR1) == -1)
errors("Error in sending signal!\n");
}
else
{
if (kill(pid, SIGUSR2) == -1)
errors("Error in sending signal!\n");
}
i /= 2;
g_recieve = 0;
}
//usleep(600);
}
}
int send_str(int pid, char *s)
{
int i;
i = 0;
while (s[i])
{
sending_bits(s[i], pid);
i++;
}
return (0);
}
void cl_handler(int signum, siginfo_t *siginfo, void *context)
{
(void)context;
(void)siginfo;
(void)signum;
g_recieve = 1;
write(1, "Recieved signal from server\n", 28);
return ;
}
int main(int argc, char **argv)
{
struct sigaction sigac;
g_recieve = 1;
sigemptyset(&sigac.sa_mask);
sigaddset(&sigac.sa_mask, SIGINT);
sigaddset(&sigac.sa_mask, SIGQUIT);
sigaddset(&sigac.sa_mask, SIGUSR1);
sigac.sa_flags = SA_SIGINFO;
sigac.sa_sigaction = cl_handler;
if (sigaction(SIGUSR2, &sigac, NULL) == -1)
errors("Error in client sigaction\n");
if (ft_atoi(argv[1]) < 0)
errors("Wrong PID!\n");
if (argc == 3)
send_str(ft_atoi(argv[1]), argv[2]);
else
errors("Wrong arguments!\n");
while (1)
pause ();
return (0);
}
服务器:
#include "minitalk.h"
void sv_handler(int signum, siginfo_t *siginfo, void *unused)
{
static int ascii = 0;
static int power = 0;
(void)unused;
if (signum == SIGUSR1)
ascii += (128 >> power);
power += 1;
if (power == 8)
{
ft_putchar(ascii);
power = 0;
ascii = 0;
}
if (siginfo->si_pid == 0)
errors("Server didn't get client's PID\n");
if (kill(siginfo->si_pid, SIGUSR2) == -1)
errors("Error in returning signal!\n");
}
int main(int argc, char **argv)
{
struct sigaction sigac;
(void)argv;
if (argc != 1)
errors("Error arguments\n");
write(1, "Server started!\nPID: ", 21);
ft_putnbr(getpid());
write(1, "\n", 1);
sigemptyset(&sigac.sa_mask);
//sigaddset(&sigac.sa_mask, SIGINT);
//sigaddset(&sigac.sa_mask, SIGQUIT);
sigac.sa_flags = SA_SIGINFO;
sigac.sa_sigaction = sv_handler;
if ((sigaction(SIGUSR1, &sigac, 0)) == -1)
errors("Error sigaction\n");
if ((sigaction(SIGUSR2, &sigac, 0)) == -1)
errors("Error sigaction\n");
while (1)
pause();
return (0);
}
【问题讨论】:
-
你的导师非常刻意地预先考虑到恶意,给你分配了一项道德上相当于用锤子拧螺丝的任务。信号用于事件,而不用于数据。当您尝试发送大量信号时,您总是会遇到可怕的问题,因为它们不能很好地排队,因为它们从来都不是为 IPC 设计的。
-
@SteveSummit 是对的:信号非常非常不适合这个问题。 “快速调用客户端一次又一次”是什么意思?您的意思是您多次调用客户端程序,同时或重叠,每次都使用相同的服务器 PID 作为其目标?如果没有客户之间的某种协调,这将是行不通的。
-
@Shawn 感谢您花时间挖掘链接。但是,[我相信你已经知道]它充满了竞争条件。发件人
usleep可以与收件人竞争。不能保证发送者不会发送(例如)两个SIGUSR1,被接收者视为单个SIGUSR1。或者,这两个信号是按顺序传递的。在接收器中,它至少需要volatile和/或原子处理程序/主同步。回复:man7.org/linux/man-pages/man7/signal.7.html 具有讽刺意味的是,如果使用 Posix/RT 信号,它们保证排队并按顺序传递。 -
@CraigEstey 是的,这很糟糕。在问题的约束范围内没有好的方法。
标签: c linux unix signals sigaction