如何解决在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;
}
更新:
- 为什么
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]的用例来说,这没有帮助系统调用。
- 为什么
printf(...); count++;
模块中需要usleep
?
这只是要强制pgma
同时看到对pgmb
的超时调用的和条件,以及用于测试/验证目的的正常调用。在普通/生产代码中,semop
无需睡觉。
在开始时,pgma
将一个pgma
值发布到该信号量。然后休眠1秒钟(1000毫秒)。
0
开始时,它将看到此pgmb
值[和〜250毫秒后超时]。 0
将以此循环约4次,直到pgmb
唤醒并发布一个pgma
值[通常会完成1
的{{1}}为止。 / p>
因此,现在,我们已经pgmb
经历了 失败/超时情况和正常/成功情况。
这是一组[良好]的单元测试应该执行的操作:测试目标代码[semop
中pgmb
的等待循环对于所有可能的模式/状态都是正确的。
在这段代码中,我们通过查看调试输出来看到这一点。对于一组真正的单元测试,我们需要其他代码以编程方式对此进行检查。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。