在C / Linux中,计时器到期后如何执行代码发布阻止操作?

如何解决在C / Linux中,计时器到期后如何执行代码发布阻止操作?

在这种情况下,我需要在进行阻止操作之前启动到期计时器。如果在预定的时间间隔内控制仍未脱离阻止操作,那么我需要继续进行并继续其他活动。该程序不应在计时器到期时终止。而是我必须执行阻塞操作之后的代码。在以下代码段中,计时器到期后,我需要转到B点而不退出。工作环境是ubuntu 18.04,代码在c中。我已经探讨了setitimer函数调用以及与信号处理程序相关的函数。但是控制并没有跳过阻塞操作,而是继续进行后续代码。

  Program to create a semaphore and setting the semaphore for use by another program.

  int main(int argc,char *argv[])
  {
    key_t SemKey = 0x12345 ;

    int SemId = -1;
    int NumOfSems = 2 ;
    int Flag ;
    int RetStat ;

    char choice = 'n' ;

    struct
    {
            int val;
            struct semid_ds *buf;
            ushort array[2] ;
    } Arg ;


    Flag = IPC_CREAT | IPC_EXCL | 0666 ;

    SemId = semget(SemKey,NumOfSems,Flag ) ;
    if ( SemId == -1 )
    {
            perror("semget");
            exit(EXIT_FAILURE);
    }

    Arg.array[0] = 0 ;
    Arg.array[1] = 0 ;

    RetStat = semctl(SemId,SETALL,Arg.array);

    if (RetStat == -1)
    {
            perror("semctl");
            exit(EXIT_FAILURE);
    }

    printf("\nPress y or Y to continue ahead.\n");
    scanf("%c",&choice);

    if ( ( choice != 'y') && ( choice != 'Y'))
            exit(EXIT_FAILURE);

    if ( ( choice != 'y') && ( choice != 'Y'))
            exit(EXIT_FAILURE);


    RetStat = semctl( SemId,SETVAL,1 );
    if( RetStat < 0 )
    {
            perror( "SET SEMOP Failure: " );
            exit(EXIT_FAILURE);
    }

    return EXIT_SUCCESS ;
  }

  Program that waits for the semaphore set by the above program.

  int DriverModule(int );

  int main(int argc,char *argv[])
  {
    key_t SemKey = 0x12345 ;
    int SemId ;
    u_int Flag;

    Flag = 0666 ;

    SemId = semget( SemKey,Flag );
    if ( SemId == -1 )
    {
            perror("semget");
            exit(EXIT_FAILURE);
    }

    DriverModule(SemId) ;

    return EXIT_SUCCESS ;
  }

  #define MAX_ITERATIONS 100

  struct itimerval timer;

  int WaitForSemaphore(int,unsigned short ) ;

  void timer_handler (int signum)
  {
    static int count = 0;
    printf ("timer expired %d times\n",++count);

    if ( count >= MAX_ITERATIONS ) // Stop the timer
    {
            printf("\n\nForcing Time Out Termination.....\n\n");

            timer.it_value.tv_sec = 0;
            timer.it_value.tv_usec = 0;
            timer.it_interval.tv_sec = 0;
            timer.it_interval.tv_usec = 0;

            setitimer (ITIMER_VIRTUAL,&timer,NULL);

            return ;
    }

   }

  int DriverModule (int SemId)
  {
    struct sigaction sa;

    /* Install timer_handler as the signal handler for SIGVTALRM. */
    memset (&sa,sizeof (sa));

    sa.sa_flags = SA_SIGINFO;
    sa.sa_handler = &timer_handler;
    sigaction (SIGVTALRM,&sa,NULL);

    /* Configure the timer to expire after 250 msec... */
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 250000;
    /* ... and every 500 msec after that. */
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 500000;

    /* Start a virtual timer. It counts down whenever this process is  
     executing. */
    setitimer (ITIMER_VIRTUAL,NULL);

    printf("\nBefore calling wait for semaphore.......\n");

    // Waiting for sempahore
    if( !WaitForSemaphore( SemId,0) )
    {
            printf("\nUnable to get sempahore.\n");
            return 0 ;
    }

    printf("\nAfter calling after calling wait for semaphoe module.........\n");

    return 1 ;
  }

  int WaitForSemaphore(int SemId,unsigned short SemNum )
  {
    struct sembuf SemBuf;

    int  RetStat;
    unsigned int NoOfSemOps;

    SemBuf.sem_num = SemNum;
    SemBuf.sem_op = -1;
    SemBuf.sem_flg = 0;

    NoOfSemOps = 1;

    RetStat = semop( SemId,&SemBuf,NoOfSemOps );
    if( RetStat < 0 )
    {
            if( errno == EINTR )
            {
                    WaitForSemaphore( SemId,SemNum );
            }
            else
            {
                    perror( "Wait SEMOP Failure: " );
                    return 0 ;
            }
    }

    return 1 ;

   }

解决方法

好的,主要问题是使用ITIMER_VIRTUAL。当进程正在运行时,此[类型]计数器仅 递减。如果进程正在执行系统调用,则该进程未运行

因此,我们必须改用ITIMER_REAL。而且,如果执行此操作,它将生成一个SIGALRM信号[而不是一个SIGVTALRM一个]。

进行了这些更改之后,还有另一个问题。

必须在保护的系统调用后 禁用计时器 (例如semop)。否则,未过期计时器(即semop不会 超时)可能会中断另一个/后续/不相关的系统调用(例如read,{{1 }}等。

因此,在下面的代码中,我们需要(例如):

write

此外,请注意,可以从信号处理程序[安全地]执行个有限的个系统调用[和/或库函数调用]。值得注意的是,timer_set(250000); semop(...); timer_set(0); 不能在信号处理程序中使用。

而且,即使执行“信号安全”系统调用,也都要求信号处理程序保存[进入时],并恢复[退出时] printf的原始值。

否则,基线代码(例如在[中断] errno之后)将看不到正确的semop值(例如errno),但是看到的EINTR值信号处理程序选择执行的系统调用。


我必须重构代码以制定合理的测试程序。

我将这两个程序合并为一个测试程序,以便针对正常情况和超时情况生成适当的单元测试,由于计时问题和比赛条件,将它们作为单独的程序很难做到。

我还增强了调试打印输出。

无论如何,这是代码:

errno

以下是重构程序的一些示例输出:

// create a semaphore and setting the semaphore for use by another program.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/time.h>
#include <sys/wait.h>

int opt_n;
int opt_y;

#define MAX_ITERATIONS 100

int itype;
struct itimerval timer;
const char *pgmtail;

int WaitForSemaphore(int,unsigned short);
int DriverModule(int);

typedef long long tsc_t;

tsc_t tvzero;

tsc_t
tscget(void)
{
    struct timespec ts;
    tsc_t tsc;

    clock_gettime(CLOCK_MONOTONIC,&ts);

    tsc = ts.tv_sec;
    tsc *= 1000000000;
    tsc += ts.tv_nsec;

    tsc -= tvzero;

    return tsc;
}

double
tscsec(tsc_t tsc)
{
    double sec;

    sec = tsc;
    sec /= 1e9;

    return sec;
}

void
dbgprt(const char *fmt,...)
{
    va_list ap;
    char buf[1000];
    char *bp = buf;

    bp += sprintf(bp,"%.9f %s ",tscsec(tscget()),pgmtail);

    va_start(ap,fmt);
    bp += vsprintf(bp,fmt,ap);
    va_end(ap);

    fputs(buf,stdout);
    fflush(stdout);
}

int
pgma(void)
{
    key_t SemKey = 0x12345;

    pgmtail = "pgma";

    int SemId = -1;
    int NumOfSems = 2;
    int Flag = 0;
    int RetStat;

#if 0
    char choice = 'n';
#endif

#if 0
    struct {
        int val;
        struct semid_ds *buf;
        ushort array[2];
    } Arg;
#endif

    Flag |= IPC_CREAT;
    //Flag |= IPC_EXCL;
    Flag |= 0666;

    SemId = semget(SemKey,NumOfSems,Flag);
    if (SemId == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

#if 0
    Arg.array[0] = 0;
    Arg.array[1] = 0;

    RetStat = semctl(SemId,SETALL,Arg.array);

    if (RetStat == -1) {
        perror("semctl");
        exit(EXIT_FAILURE);
    }
#endif

    for (int phase = 0;  phase <= opt_y;  ++phase) {
        int setval = phase;
        dbgprt("pgma: SET phase=%d\n",phase);

        RetStat = semctl(SemId,SETVAL,setval);
        if (RetStat < 0) {
            perror("SET SEMOP Failure: ");
            exit(EXIT_FAILURE);
        }

        dbgprt("USLEEP/BEF\n");
        usleep(1000 * 1000);
        dbgprt("USLEEP/AFT\n");
    }

    return EXIT_SUCCESS;
}

// Program that waits for the semaphore set by the above program.

int
pgmb(void)
{
    key_t SemKey = 0x12345;
    int SemId;

    pgmtail = "pgmb";

// NOTE/BUG: we must add IPC_CREAT if we start pgmb first
    u_int Flag = 0;
    Flag |= 0666;
#if 1
    Flag |= IPC_CREAT;
#endif
    SemId = semget(SemKey,2,Flag);

    if (SemId == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    DriverModule(SemId);

    dbgprt("pgmb: complete\n");

    return EXIT_SUCCESS;
}

char *
exmsg(int status)
{
    static char buf[1000];
    char *bp = buf;

    bp += sprintf(bp,"status=[%8.8X ",status);

    do {
        if (WIFEXITED(status)) {
            bp += sprintf(bp,"exited status=%d",WEXITSTATUS(status));
            break;
        }

        if (WIFSIGNALED(status)) {
            bp += sprintf(bp,"killed by signal %d",WTERMSIG(status));
            break;
        }

        if (WIFSTOPPED(status)) {
            bp += sprintf(bp,"stopped by signal %d",WSTOPSIG(status));
            break;
        }

        bp += sprintf(bp,"continued");
    } while (0);

    bp += sprintf(bp,"]");

    return buf;
}

void
testit(int newval)
{
    int code;
    pid_t pid;

    opt_y = newval;

    for (int phase = 1;  phase <= 2;  ++phase) {
        // start the receiver
        pid = fork();
        if (pid != 0) {
            dbgprt("testit: pid=%d\n",pid);
            continue;
        }

        switch (phase) {
        case 1:
            code = pgma();
            break;
        case 2:
            code = pgmb();
            break;
        default:
            code = 99;
            break;
        }

        exit(code);
    }

    while (1) {
        int status;

        pid = wait(&status);
        if (pid <= 0)
            break;

        dbgprt("main: pid %d reaped -- %s\n",pid,exmsg(status));
    }
}

int
main(int argc,char **argv)
{
    //pid_t pid;

    --argc;
    ++argv;

    pgmtail = "main";

    tvzero = tscget();

    for (;  argc > 0;  --argc,++argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'n':
            opt_n = ! opt_n;
            break;
        }
    }

    opt_y = ! opt_n;
    dbgprt("pgma: opt_y=%d\n",opt_y);

    //testit(0);
    testit(1);

    return 0;
}

void
timer_set(int usec)
{

    dbgprt("timer_set: SET usec=%d\n",usec);

    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = usec;

    /* ... and every 500 msec after that. */
// NOTE/BUG: timer must _not_ be rearmed -- can abort unrelated syscalls
    timer.it_interval.tv_sec = 0;
#if 0
    timer.it_interval.tv_usec = 500000;
#else
    timer.it_interval.tv_usec = 0;
#endif

    setitimer(itype,&timer,NULL);
}

void
timer_handler(int signum)
{
    static int count = 0;

// NOTE/BUG: printf may _not_ be called from a signal handler
    //dbgprt("timer expired %d times\n",++count);

    // Stop the timer
// NOTE/BUG: disabling now done elsewhere
    if (count >= MAX_ITERATIONS) {
        //dbgprt("Forcing Time Out Termination.....\n");

        timer.it_value.tv_sec = 0;
        timer.it_value.tv_usec = 0;
        timer.it_interval.tv_sec = 0;
        timer.it_interval.tv_usec = 0;

#if 0
        setitimer(ITIMER_VIRTUAL,NULL);
#else
        setitimer(ITIMER_REAL,NULL);
#endif

        return;
    }
}

int
DriverModule(int SemId)
{
    struct sigaction sa;
    sigset_t set;
    int signo;

#if 0
    itype = ITIMER_VIRTUAL;
    signo = SIGVTALRM;
#else
    itype = ITIMER_REAL;
    signo = SIGALRM;
#endif

    /* Install timer_handler as the signal handler for SIGVTALRM. */
    memset(&sa,sizeof(sa));

    sa.sa_flags = SA_SIGINFO;
    sa.sa_handler = &timer_handler;
    sigaction(signo,&sa,NULL);

    sigemptyset(&set);
    sigaddset(&set,signo);
    sigprocmask(SIG_UNBLOCK,&set,NULL);

    /* Configure the timer to expire after 250 msec... */

    // Start virtual timer. It counts down whenever this process is executing.
#if 0
    setitimer(itype,NULL);
#else
    timer_set(250000);
#endif

    dbgprt("Before calling wait for semaphore.......\n");

    // Waiting for sempahore
    if (! WaitForSemaphore(SemId,0)) {
        dbgprt("Unable to get sempahore.\n");
        return 0;
    }

    dbgprt("After calling after calling wait for semaphoe module.........\n");

    return 1;
}

int
WaitForSemaphore(int SemId,unsigned short SemNum)
{
    struct sembuf SemBuf;

    int RetStat;
    unsigned int NoOfSemOps;

    while (1) {
        SemBuf.sem_num = SemNum;
        SemBuf.sem_op = -1;
        SemBuf.sem_flg = 0;

        NoOfSemOps = 1;

        dbgprt("WaitFor: SEMOP\n");

        RetStat = semop(SemId,&SemBuf,NoOfSemOps);
        if (RetStat >= 0) {
            dbgprt("WaitFor: OKAY\n");
            break;
        }

        if (errno == EINTR) {
            dbgprt("WaitFor: EINTR\n");

            // do stuff here ...

            // rearm timer
            timer_set(250000);
            continue;
        }

        perror("Wait SEMOP Failure: ");
        exit(1);
    }

// NOTE/BUG: _must_ always disable timer to prevent other syscalls from being
// interrupted
#if 1
    timer_set(0);
#endif

    return 1;
}

更新:

  1. 为什么0.000000332 main pgma: opt_y=1 0.000182324 main testit: pid=849558 0.000267203 main testit: pid=849559 0.000830005 pgma pgma: SET phase=0 0.000847541 pgmb timer_set: SET usec=250000 0.000882037 pgma USLEEP/BEF 0.000891077 pgmb Before calling wait for semaphore....... 0.000895977 pgmb WaitFor: SEMOP 0.250932859 pgmb WaitFor: EINTR 0.250950128 pgmb timer_set: SET usec=250000 0.250956676 pgmb WaitFor: SEMOP 0.500996272 pgmb WaitFor: EINTR 0.501014687 pgmb timer_set: SET usec=250000 0.501021903 pgmb WaitFor: SEMOP 0.751066428 pgmb WaitFor: EINTR 0.751089504 pgmb timer_set: SET usec=250000 0.751097693 pgmb WaitFor: SEMOP 1.000970921 pgma USLEEP/AFT 1.000987303 pgma pgma: SET phase=1 1.001001916 pgma USLEEP/BEF 1.001046071 pgmb WaitFor: OKAY 1.001055982 pgmb timer_set: SET usec=0 1.001062505 pgmb After calling after calling wait for semaphoe module......... 1.001066632 pgmb pgmb: complete 1.001210687 main main: pid 849559 reaped -- status=[00000000 exited status=0] 2.001078396 pgma USLEEP/AFT 2.001269995 main main: pid 849558 reaped -- status=[00000000 exited status=0] 内需要重新布防计时器?

通过注释掉信号处理程序中的WaitForSemaphore调用(由于信号安全问题,我们必须这样做),作为副作用,printf的增量无效。

首先,我只是注释掉count,但没有意识到副作用“破坏了”了处理程序的撤防代码。一段时间后,我意识到自己做了什么,并且想要 效果(即让处理程序“什么也不做”)。

因此,整个信号处理程序[有效]除了允许信号被拦截[并导致挂起的系统调用接收到printf错误-这是我们真正想要的]以外,什么也不做。

我忽略了在注释中明确声明这一点[并用EINTR注释掉所有代码]。

作为示例,由于我最初的困惑,最好不要在调试代码(#if 0)中放入活动/必要的代码[count++]。因此,printf最好是:printf(...,count++);

这是一种体系结构选择,需要使用基线代码(非处理程序代码)来控制布/撤防。这是因为基线不知道处理程序是否做过任何事情,因此它无论如何都必须明确地撤防[以防止计时器/信号在等待循环代码之外触发(错误)]。

通常,[在内核中]有一个定期的计时器中断当然是有用的,并且以类推,它作为一个周期性的信号是有用的,对于这里仅唤醒[stuck]的用例来说,这没有帮助系统调用。

  1. 为什么printf(...); count++;模块中需要usleep

这只是要强制pgma同时看到对pgmb的超时调用的条件,以及用于测试/验证目的的正常调用。在普通/生产代码中,semop无需睡觉。

在开始时,pgma将一个pgma值发布到该信号量。然后休眠1秒钟(1000毫秒)。

0开始时,它将看到此pgmb值[和〜250毫秒后超时]。 0将以此循环约4次,直到pgmb唤醒并发布一个pgma值[通常会完成1的{​​{1}}为止。 / p>

因此,现在,我们已经pgmb经历了 失败/超时情况正常/成功情况。

这是一组[良好]的单元测试应该执行的操作:测试目标代码[semoppgmb的等待循环对于所有可能的模式/状态都是正确的。

在这段代码中,我们通过查看调试输出来看到这一点。对于一组真正的单元测试,我们需要其他代码以编程方式对此进行检查。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-