向信号处理程序提供/传递参数

发布于 2024-11-28 16:16:35 字数 406 浏览 2 评论 0原文

我可以向信号处理程序提供/传递任何参数吗?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */

现在,处理程序如下所示:

void signal_handler(int signo) {
    /* some code */
}

如果我想做一些特殊的事情,即删除临时文件,我可以将这些文件作为参数提供给该处理程序吗?

编辑0:感谢您的回答。我们通常避免/不鼓励使用全局变量。在这种情况下,如果您有一个庞大的程序,不同的地方可能会出现问题,您可能需要进行大量的清理工作。为什么API要这样设计?

Can I provide/pass any arguments to signal handler?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */

Now, handler looks like this:

void signal_handler(int signo) {
    /* some code */
}

If I want to do something special i.e. delete temp files, can I provide those files as an argument to this handler?

Edit 0: Thanks for the answers. We generally avoid/discourage use of global variables. And in this case, If you have a huge program, things can go wrong at different places and you might need to do a lot of cleanup. Why was the API designed this way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

零時差 2024-12-05 16:16:35

您不能将自己的数据作为参数传递给信号处理程序。相反,您必须将参数存储在全局变量中。 (如果您在安装信号处理程序后需要更改这些数据,请务必非常小心)。

回复编辑0:历史原因。信号是一种非常古老且非常低级的设计。基本上,您只是为内核提供了一些机器代码的单个地址,并要求它在发生这种情况时转到这个特定地址。我们又回到了“便携式汇编器”的思维模式,内核提供了一种简单的基线服务,无论用户进程可以合理地期望自己做什么,它都必须自己做。

此外,针对全局变量的常见论点在这里并不适用。信号处理程序本身是一个全局设置,因此不存在为其提供多组不同的用户指定参数的相关可能性。 (嗯,实际上它并不完全是全局的,而只是线程全局的。但是线程 API 将包含一些线程本地存储的机制,这正是本例中您所需要的)。

You can't have data of your own passed to the signal handler as parameters. Instead you'll have to store your parameters in global variables. (And be really, really careful if you ever need to change those data after installing the the signal handler).

Response to edit 0: Historical reasons. Signals are a really old and really low-level design. Basically you're just given the kernel a single address to some machine code and asking it to go to this specific address if such and such happens. We're back in the "portable assembler" mindset here, where the kernels provide a no-frills baseline service, and whatever the user process can reasonably be expected to to for itself, it must do itself.

Also, the usual arguments against global variables don't really apply here. The signal handler itself is a global setting, so there is no relevant possibility of having several different sets of user-specified parameters for it around. (Well, actually it is not entirely global but only thread-global. But the threading API will include some mechanism for thread-local storage, which is just what you need in this case).

感情洁癖 2024-12-05 16:16:35

信号处理程序注册已经是一个全局状态,相当于全局变量。因此,使用全局变量向其传递参数并没有什么更大的冒犯。然而,无论如何,从信号处理程序中执行任何操作都是一个巨大的错误(几乎肯定未定义的行为,除非您是专家!)。如果您只是阻止信号并从主程序循环中轮询它们,则可以避免所有这些问题。

A signal handler registration is already a global state equivalent to global variables. So it's no greater offense to use global variables to pass arguments to it. However, it's a huge mistake (almost certainly undefined behavior unless you're an expert!) to do anything from a signal handler anyway. If you instead just block signals and poll for them from your main program loop, you can avoid all these issues.

凉世弥音 2024-12-05 16:16:35

这是一个非常古老的问题,但我想我可以向您展示一个可以回答您的问题的好技巧。
无需使用 sigqueue 或其他任何东西。

我也不喜欢使用全局变量,因此就我而言,我必须找到一种聪明的方法来发送 void ptr(您可以稍后将其转换为适合您需要的任何内容)。

实际上你可以这样做:

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc

那么你的叹息处理程序将如下所示:

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !

你可能会问: 那么如何获取 *ptr 呢?

方法如下:
初始化时

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);

在您的ighandler函数中
:

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}

This is a really old question but I think I can show you a nice trick that would have answered your problem.
No need to use sigqueue or whatever.

I also dislike the use of globals variables so I had to find a clever way, in my case, to send a void ptr (which you can later cast to whatever suits your need).

Actually you can do this :

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc

Then your sighandler would look like this :

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !

You might ask : How to get the *ptr then ?

Here's how :
At initialization

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);

In your sighandler func
:

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}
赠佳期 2024-12-05 16:16:35

绝对地。您可以使用 sigqueue() 而不是通常的kill() 将整数和指针传递给信号处理程序。

http://man7.org/linux/man-pages/man2/sigqueue .2.html

Absolutely. You can pass integers and pointers to signal handlers by using sigqueue() instead of the usual kill().

http://man7.org/linux/man-pages/man2/sigqueue.2.html

行至春深 2024-12-05 16:16:35

将文件名存储在全局变量中,然后从处理程序访问它。信号处理程序回调只会传递一个参数:导致问题的实际信号的 ID(例如 SIGINT、SIGTSTP)

编辑 0:“必须有一个坚定的理由不允许向处理程序提供参数。” <-- 有一个中断向量(基本上,是每个可能信号的例程的一组跳转地址)。给定中断触发方式,根据中断向量,调用特定函数。不幸的是,尚不清楚与变量关联的内存将在哪里被调用,并且根据中断,内存实际上可能会被损坏。有一种方法可以解决这个问题,但是您无法利用现有的 int 0x80 汇编指令(某些系统仍在使用)

Store the names of the files in a global variable and then access it from the handler. The signal handler callback will only be passed one argument: the ID for the actual signal that caused the problem (eg SIGINT, SIGTSTP)

Edit 0: "There must be a rock solid reason for not allowing arguments to the handler." <-- There is an interrupt vector (basically, a set of jump addresses to routines for each possible signal). Given the way that the interrupt is triggered, based on the interrupt vector, a particular function is called. Unfortunately, it's not clear where the memory associated with the variable will be called, and depending on the interrupt that memory may actually be corrupted. There is a way to get around it, but then you can't leverage the existing int 0x80 assembly instruction (which some systems still use)

萌吟 2024-12-05 16:16:35

我认为你最好在 sa_flags 中使用 SA_SIGINFO 这样处理程序就会得到
void signal_handler(int sig, siginfo_t *info, void *secret)
在 siginfo_t 中,您可以提供您的参数。
Ty:HAPPY代码

I think you it's better to use SA_SIGINFO in sa_flags so the handler will get
void signal_handler(int sig, siginfo_t *info, void *secret)
in siginfo_t you can provide your params.
Ty:HAPPY code

迷雾森÷林ヴ 2024-12-05 16:16:35

尽管这是一个非常古老的问题,但这个问题今天仍然存在。
基本上是为了关闭临时文件、正确终止线程等。例如,可以使用以下逻辑:

volatile sig_atomic_t sig_received = 0;

void sigterm_handler(int signum)
{
    printf("SIGTERM. PID: %d\n", getpid());
    sig_received = 1;
}
    void sigint_handler(int signum)
{
    fprintf(stderr, "SIGINT. PID: %d\n", getpid());
}

...

int main()
{
    struct sigaction action;
    action.sa_handler = sigterm_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    sigaction(SIGTERM, &action, NULL);

    action.sa_handler = sigint_handler;
    sigaction(SIGINT, &action, NULL);

    pthread_t writer_thread, reader_thread;
    struct master_argv writer_args, reader_args;

    buffer_init(&(writer_args.buffer));
    buffer_init(&(reader_args.buffer));

    writer_args.pipename = PIPE_CAPOSC;
    reader_args.pipename = PIPE_CAPOLET;

    if (pthread_create(&writer_thread, NULL, master, (void *)&writer_args) != 0)
    {
        exit(1);
    }

    if (pthread_create(&reader_thread, NULL, master, (void *)&reader_args) != 0)
    {
        exit(1);
    }

    while (!sig_received)
    {
        sleep(1);
    }

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    buffer_destroy(&(writer_args.buffer));
    buffer_destroy(&(reader_args.buffer));

    return 0;
}

基本上,在信号管理器中,设置了一个 sig_atomic_t 标志,它保证对此变量和 的原子访问>volatile 向编译器发出信号,表明该变量不得优化,因为它可能会发生意外的更改,例如信号的修改。

使用此标志,您可以通过不使用全局变量以安全的方式处理闭包(在一系列线程的示例中)

Despite the very old question the problem still exists today.
Basically to close temporary files, terminate threads correctly, etc. for example, this logic could be used:

volatile sig_atomic_t sig_received = 0;

void sigterm_handler(int signum)
{
    printf("SIGTERM. PID: %d\n", getpid());
    sig_received = 1;
}
    void sigint_handler(int signum)
{
    fprintf(stderr, "SIGINT. PID: %d\n", getpid());
}

...

int main()
{
    struct sigaction action;
    action.sa_handler = sigterm_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    sigaction(SIGTERM, &action, NULL);

    action.sa_handler = sigint_handler;
    sigaction(SIGINT, &action, NULL);

    pthread_t writer_thread, reader_thread;
    struct master_argv writer_args, reader_args;

    buffer_init(&(writer_args.buffer));
    buffer_init(&(reader_args.buffer));

    writer_args.pipename = PIPE_CAPOSC;
    reader_args.pipename = PIPE_CAPOLET;

    if (pthread_create(&writer_thread, NULL, master, (void *)&writer_args) != 0)
    {
        exit(1);
    }

    if (pthread_create(&reader_thread, NULL, master, (void *)&reader_args) != 0)
    {
        exit(1);
    }

    while (!sig_received)
    {
        sleep(1);
    }

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    buffer_destroy(&(writer_args.buffer));
    buffer_destroy(&(reader_args.buffer));

    return 0;
}

Basically, within the signal manager, a sig_atomic_t flag is set which guarantees atomic access to this variable and volatile to signal to the compiler that this variable must not be optimized since it could undergo unexpected changes such as the modification by a signal .

Using this flag you can handle the closure, in the example of a series of threads, in a safe way by not using global variables

回忆凄美了谁 2024-12-05 16:16:35

您可以使用信号处理程序,它是类的方法。然后该处理程序可以访问该类的成员数据。我不完全确定 Python 在 C signal() 调用周围做了什么,但它一定是重新界定数据范围?

我很惊讶这有效,但确实如此。运行此命令,然后从另一个终端终止该进程。

import os, signal, time

class someclass:
    def __init__(self):
        self.myvalue = "something initialized not globally defined"
        signal.signal(signal.SIGTERM, self.myHandler)
    def myHandler(self, s, f):
        # WTF u can do this?
        print "HEY I CAUGHT IT, AND CHECK THIS OUT", self.myvalue


print "Making an object"
a = someclass()

while 1:
    print "sleeping.  Kill me now."
    time.sleep(60)

You can use a signal handler which is a method of a class. Then that handler can access member data from that class. I'm not entirely sure what Python does under the covers here around the C signal() call, but it must be re-scoping data?

I was amazed that this works, but it does. Run this and then kill the process from another terminal.

import os, signal, time

class someclass:
    def __init__(self):
        self.myvalue = "something initialized not globally defined"
        signal.signal(signal.SIGTERM, self.myHandler)
    def myHandler(self, s, f):
        # WTF u can do this?
        print "HEY I CAUGHT IT, AND CHECK THIS OUT", self.myvalue


print "Making an object"
a = someclass()

while 1:
    print "sleeping.  Kill me now."
    time.sleep(60)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文