0

您好,我在使用 siglongjmp(多线程)和 sigaction 配置 SIGVTALRM 处理程序时遇到问题。

我已经配置了 sigsetjmp 2 个新线程: 1. env[0] 和 func f 上的 PC 2. env[1] 和 func g 上的 PC

我已经为 SIGVTALRM 信号配置了一个处理程序,以在两个线程之间切换。

我将 itimer_virtual 设置为调用一个名为“switchThreads”的函数,该函数在线程之间切换。

我测试了2个案例:

  1. 设置计时器并调用无限循环,它只调用一次信号,然后在 siglongjmp 之后停止调用处理程序。

  2. 设置计时器并在它之后调用 f,甚至不调用处理程序一次。

这是我的代码:

#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>

#define SECOND 1000000
#define STACK_SIZE 4096

char stack1[STACK_SIZE];
char stack2[STACK_SIZE];

sigjmp_buf env[2];

#ifdef __x86_64__
/* code for 64 bit Intel arch */

typedef unsigned long address_t;
#define JB_SP 6
#define JB_PC 7

/* A translation is required when using an address of a variable.
   Use this as a black box in your code. */
address_t translate_address(address_t addr)
{
    address_t ret;
    asm volatile("xor    %%fs:0x30,%0\n"
                 "rol    $0x11,%0\n"
    : "=g" (ret)
    : "0" (addr));
    return ret;
}

#else
/* code for 32 bit Intel arch */

typedef unsigned int address_t;
#define JB_SP 4
#define JB_PC 5 

/* A translation is required when using an address of a variable.
   Use this as a black box in your code. */
address_t translate_address(address_t addr)
{
    address_t ret;
    asm volatile("xor    %%gs:0x18,%0\n"
        "rol    $0x9,%0\n"
                 : "=g" (ret)
                 : "0" (addr));
    return ret;
}

#endif
static struct sigaction sa;
static struct itimerval timer;
static int currentThread = 0;

static void switchThreads(int sig)
{
    int ret_val = sigsetjmp(env[currentThread], 1);
    printf("SWITCH: ret_val=%d\n", ret_val);
    if (ret_val == 1)
    {
        return;
    }
    currentThread = 1 - currentThread;
    if (setitimer(ITIMER_VIRTUAL, &timer, NULL))
    {
        printf("setitimer error.");
    }
    siglongjmp(env[currentThread], 1);
}


void f(void)
{
    int i = 0;
    while (1)
    {
        ++i;
        printf("in f (%d)\n", i);
        usleep(SECOND);
    }
}

void g(void)
{
    int i = 0;
    while (1)
    {
        ++i;
        printf("in g (%d)\n", i);
        usleep(SECOND);
    }
}

void setup(void)
{
    address_t sp, pc;

    sp = (address_t) stack1 + STACK_SIZE - sizeof(address_t);
    pc = (address_t) f;
    sigsetjmp(env[0], 1);
    (env[0]->__jmpbuf)[JB_SP] = translate_address(sp);
    (env[0]->__jmpbuf)[JB_PC] = translate_address(pc);
    sigemptyset(&env[0]->__saved_mask);

    sp = (address_t) stack2 + STACK_SIZE - sizeof(address_t);
    pc = (address_t) g;
    sigsetjmp(env[1], 1);
    (env[1]->__jmpbuf)[JB_SP] = translate_address(sp);
    (env[1]->__jmpbuf)[JB_PC] = translate_address(pc);
    sigemptyset(&env[1]->__saved_mask);
}

int main(void)
{
    printf("Starting...");

    // Install timer_handler as the signal handler for SIGVTALRM.
    sa.sa_handler = &switchThreads;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGVTALRM, &sa, NULL) < 0)
    {
        printf("sigaction error.");
    }

    // Configure the timer to expire after 1 sec... */
    timer.it_value.tv_sec = 3;        
    timer.it_value.tv_usec = 0;        

    // configure the timer to expire every 1 sec after that.
    timer.it_interval.tv_sec = 1;    
    timer.it_interval.tv_usec = 0;   

    // Start a virtual timer. It counts down whenever this process is executing.
    if (setitimer(ITIMER_VIRTUAL, &timer, NULL))
    {
        printf("setitimer error.");
    }
    setup();
//    for(;;){}
    f();
}

我也有 2 个电话的 strace:

第一个(仅出现一次):

rt_sigaction(SIGVTALRM, {sa_handler=0x563ff48db82d, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f22c0f72f20}, NULL, 8) = 0
setitimer(ITIMER_VIRTUAL, {it_interval={tv_sec=1, tv_usec=0}, it_value={tv_sec=3, tv_usec=0}}, NULL) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
--- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_KERNEL} ---
rt_sigprocmask(SIG_BLOCK, NULL, [VTALRM], 8) = 0
write(1, "Starting...SWITCH: ret_val=0\n", 29Starting...SWITCH: ret_val=0
) = 29
setitimer(ITIMER_VIRTUAL, {it_interval={tv_sec=1, tv_usec=0}, it_value={tv_sec=3, tv_usec=0}}, NULL) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
write(1, "in g (1)\n", 9in g (1)
)               = 9
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in g (2)\n", 9in g (2)
)               = 9
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in g (3)\n", 9in g (3)
)               = 9

第二个(甚至没有发生一次):

rt_sigaction(SIGVTALRM, {sa_handler=0x55dc7274e82d, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe6e3768f20}, NULL, 8) = 0
setitimer(ITIMER_VIRTUAL, {it_interval={tv_sec=1, tv_usec=0}, it_value={tv_sec=3, tv_usec=0}}, NULL) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
write(1, "Starting...in f (1)\n", 20Starting...in f (1)
)   = 20
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in f (2)\n", 9in f (2)
)               = 9
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in f (3)\n", 9in f (3)
)               = 9
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in f (4)\n", 9in f (4)
)               = 9
nanosleep({tv_sec=1, tv_nsec=0}, NULL)  = 0
write(1, "in f (5)\n", 9in f (5)
)               = 9

提前致谢!!

4

1 回答 1

0

我已经设法解决了这个问题,uslepp 停止了线程的执行,因此计时器被配置为 1 秒,并且 2 个函数在它们的循环中使用 usleep,它从未达到 1 秒的运行时间。

来自沉睡之人:

The usleep() function suspends execution of the calling thread for
       (at least) usec microseconds.  The sleep may be lengthened slightly
       by any system activity or by the time spent processing the call or by
       the granularity of system timers.
于 2020-04-21T08:58:48.597 回答