gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 10:00:19 25 4
gpt4 key购买 nike

我遇到一种情况,需要在阻止操作之前启动到期计时器。如果在预定的时间间隔内控制仍未脱离阻止操作,那么我需要继续进行并继续其他 Activity 。该程序不应在计时器到期时终止。而是我必须执行阻塞操作之后的代码。在以下代码段中,计时器到期后,我需要转到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, 0, 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,0, 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, 0, 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, 0, 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, &timer, 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没有超时)可能会中断另一个/后续/不相关的系统调用(例如readwrite等)。
因此,在下面的代码中,我们需要(例如):

timer_set(250000);
semop(...);
timer_set(0);
另外,请注意,可以从信号处理程序[安全地]执行有限数量的syscall [和/或库函数调用]。值得注意的是, printf不能在信号处理程序中使用。
而且,即使执行“信号安全”系统调用,都要求信号处理程序保存[进入时]并恢复[退出时] errno的原始值。
否则,基线代码(例如在[interrupted] 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, 0, 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, 0, 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, &timer, NULL);
#else
setitimer(ITIMER_REAL, &timer, 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, 0, 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, &timer, 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;
}

这是重构程序的一些示例输出:
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]

更新:
  1. Why re-arming the timer is required inside WaitForSemaphore?

通过注释掉信号处理程序中的 printf调用(由于信号安全问题,我们必须这样做),作为副作用, count的增量无效。
首先,我只是注释掉了 printf,没有意识到副作用“破坏了”了处理者的撤防代码。一段时间后,我意识到自己做了什么并想要这种效果(即让处理程序“什么也不做”)。
因此,整个信号处理程序[有效]除了允许信号被拦截[并导致挂起的系统调用接收到 EINTR错误-这是我们真正想要的]以外,什么也不做。
我忽略了在此处的注释中明确声明这一点[并使用 #if 0注释掉所有代码]。
例如,由于最初的困惑,最好不要在调试代码( count++)中放入 Activity /必需的代码[ printf]。因此, printf(...,count++);最好是: printf(...); count++;这是一种体系结构选择,可以使用基准代码(非处理程序代码)来控制布/撤防。这是因为基线不知道处理程序是否做过任何事情,因此无论如何它都必须明确地进行撤防(以防止计时器/信号在等待循环代码之外触发)。
通常,[在内核中]有一个定期的计时器中断当然是有用的,并且以类推,它作为一个定期信号有用,这对于仅唤醒[stuck] syscall的用例没有帮助。
  1. Why usleep is required inside pgma module?

这仅是为了强制 pgmb同时查看对 semop的超时调用条件和用于测试/验证目的的正常条件。在普通/生产代码中,无需 pgma进休眠眠状态。
在一开始, pgma0值发布到信号量。然后睡眠1秒钟(1000毫秒)。 pgmb启动时,它将看到此 0值[和〜250毫秒后超时]。 pgmb将以此循环约4次,直到 pgma唤醒并发布 1值[正常完成 pgmbsemop为止]。
因此,现在,我们已经获得 pgmb来体验失败/超时情况和正常/成功情况。
这是一组[良好]的单元测试应该做的:测试目标代码[ semoppgmb的等待循环]对于所有可能的模式/状态都是正确的。
在这段代码中,我们通过查看调试输出来看到这一点。对于一组真正的单元测试,我们需要其他代码以编程方式对此进行检查。

关于c - 在C/Linux中,计时器到期后如何执行代码发布阻止操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63729514/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com