【问题标题】:Wait for events based on time, child process ending, data arriving on file descriptor基于时间等待事件,子进程结束,数据到达文件描述符
【发布时间】:2011-11-29 13:31:24
【问题描述】:

应用需要能够对各种事件设置回调。事件可以是:

  • 已达到 UTC 时间

  • 数据已到达文件描述符

  • 子进程已完成。

我需要编写一个函数来调度事件。该应用程序将在嵌入式平台上运行,并应采取合理的措施来最大限度地减少 CPU 使用率。

一种方法是繁忙的循环:

#define MAX_EVENT 10

typedef enum tagEventType {EV_DATA, EV_CHILD, EV_TIME} tEventType;
typedef void(*tEventCallback)(int);
typedef struct tagEvent
{
    int sequence;
    tEventType type;
    tEventCallback callback;
    time_t time;
    int fd;
    pid_t pid;    
} tEvent;

static tEvent eventsTable[MAX_EVENT];    

static void processEvents(void)
{
    int i;
    for ( i=0; i<MAX_EVENT; ++i )
    {
        if ( eventsTable[i].sequence > 0 )
        {
            switch(eventsTable[i].type)
            {
                case EV_DATA:
                    if ( checkForDataUsingPoll( eventsTable[i].fd) )
                    {
                        eventsTable[i].callback(eventsTable[i].sequence);
                    }                    
                    break;
                case EV_CHILD:
                    if ( kill(eventsTable[i].pid, 0) == -1 )
                    {
                        eventsTable[i].callback(eventsTable[i].sequence);
                        eventsTable[i].sequence = 0;                        
                    }
                    break;
                case EV_TIME:
                    {
                        if ( time(NULL) > eventsTable[i].time )
                        {
                            eventsTable[i].callback(eventsTable[i].sequence);
                            eventsTable[i].sequence = 0;
                        }
                    }
                    break;
            }
        }
    }
}

int main(  int argc, char* argv[] )
{
    signal(SIGCHLD, SIG_IGN);
    while(1)
    {
        processEvents();
        usleep(1000);
    }
    return 0;    
}

checkForDataUsingPoll() 使用超时为零的 poll() 来检查数据是否已到达文件描述符。为简洁起见,我省略了它。

有没有办法避免繁忙的循环?也许是民意调查、信号和警报的混合体?

【问题讨论】:

    标签: c linux


    【解决方案1】:

    对于文件描述符,有 poll(您已经使用)或 selectepoll 或 libevent。您还可以检查SIGIO 信号,或以aio_ 为前缀的函数。

    暂时可以查看alarm系统调用。

    要终止子进程,请检查SIGCHLD 信号。

    编辑 回答评论:对于信号,您实际上不必做任何事情,您设置的信号处理程序将在它们发生时由系统自动调用。如果它们发生,请设置一个标志和可能的其他变量来通知您的程序发生了什么。

    当接收到信号时,poll 将返回错误并将errno 设置为EINTR。如果是这种情况,请检查信号处理程序设置的标志以查看发生了什么,并调用您需要的任何函数。如果是警报,那么您必须为下一个事件重新启动警报。然后再次调用poll,等待下一个事件发生。

    除非您有其他事情要做,否则您甚至可以无限超时调用poll。它将在信号或文件描述符就绪时返回。

    【讨论】:

    • 我不清楚的是如何同时等待轮询、SIGALRM 和 SIGCHLD。我想 poll() 会被信号打断。然后我必须为下一个定时事件重新发出警报()......
    • @SimonElliott 更新了我的答案。
    • 谢谢。其实我不需要 alarm() 因为我可以使用 poll() 超时。
    • 为设置标志的 SIGCHLD 安装一个信号处理程序。 poll() 将返回 -1 并设置 errno == EINTR 如果在阻塞轮询时捕获到信号,检查每一轮轮询的标志(你实际上需要使用信号掩码并使用 ppoll()为了没有竞争条件)。
    • 作为替代方案,使用 linux 特定的 signalfd() 和 timerfd_create() ,它为您提供文件描述符,您可以像 poll() 中的任何其他文件描述符一样使用这些文件描述符,通过文件描述符。
    最近更新 更多