草庐IT

c - 等待信号,然后继续执行

coder 2023-06-21 原文

我正在尝试制作一个暂停其执行直到信号到达的程序。然后,在信号到达后,我只想让我的代码从原来的地方继续执行。我不希望它执行函数处理程序或其他任何东西。有没有简单的方法可以做到这一点?我已经苦苦挣扎了一个星期左右,在这里和那里阅读,并没有设法获得一个完全可操作的代码。

特别是,我希望主程序创建一个线程 等待某个特定事件的发生(例如,用户向标准输入输入了一些数据)。与此同时,主程序正在做一些事情,但在某个时候它会暂停执行,直到它收到一个信号。

信号可能来自线程,因为它已检测到事件,也可能是由于超时,因为我没有想要它永远等待。

我已经编写了一些代码,但它没有按预期工作......

/*
 * This code SHOULD start a thread that gets messages from stdin.
 *  If the message is a "quit", the thread exits. Otherwise it raises
 *  a signal that should be caught by the main program.
 *  The main program simply waits for the message unless a timer of
 *  5.5 seconds expires before receiving the signal from the thread.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <signal.h>

#define BSIZE 100   /* Buffer size */

sigset_t mask;              
pthread_t tid;
//struct itimerval timervalue;

int milisec = 5500; /* Timeout: 5,5 seconds */
int end = 0;

char buffer[BSIZE];


//Function prototypes
void init(void);
void * thread_job(void *);
void message_rcvd(void);
void wait_for_message_or_timeout(void);
int start_timer(struct itimerval, int);
int stop_timer(struct itimerval);
void on_signal(int);


// MAIN: Wait for message or timeout
int main(int argc, char ** argv) {

    init();

    while(!end){
        wait_for_message_or_timeout();
        if(!end)
            printf("Message received [%s]\n", buffer);
    }

    return 0;
}


// INIT: Initializes the signals that the program will wait for
//       and creates a thread that will eventually generate a signal
void init()
{

    /* Init the signals I want to wait for with sigwait() */
    sigemptyset(&mask);         
    sigaddset(&mask, SIGUSR1);  
    sigaddset(&mask, SIGALRM);
    sigprocmask(SIG_BLOCK, &mask, NULL);

    //signal(SIGUSR1, SIG_IGN);
    signal(SIGUSR1, on_signal);

    /* Create the thread and put it to work */
    pthread_t tid;
    pthread_create(&tid, NULL, thread_job, NULL);

}

void on_signal(int signum){
    printf("on_signal\n");
}

// THREAD CODE -------------
// THREAD JOB: When the user inputs a message, it passes the message
//              to the main thread by invoking message_rcvd()
void * thread_job(){

    int end = 0;

    while(!end){
        printf("Input message:");
        if (fgets(buffer, BSIZE, stdin) != NULL)
            message_rcvd();
    }
}

// MESSAGE RECEIVED: If message is not equal to "quit" raise a signal
void message_rcvd(){

    if(strcmp(buffer, "quit") == 0){
        exit(0);
    }else{
        printf("Going to raise SIGUSR1...");
        if(raise(SIGUSR1) == 0)
            printf("raised!\n");
    }

}


// WAIT: Should wait for signal SIGUSR1 for some time
void wait_for_message_or_timeout(){

    int sigid;  
    struct itimerval t;

    /* Set a timer to prevent waiting for ever*/
    printf("Setting timer...\n");
    start_timer(t, milisec);

    /* Put the process to wait until signal arrives */
    sigwait(&mask, &sigid);

    switch(sigid){
        case SIGUSR1:
                printf("Received SIGUSR1: Message avaible!\n");
                break;
        case SIGALRM:
                printf("Received SIGALRM: Timeout\n");
                end = 1;
                break;
        default:
                printf("Unknown signal received\n");
                break;
    }

    printf("Stopping timer...\n");
    /* Stop timer */
    stop_timer(t);
}

// START TIMER: I don't want the timer to cause the execution
//              of a handler function 
int start_timer(struct itimerval timervalue, int msec)
//int start_timer(int msec)
{

  timervalue.it_interval.tv_sec = msec / 1000;
  timervalue.it_interval.tv_usec = (msec % 1000) * 1000;
  timervalue.it_value.tv_sec = msec / 1000;
  timervalue.it_value.tv_usec = (msec % 1000) * 1000;

  if(setitimer(ITIMER_REAL, &timervalue, NULL))
  {
    printf("\nsetitimer() error\n");
    return(-1);
  }
  return(0);
}

// STOP TIMER: 
int stop_timer(struct itimerval timervalue)
//int stop_timer()
{
  timervalue.it_interval.tv_sec = 0;
  timervalue.it_interval.tv_usec = 0;
  timervalue.it_value.tv_sec = 0;
  timervalue.it_value.tv_usec = 0;

  if(setitimer(ITIMER_REAL, &timervalue, NULL))
  {
    printf("\nsetitimer() error\n");
    return(-1);
  }
  return(0);

}

这是这段代码的典型执行。

./signaltest 
Setting timer...
Input message:hello
Going to raise SIGUSR1...raised!
Input message:friend
Going to raise SIGUSR1...raised!
Input message:Received SIGALRM: Timeout
Stopping timer...

如您所见,信号 SIGUSR1 被提升并且 sigwait 被解锁。但是,代码似乎在发出信号后不会继续。 (请注意,我不需要信号处理程序,但我只是为了调试目的而添加的。我已经使用 sigprocmask 阻止了它的执行)

为什么 SIGUSR1 解除了对 sigwait 的阻塞,但执行没有从那里继续?有没有办法让它在解封后继续?这似乎适用于 SIGALRM,但为什么不适用于 SIGUSR1?

正如我所说,我一直在查看大量的 stackoverflow 问题、在线 howto、尝试使用不同的系统调用(例如,暂停、sigsuspend),...但找不到解决这个问题的方法:-(

如果您想知道为什么我不通过不使用线程来简化此代码,那是因为这实际上不是我正在实现的代码,而只是一个更简单的示例,可以使我的问题更清楚。我实际上正在尝试实现一个网络协议(protocol) API,类似于我自己的协议(protocol)的套接字 API。

提前致谢

最佳答案

SIGUSR1 信号没有到达您认为的位置。

在多线程程序中,raise 函数向当前线程 发送信号,在本例中为thread_job 线程。所以主线程永远不会看到信号。

您需要保存主线程的线程 ID,然后使用 pthread_kill 向该线程发送信号。

添加一个新的全局:

pthread_t main_tid;

然后在您的 init 函数中填充它在启动新线程之前:

void init()
{
    main_tid = pthread_self();
    ...

然后在message_rcvd中,使用pthread_kill:

    if(pthread_kill(main_tid, SIGUSR1) == 0)
        printf("raised!\n");

另外,去掉thread_jobend的定义,去掉inittid的定义。这些定义掩盖了同名的全局变量。

示例输出:

Setting timer...
Input message:hello
Going to raise SIGUSR1...raised!
Input message:Received SIGUSR1: Message avaible!
Stopping timer...
Message received [hello
]
Setting timer...
test
Going to raise SIGUSR1...raised!
Input message:Received SIGUSR1: Message avaible!
Stopping timer...
Message received [test
]
Setting timer...
Received SIGALRM: Timeout
Stopping timer...

关于c - 等待信号,然后继续执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47658347/

有关c - 等待信号,然后继续执行的更多相关文章

  1. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  2. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  3. ruby - 按值降序排列散列,然后按升序键入 ruby - 2

    我有这样的哈希trial_hash={"key1"=>1000,"key2"=>34,"key3"=>500,"key4"=>500,"key5"=>500,"key6"=>500}我按值降序排列:my_hash=trial_hash.sort_by{|k,v|v}.reverse我现在是这样理解的:[["key1",1000],["key4",500],["key5",500],["key6",500],["key3",500],["key2",34]]但我希望当值相同时按键的升序排序。我该怎么做?例如:上面的散列将以这种方式排序:[["key1",1000],["key3",500

  4. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  5. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

    我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

  6. ruby - 继续,未定义 callcc 方法 - 2

    我想学习一些关于Continuation的知识,使用callcc方法从一些文章中键入几个示例,但我遇到了错误:NoMethodError:undefinedmethod`callcc'formain:Objectfrom(pry):2:in`'没有文章提到包含延续库。那么如何解决这个问题呢?谢谢编辑:ruby1.9.2p290(2011-07-09修订版32553)[x86_64-linux] 最佳答案 您需要要求“继续”。require'continuation' 关于ruby-继续,

  7. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  8. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

    运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

  9. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  10. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

随机推荐