linux 平台上 C# 中的自定义 posix 信号不起作用

Custom posix signal in C# on a linux platform does not work

问题

我正在尝试将 linux 中的信号发送到 Linux 中 dotnet 核心中的 C# 线程。我的测试代码适用于 SIGARAM,但不适用于映射到 SIGRTMIN# 的自定义信号。 sigwait 在线程上永远不会 returns 当主线程引发自定义信号时。

C++ 版本运行良好。 C++ 和 C# 版本几乎相同。谁能弄清楚为什么它不起作用?

我的最终目标是捕捉来自设备驱动程序的信号。

C++ 版本

//https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/sigwaiti.htm
//g++ -Wall -O2 example.cpp -o example -pthread

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <thread>

void catcher( int sig ) {
    printf( "Signal catcher called for signal %d\n", sig );
}

void timestamp( const char *str ) {
    time_t t;

    time( &t );
    printf( "The time %s is %s\n", str, ctime(&t) );
}

void on_sigusr1(int sig)
{
  // Note: Normally, it's not safe to call almost all library functions in a
  // signal handler, since the signal may have been received in a middle of a
  // call to that function.
  printf("SIGUSR1 received!\n");
}

#define SIG_SYSTEM (SIGRTMIN+7)

void run()
{
    struct sigaction sigact;
    sigset_t waitset;
    siginfo_t info;

    sigemptyset( &sigact.sa_mask );
    sigact.sa_flags = 0;
    sigact.sa_handler = catcher;
    sigaction( SIG_SYSTEM, &sigact, NULL );

    sigemptyset( &waitset );
    sigaddset( &waitset, SIG_SYSTEM );

    sigprocmask( SIG_BLOCK, &waitset, NULL );

    timestamp( "before sigwaitinfo()" );

//    int result = sigwaitinfo( &waitset, &info );
    int sig;
    int result = sigwait( &waitset, &sig );

    if( sig == SIG_SYSTEM )
        printf( "sigwaitinfo() returned for signal %d\n",
                 info.si_signo );
    else {
        printf( "sigwait() sig %d\n", sig );
        printf( "sigwait() returned code %d\n", result );
        printf( "sigwait() returned error number %d\n", errno );
        perror( "sigwait() function failed\n" );
    }

    timestamp( "after sigwaitinfo()" );
}

int main( int argc, char *argv[] ) {

    int result = 0;

    signal(SIG_SYSTEM, &on_sigusr1);

    sigset_t waitset;
    sigemptyset( &waitset );
    sigaddset( &waitset, SIG_SYSTEM );

    sigprocmask( SIG_BLOCK, &waitset, NULL );

    std::thread tx(run);

    sleep(5);
    //alarm( 5 );
    //result = raise(SIG_SYSTEM);
    result = kill(getpid(), SIG_SYSTEM);
    printf( "kill(%d) %d\n", SIG_SYSTEM, result);

    tx.join();

    return( result );
}

C# 版本

但是 C# 中的测试程序不起作用。

using System;

using static Tmds.Linux.LibC;
using Tmds.Linux;
using System.Runtime.InteropServices;

delegate void SignalCallback(int sig);

namespace example
{
    class Program
    {
        static void catcher(int sig)
        {
            Console.WriteLine($"Signal catcher called for signal {sig}");
        }

        static void timestamp(string str)
        {
            Console.WriteLine($"The time {str} is {DateTime.Now}");
        }

        static System.Threading.Thread Thread;

        static int SIG_SYSTEM = SIGRTMIN + 7;

        static unsafe void Run()
        {
            int result = 0;

            sigaction sigact = new sigaction();
            sigset_t waitset = new sigset_t();
            siginfo_t info = new siginfo_t();

            sigset_t* ptr = &sigact.sa_mask;
            sigemptyset(&sigact.sa_mask);
            sigact.sa_flags = 0;

            SignalCallback callback = new SignalCallback(catcher);
            sigact.sa_handler = (void*)Marshal.GetFunctionPointerForDelegate(callback);

            sigaction(SIG_SYSTEM, &sigact, null);

            sigemptyset(&waitset);
            sigaddset(&waitset, SIG_SYSTEM);

            sigprocmask(SIG_BLOCK, &waitset, null);

            timestamp("before sigwaitinfo()");

            //result = sigwaitinfo(&waitset, &info);
            int sig;
            result = sigwait(&waitset, &sig);

            // after it's last usage by unmanaged code
            GC.KeepAlive(callback);

            if (sig == SIG_SYSTEM)
                Console.WriteLine($"sigwaitinfo() returned for signal {info.si_signo}");
            else
            {
                Console.WriteLine($"sigwait() sig {sig}");
                Console.WriteLine($"sigwait() returned code {result}");
                Console.WriteLine($"sigwait() returned error number {errno}");
                Console.WriteLine("sigwait() function failed");
            }

            timestamp("after sigwaitinfo()");
        }

        static unsafe void Main(string[] args)
        {
            sigset_t waitset = new sigset_t();
            sigemptyset(&waitset);
            sigaddset(&waitset, SIG_SYSTEM);

            sigprocmask(SIG_BLOCK, &waitset, null);

            Thread = new System.Threading.Thread(Run);
            Thread.Start();

            //alarm(10);
            sleep(5);
            int result = kill(getpid(), SIG_SYSTEM);
            Console.WriteLine($"kill({SIG_SYSTEM}) {result}");

            Thread.Join();
        }
    }
}

环境

我能够在写入一个小的 .so 文件时收到信号。

static void (*_callback)(int, int);

void catcher(int sig, siginfo_t* info, void* context) {
    int* sival_int = &info->si_int;
    _callback(sig, sival_int[1]);
}

extern "C" void setaction(int sig, void *callback)
{
    _callback = (void(*)(int, int))callback;

    struct sigaction sigact;
    sigemptyset(&sigact.sa_mask);

    sigact.sa_flags = SA_SIGINFO;
    sigact.sa_sigaction = catcher;
    sigaction(sig, &sigact, NULL);
}
delegate void SignalCallback(int sig, int info);

static SignalCallback cb = Callback;

[DllImport("signalhandle.so")]
static extern void setaction(int sig, SignalCallback callback);

//Call this on the main thread.
public static void Start()
{
    setaction(SIG_SYSTEM, cb);
}

private static void Callback(int sig, int info)
{
}