我正在做一个测试设置,使用 UNIX 信号进行通信。 以下代码使用 SIGUSR1 和 SIGUSR2 将字符串从客户端进程传输到将侦听传入信号的服务器进程。我知道这不是信号的使用方式,我恳请您忽略任何“该函数不是信号安全的”,因为在此测试设置中,客户端将等待服务器通过信号确认每个接收到的位客户端将作为对该信号的应答,将下一位发送到服务器。 这样,在处理信号时,任何信号都不应中断。或者我认为至少...我现在对任何事情都不确定了...
问题是我突然收到来自pid 0的信号(这是广播?),但我从未发送广播。该代码适用于较小的字符串,但对于较大的字符串(~100 个字符),它有时会起作用,有时则不起作用。我可以通过发送->睡眠->在客户端发送来处理这种情况,这会比较慢,但我对这里发生的事情真的很感兴趣。
这是服务器代码:
void sig_handler(int _signum, siginfo_t *_info, void *_context)
{
static unsigned char str[MAX_MSG_SIZE];
static int str_i;
t_bool done;
int i;
if (_info->si_pid != 0)
{
if (((str_i++) * 0) || _signum == SIGUSR2)
str[(str_i - 1) / 8] |= 1 << (7 - (((str_i - 1) % 8)));
done = !(str_i % 8) && !str[(str_i / 8) - 1];
if (done)
{
i = ((str_i / 8 - 4) >= 0) * (str_i / 8 - 4)
+ ((str_i / 8 - 4) < 0) * -1;//this saves a line...
while (++i < str_i / 8)
if (str[i] == 0xf0)
done = FALSE;
}
if ((str_i / 8 == MAX_MSG_SIZE || done) && _info->si_pid != 0)
{
ft_printf("kill\n");
write(1, str, str_i / 8);
ft_memset(str, 0, MAX_MSG_SIZE);
str_i = 0;
kill(_info->si_pid, SIGUSR2);
}
else if (_info->si_pid != 0)
kill(_info->si_pid, SIGUSR1);
else ft_printf("wtf");
}
else ft_printf("\nOH WTF IS HAPPENING\n");
}
int main(void)
{
struct sigaction sa;
ft_printf("%d\n", getpid());
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = sig_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGUSR1, &sa, NULL) == -1
|| sigaction(SIGUSR2, &sa, NULL) == -1)
return (0);
while (TRUE)
sleep(10000);
return (0);
}
注意,我添加了一些打印语句来了解发生了什么。 这是客户:
typedef struct s_client_info
{
int pid;
char *str;
int i;
t_bool run;
} t_client_info;
void sig_handler(int _signum, siginfo_t *_info, void *_context)
{
static t_client_info *v;
//usleep(200);
if (_info && !_info->si_pid) //this prints 0-n times randomly at any moment
ft_printf("wtf");
if (!_signum && !_info) //setup
{
v = _context;
v->run = TRUE;
}
if (_signum == SIGUSR2 || !v->run) //received stop signal from server
{
v->run = FALSE;
return ;
}
if (v->pid == 0) //is never called
ft_printf("Wtf1");
if ((v->str[v->i / 8] << (v->i % 8)) & 0b10000000) //extracts current bit
kill(v->pid, SIGUSR2); //send '1' to server
else
kill(v->pid, SIGUSR1); //send '0' to server
v->i++;
}
int main(int _argc, char *_argv[])
{
t_client_info v;
struct sigaction sa;
int i;
//check input
i = -1;
while (_argv[1][++i])
if (_argv[1][i] < '0' || _argv[1][i] > '9')
return (print_usage(), 0);
if (_argc != 3)
return (print_usage(), 0);
//prepare client info
v.pid = ft_atoi(_argv[1]);
v.str = _argv[2];
v.i = 0;
//register signal handler
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = sig_handler;
if (sigaction(SIGUSR1, &sa, NULL) == -1
|| sigaction(SIGUSR2, &sa, NULL) == -1)
return (0);
//start sending
sig_handler(0, NULL, &v);
while (v.run)
usleep(10);
return (0);
}
如果我取消注释 usleep,通信将会工作,但客户端有时仍会收到来自 pid 0 的信号并打印“wtf”
我想也许另一个进程正在随机发送广播,所以我设置了一个额外的进程来检查......不,没有其他进程。不知何故,其中之一或两者最终发送了格式错误的信号。
这是服务器的一些输出。连续 3 次收到相同的消息后,它将失败并且永远不会向客户端发送停止信号(SIGUSR2)
😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuhkill
😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh
天哪,发生了什么
此时我需要手动停止客户端。
所以我在客户端添加了这个,并再次启用了 usleep 来检查奇怪的 pid = 0 信号是否在某个特定时间出现:
if ((v->str[v->i / 8] << (v->i % 8)) & 0b10000000)
ft_printf("1"),kill(v->pid, SIGUSR2);
else
ft_printf("0"),kill(v->pid, SIGUSR1);
而且它们只是随机出现。这是一个例子:
./client 19863
😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh😀wiegyhweoufgiaqefouhqaefouhqofsuh
111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001110110100101100001011100010110010101100110011011110111010101101000011100010110000101100101011001100110111101110101011010000111000101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110111101110101011001100110011101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110001011011110110011001110011011101010110100011110000100111111001100010000000011101110110100101100101011001110111100101101000011101110110010101101111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100110011100110111010101101000111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001110110100101100001011100010110010101100110011011110111010101101000011100010110000101100101011001100110111101110101011010000111000101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110111101110101011001100110011101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110
wtf
00101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110111101110101011001100110011101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110001011011110110011001110011011101010110100011110000100111111001100010000000011101110110100101100101011001110111100101101000011101110110010101101111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100110011100110111010101101000111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001110110100101100001011100010110010101100110011011110111010101101000011100010110000101100101011001100110111101110101011010000111000101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110111101110101011001100110011101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110001011011110110011001110011011101010110100011110000100111111001100010000000011101110110100101100101011001110111100101101000011101110110010101101111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100110011100110111010101101000111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001110110100101100001011100010110010101100110011011110111010101101000011100010110000101100101011001100110111101110101011010000111000101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110111101110101011001100110011101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110001011011110110011001110011011101010110100011110000100111111001100010000000011101110110100101100101011001110111100101101000011101110110010101101111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100110011100110111010101101000111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001110110100101100001011100010110010101100110011011110111010101101000011100010110000101100101011001100110111101110101011010000111000101101111011001100111001101110101011010001111000010011111100110001000000001110111011010010110010101100111011110010110100001110111011001010110
wtf
1111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100110011100110111010101101000111100001001111110011000100000000111011101101001011001010110011101111001011010000111011101100101011011110111010101100110011001
wtf
1101101001011000010111000101100101011001100110111101110101011010000111000101100001011001010110011001101111011101010110100001110001011011110110011001110011011101010110100011110000100111111001100010000000011101110110100101100101011001110111100101101000011101110110010101101111011101010110011001100111011010010110000101110001011001010110011001101111011101010110100001110001011000010110010101100110011011110111010101101000011100010110111101100
wtf
11001110011011101010110100000000000
这是客户端中没有 usleep 的运行的另一个屏幕截图:
服务器左,客户端右。 之后,我在没有重新启动服务器的情况下向服务器发送了一条小消息,它突然不知从何而来发送了一条非常大的消息。仅包含 ./client pid asd 的 0 和 1 页面。 这是服务器的反应: 最上面的消息是最后一张图片中的最后一条消息
有人知道发生了什么事吗? 我认为通过总是对信号做出反应,我不应该进入任何竞争状态,但这种行为超出了我的想象。
更新:
我曾尝试使用相关问题dimich评论中的解决方案(SIGINFO的si_pid在多次调用同一函数后将其自身设置为0) 我将 pid 保存到服务器中的静态变量中,通信突然在没有 usleep 的情况下正常工作。然而,客户端仍然收到 _info->si_pid == 0 的信号。更有趣的是:服务器发送了 100% 的信号。它们是服务器发送的 ack。如果我将它们过滤掉,客户端将挂起等待确认。顺便说一句,我也在 Mac 上。
在客户端的
sig_handler
结束时,您首先向服务器发出信号,然后递增 v->i
。考虑 sig_handler
的第一次调用是来自 main
的调用,其中 SIGUSR1
未被阻止;客户端可能会在 kill
和 v->i++
之间被抢占,服务器可能会在客户端恢复之前以 SIGUSR1
进行响应,导致其处理程序重新进入并再次发送消息的第一位,从而破坏传输。为了避免这种情况,请在 v->i
调用之前递增 kill
。