我有一个接受套接字客户端并派生子进程的程序。为了保持子进程的计数,我使用了一个全局变量,原子函数用于增加和减少计数。
我担心 __sync_fetch_and_add() 的信号安全性。多个子节点频繁连接和断开时是否有死锁的可能。
下面是我的代码片段。
int main()
{
...
signal(SIGCHLD, handle_sigchild);
...
if ((pid = fork()) == -1) {
close(sock);
continue;
} else if (pid > 0) { //Parent
__sync_fetch_and_add(&counter, 1);
close(sock);
continue;
} else if (pid == 0) {
...
}
...
}
// Signal Handler
void handle_sigchild(int arg)
{
while( waitpid(-1, NULL, WNOHANG) > 0){
__sync_fetch_and_add(&counter, -1);
}
signal(SIGCHLD, handle_sigchild);
}
答案 0 :(得分:0)
原子函数通常被实现为围绕执行实际操作的关键指令的循环,并且可能会失败,在这种情况下需要重试。
任何冲突都会导致关键指令失败,信号处理程序引入的冲突也是如此。
例如,fetch-and-add 通常类似于
.L2:
lwarx 10,0,3
addi 9,10,1
stwcx. 9,0,3
bne 0,.L2
extsw 3,10
blr
因此该值被加载并进行预订,然后递增,如果预订仍然有效,则将其存储回来。如果失败,我们会重新开始获取新值(因为很可能有人在此期间写入)。
在Intel上,这个过程隐藏在里面
mov eax, 1
lock xadd DWORD PTR [rdi], eax
ret
在旧机器上,这确实会在添加指令期间锁定整个总线,而在新机器上,它会在微代码中默默地生成一个循环,反复尝试增加值。
任何中断(传递信号所必需的)只会延长加载和存储之间的时间,这使得当前迭代更有可能无法更新值,因为其他东西更快。