【问题标题】:getutent and Linux timer issuesgetutent 和 Linux 计时器问题
【发布时间】:2010-11-22 23:29:28
【问题描述】:

我在运行以下代码时遇到问题,该代码调用 getutent() 来计算当前登录到系统的用户总数。计时器将每 1 秒调用一次,并将名为“isSigAlrmOccured”的布尔值设置为 true 并退出。主函数通过检查此布尔值来检查是否传递了计时器信号并监视登录用户的数量。不幸的是,定时器信号仅正确传递到主程序两次,之后我没有收到任何进一步的信号。在前两个信号之后,暂停函数调用不会被中断。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <utmp.h>
#include <errno.h>

static int isSigAlrmOccured;

void alarm_handler (int signo)
{
  static int i=1;
  printf("\n Signal Occurred %d times\n",i++);
  isSigAlrmOccured = 1;

}

int main (int argc, char *argv[]) {
   struct itimerval delay;
   struct utmp *utmpstruct;
   int numuser;


   int ret;
   signal (SIGALRM, alarm_handler);
   delay.it_value.tv_sec = 1;
   delay.it_value.tv_usec = 0;
   delay.it_interval.tv_sec = 1;
   delay.it_interval.tv_usec = 0;
   ret = setitimer (ITIMER_REAL, &delay, NULL);
   if (ret) {
     perror ("setitimer");
     return 0;
   }
   for (;;) {
       pause ( );
       /* count the number of users */
       if ( (errno == EINTR) && (isSigAlrmOccured) ) {
           isSigAlrmOccured = 0;
           setutent();
           while ((utmpstruct = getutent())) {
           if ((utmpstruct->ut_type == USER_PROCESS) &&
               (utmpstruct->ut_name[0] != '\0'))
               numuser++;
           }
           endutent();
       }
   }
   return 0;
}

输出:

信号发生1次

信号发生 2 次

【问题讨论】:

  • 为什么不直接打电话给sleep(1); 而不是pause() 和所有的信号资料?

标签: c linux posix system-calls


【解决方案1】:

This page 列出了可以从信号处理程序调用的“安全”函数集。如果您调用其他函数,则行为未定义。我注意到setutent() 似乎并不是最重要的,对于初学者来说......

【讨论】:

  • 嗨放松,我将函数调用移到计时器上下文之外(参见上面的更改代码),但它也不起作用。
  • printf() 也不是异步信号安全的。同样printf() 可以更改errno,但是您的信号处理程序不会在开始时保存它并在返回之前将其恢复。
【解决方案2】:

*utent() 的实现正在使用 alarm() 并正在重置您的警报。
你将不得不做其他事情。

strace ttest(为简洁起见删除了一些行)
[...]
暂停()
--- SIGALRM(闹钟)@ 0 (0) ---
write(1, "信号发生 1 次\n", 25 信号发生 1 次) = 25
open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 3
警报(0)= 5
rt_sigaction(SIGALRM, {0x7f52580a91c0, [], SA_RESTORER, 0x7f5257fd46e0}, {0x40075c, [ALRM], SA_RESTORER|SA_RESTART, 0x7f5257fd46e0}, 8) = 0
警报(1) = 0

仅在睡眠期间设置闹钟的示例代码。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <utmp.h>
#include <errno.h>

static int isSigAlrmOccured;

void alarm_handler (int signo)
{
  static int i=1;
  printf("\n Signal Occurred %d times\n",i++);
  isSigAlrmOccured = 1;

}

int main (int argc, char *argv[]) {
   struct itimerval delay;
   struct utmp *utmpstruct;
   int numuser;
   int ret;

   for (;;) {
   signal (SIGALRM, alarm_handler);
       alarm(1);       /* wake me later */
       pause ( );
       /* count the number of users */
       if ( (errno == EINTR) && (isSigAlrmOccured) ) {
       signal (SIGALRM, SIG_DFL);
           isSigAlrmOccured = 0;
           numuser = 0;
           setutent();
           while ((utmpstruct = getutent())) {
           if ((utmpstruct->ut_type == USER_PROCESS) &&
               (utmpstruct->ut_name[0] != '\0'))
               numuser++;
           }
           endutent();
           printf("found %d users\n", numuser);
       }
   }
   return 0;
}

【讨论】:

  • 感谢 codeDr.这似乎是问题所在。为了克服这个问题,我每次都在 for 循环结束时重置信号处理程序和 setitimer。有没有更好的方法来做到这一点?我能想到的一种更好的选择是使用 timer_create 和 sigaction 调用并定义我自己的 SIGUSR 信号。
  • 您应该使用 timer_*、alarm 或 sleep 代替,因为 setitimer 已被 POSIX 标记为过时:opengroup.org/onlinepubs/9699919799/functions/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多