__sync_fetch_and_add() 的信号安全

时间:2021-06-03 11:59:24

标签: c multithreading signals atomic

我有一个接受套接字客户端并派生子进程的程序。为了保持子进程的计数,我使用了一个全局变量,原子函数用于增加和减少计数。

我担心 __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);
    }

1 个答案:

答案 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

在旧机器上,这确实会在添加指令期间锁定整个总线,而在新机器上,它会在微代码中默默地生成一个循环,反复尝试增加值。

任何中断(传递信号所必需的)只会延长加载和存储之间的时间,这使得当前迭代更有可能无法更新值,因为其他东西更快。