]> Zhao Yanbai Git Server - minix.git/commitdiff
Support for setitimer(ITIMER_VIRTUAL/ITIMER_PROF). New test (41) for setitimer.
authorDavid van Moolenbroek <david@minix3.org>
Sat, 15 Aug 2009 21:37:26 +0000 (21:37 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Sat, 15 Aug 2009 21:37:26 +0000 (21:37 +0000)
27 files changed:
commands/ash/mksignames.c
commands/ash/trap.c
commands/simple/kill.c
include/minix/com.h
include/minix/syslib.h
include/signal.h
kernel/clock.c
kernel/config.h
kernel/proc.h
kernel/proto.h
kernel/system.c
kernel/system.h
kernel/system/Makefile
kernel/system/do_fork.c
kernel/system/do_vtimer.c [new file with mode: 0644]
lib/syslib/Makefile.in
lib/syslib/sys_vtimer.c [new file with mode: 0644]
man/man2/getitimer.2
man/man2/sigaction.2
servers/pm/alarm.c
servers/pm/const.h
servers/pm/forkexit.c
servers/pm/mproc.h
servers/pm/proto.h
servers/pm/signal.c
test/Makefile
test/test41.c [new file with mode: 0644]

index bbead25e189da81260df39e811dd0dc49813af0a..b7878613328c5c4bf9c286f637b8c2e56d7fed7a 100755 (executable)
@@ -131,8 +131,8 @@ struct sig sigtab[] = {
 #ifdef SIGXFSZ
        SIGXFSZ, "XFSZ", NULL,
 #endif
-#ifdef SIGVTALARM
-       SIGVTALARM, "VTALARM", "Virtual alarm",
+#ifdef SIGVTALRM
+       SIGVTALRM, "VTALARM", "Virtual alarm",
 #endif
 #ifdef SIGPROF
        SIGPROF, "PROF", "Profiling alarm",
index 83ce40991ad1b9524e07cd2e506dd13b5871f367..9cddb6f37c3eb89feacabb9298f4d3b1907b5d7c 100755 (executable)
@@ -548,6 +548,8 @@ int sig;
        case SIGTTIN:   return "ttin";          /* 21 */
        case SIGTTOU:   return "ttou";          /* 22 */
        case SIGWINCH:  return "winch";         /* 23 */
+       case SIGVTALRM: return "vtalrm";        /* 24 */
+       case SIGPROF:   return "prof";          /* 25 */
 #ifdef __minix_vmd
        case SIGFPEMU:  return "fpemu";         /* 30 */
 #endif
index ae6ab1e366e543545edef62ac40e13bf307c082f..7f25e4eef018992a9b5fbfd477bdd535e3441d19 100755 (executable)
@@ -41,6 +41,8 @@ struct signames {
 #ifdef SIGWINCH
        { "WINCH",      SIGWINCH        },
 #endif
+       { "VTALRM",     SIGVTALRM       },
+       { "PROF",       SIGPROF         },
        { NULL,         0               }
 };
 
index b640c046d6cde2499acaa885f350b82875d544a9..4402fa7607a0b9385a0f9a33c575b63876cc824d 100755 (executable)
 #  define SYS_VMCTL    (KERNEL_CALL + 43)      /* sys_vmctl() */
 #  define SYS_SYSCTL   (KERNEL_CALL + 44)      /* sys_sysctl() */
 
-#define NR_SYS_CALLS   45      /* number of system calls */ 
+#  define SYS_VTIMER     (KERNEL_CALL + 45)    /* sys_vtimer() */
+
+#define NR_SYS_CALLS   46      /* number of system calls */ 
 
 /* Pseudo call for use in kernel/table.c. */
 #define SYS_ALL_CALLS (NR_SYS_CALLS)
 #define VMCTL_INCSP            16
 #define VMCTL_NOPAGEZERO       18
 
+/* Field names for SYS_VTIMER. */
+#define VT_WHICH       m2_i1   /* which timer to set/retrieve */
+#  define VT_VIRTUAL        1  /* the ITIMER_VIRTUAL timer */
+#  define VT_PROF           2  /* the ITIMER_PROF timer */
+#define VT_SET         m2_i2   /* 1 for setting a timer, 0 retrieval only */
+#define VT_VALUE       m2_l1   /* new/previous value of the timer */
+#define VT_ENDPT       m2_l2   /* process to set/retrieve the timer for */
+
 /*===========================================================================*
  *                Messages for the Reincarnation Server                     *
  *===========================================================================*/
index d4facdc2c5f82805f78f95bf2afa80d540bc7680..9b51b76ac6cf1704b430b6c3c40b01dafc337c8e 100755 (executable)
@@ -86,10 +86,14 @@ _PROTOTYPE(void *alloc_contig, (size_t len, int flags, phys_bytes *phys));
 #define AC_LOWER16M    0x02
 #define AC_ALIGN64K    0x04
 
-/* Clock functionality: get system times or (un)schedule an alarm call. */
+/* Clock functionality: get system times, (un)schedule an alarm call, or
+ * retrieve/set a process-virtual timer.
+ */
 _PROTOTYPE( int sys_times, (endpoint_t proc_nr, clock_t *user_time,
        clock_t *sys_time, clock_t *uptime));
 _PROTOTYPE(int sys_setalarm, (clock_t exp_time, int abs_time));
+_PROTOTYPE( int sys_vtimer, (endpoint_t proc_nr, int which, clock_t *newval,
+       clock_t *oldval));
 
 /* Shorthands for sys_irqctl() system call. */
 #define sys_irqdisable(hook_id) \
index 9b538190d412c7466dcb005e42f685f5993b2e60..a52c7742043b4e5df0a9986b265678d277d321b6 100755 (executable)
@@ -44,6 +44,8 @@ typedef unsigned long sigset_t;
 #define SIGEMT           16    /* EMT instruction */
 #define SIGCHLD           17   /* child process terminated or stopped */
 #define SIGWINCH         21    /* window size has changed */
+#define SIGVTALRM         24   /* virtual alarm */
+#define SIGPROF           25   /* profiler alarm */
 
 /* POSIX requires the following signals to be defined, even if they are
  * not supported.  Here are the definitions, but they are not supported.
@@ -54,7 +56,7 @@ typedef unsigned long sigset_t;
 #define SIGTTIN           22   /* background process wants to read */
 #define SIGTTOU           23   /* background process wants to write */
 
-#define _NSIG             24   /* highest signal number plus one */
+#define _NSIG             26   /* highest signal number plus one */
 
 #ifdef _MINIX
 #define SIGIOT             SIGABRT /* for people who speak PDP-11 */
index a1a65cd9824d16909d0c5059b828858124488cc0..d5eb1ddfc381deffd716faad6566f3292ef37cc4 100755 (executable)
@@ -96,6 +96,8 @@ PUBLIC void clock_task()
 PRIVATE void do_clocktick(m_ptr)
 message *m_ptr;                                /* pointer to request message */
 {
+  register struct proc *bill_copy = bill_ptr;
+
 /* Despite its name, this routine is not called on every clock tick. It
  * is called on those clock ticks when a lot of work needs to be done.
  */
@@ -120,6 +122,18 @@ message *m_ptr;                            /* pointer to request message */
       }
   }
 
+  /* Check if a process-virtual timer expired. Check prev_ptr, but also
+   * bill_ptr - one process's user time is another's system time, and the
+   * profile timer decreases for both! Do this before the queue operations
+   * below, which may alter bill_ptr. Note the use a copy of bill_ptr, because
+   * bill_ptr may have been changed above, and this code can't be put higher
+   * up because otherwise cause_sig() may dequeue prev_ptr before we do.
+   */
+  vtimer_check(prev_ptr);
+
+  if (prev_ptr != bill_copy)
+       vtimer_check(bill_copy);
+
   /* Check if a clock timer expired and run its watchdog function. */
   if (next_timeout <= realtime) {
        tmrs_exptimers(&clock_timers, realtime, NULL);          
@@ -180,11 +194,13 @@ irq_hook_t *hook;
  *     realtime:
  *             The current uptime is incremented with all outstanding ticks.
  *     proc_ptr, bill_ptr:
- *             These are used for accounting.  It does not matter if proc.c
- *             is changing them, provided they are always valid pointers,
- *             since at worst the previous process would be billed.
+ *             These are used for accounting and virtual timers. It does not
+ *             matter if proc.c is changing them, provided they are always
+ *             valid pointers, since at worst the previous process would be
+ *             billed.
  */
   register unsigned ticks;
+  register int expired;
 
   if(minix_panicing) return;
 
@@ -208,6 +224,20 @@ irq_hook_t *hook;
       bill_ptr->p_ticks_left -= ticks;
   }
 
+  /* Decrement virtual timers, if applicable. We decrement both the virtual
+   * and the profile timer of the current process, and if the current process
+   * is not billable, the timer of the billed process as well.
+   * If any of the timers expire, do_clocktick() will send out signals.
+   */
+  expired = 0;
+  if ((proc_ptr->p_misc_flags & VIRT_TIMER) &&
+       (proc_ptr->p_virt_left -= ticks) <= 0) expired = 1;
+  if ((proc_ptr->p_misc_flags & PROF_TIMER) &&
+       (proc_ptr->p_prof_left -= ticks) <= 0) expired = 1;
+  if (! (priv(proc_ptr)->s_flags & BILLABLE) &&
+       (bill_ptr->p_misc_flags & PROF_TIMER) &&
+       (bill_ptr->p_prof_left -= ticks) <= 0) expired = 1;
+
 #if 0
   /* Update load average. */
   load_update();
@@ -216,7 +246,7 @@ irq_hook_t *hook;
   /* Check if do_clocktick() must be called. Done for alarms and scheduling.
    * Some processes, such as the kernel tasks, cannot be preempted. 
    */ 
-  if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0)) {
+  if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0) || expired) {
       prev_ptr = proc_ptr;                     /* store running process */
       lock_notify(HARDWARE, CLOCK);            /* send notification */
   } 
index 2c02039301a839b4c1e4781ed0147ae5cef255e2..07b9c8770047e030e0c64b4d6092502c550d0129 100644 (file)
@@ -29,6 +29,7 @@
 #define USE_GETINFO               1    /* retrieve a copy of kernel data */
 #define USE_TIMES         1    /* get process and system time info */
 #define USE_SETALARM      1    /* schedule a synchronous alarm */
+#define USE_VTIMER         1   /* set or retrieve a process-virtual timer */
 #define USE_DEVIO                 1    /* read or write a single I/O port */
 #define USE_VDEVIO                1    /* process vector with I/O requests */
 #define USE_SDEVIO                1    /* perform I/O request on a buffer */
index df90bfed4cc851e7bf0650c8ada8b49a7c9f449e..f08f434a5edd4f39c701e64998132a92307995e6 100755 (executable)
@@ -33,6 +33,9 @@ struct proc {
   clock_t p_user_time;         /* user time in ticks */
   clock_t p_sys_time;          /* sys time in ticks */
 
+  clock_t p_virt_left;         /* number of ticks left on virtual timer */
+  clock_t p_prof_left;         /* number of ticks left on profile timer */
+
   struct proc *p_nextready;    /* pointer to next ready process */
   struct proc *p_caller_q;     /* head of list of procs wishing to send */
   struct proc *p_q_link;       /* link to next proc wishing to send */
@@ -169,6 +172,8 @@ struct proc {
 
 /* Misc flags */
 #define REPLY_PENDING  0x01    /* reply to IPC_REQUEST is pending */
+#define VIRT_TIMER     0x02    /* process-virtual timer is running */
+#define PROF_TIMER     0x04    /* process-virtual profile timer is running */
 #define MF_VM          0x08    /* process uses VM */
 #define MF_ASYNMSG     0x10    /* Asynchrous message pending */
 #define MF_FULLVM      0x20
index c229309916eb556ffed91071de8b4cd7b05e47a6..88f1bb8f2ffb7cbb7cd1419a8df1bd85da249527 100755 (executable)
@@ -70,10 +70,12 @@ _PROTOTYPE( void clear_endpoint, (struct proc *rc)                  );
 _PROTOTYPE( phys_bytes umap_bios, (vir_bytes vir_addr, vir_bytes bytes));
 _PROTOTYPE( phys_bytes umap_verify_grant, (struct proc *rp, endpoint_t grantee,  cp_grant_id_t grant, vir_bytes offset, vir_bytes bytes, int access));
 
-
 /* system/do_newmap.c */
 _PROTOTYPE( int newmap, (struct proc *rp, struct mem_map *map_ptr)     );
 
+/* system/do_vtimer.c */
+_PROTOTYPE( void vtimer_check, (struct proc *rp)                       );
+
 /* interrupt.c */
 _PROTOTYPE( void intr_handle,     (irq_hook_t *hook)                     );
 _PROTOTYPE( void put_irq_handler, (irq_hook_t *hook, int irq,
index 1af2b4920c29cadfea458bc24c1ecb95c9c2a6dd..992770ee7cf0895eb9699eaa661dfa2310b5f132 100755 (executable)
@@ -239,6 +239,7 @@ PRIVATE void initialize(void)
   map(SYS_TIMES, do_times);            /* get uptime and process times */
   map(SYS_SETALARM, do_setalarm);      /* schedule a synchronous alarm */
   map(SYS_STIME, do_stime);            /* set the boottime */
+  map(SYS_VTIMER, do_vtimer);          /* set or retrieve a virtual timer */
 
   /* System control. */
   map(SYS_ABORT, do_abort);            /* abort MINIX */
index 10b4f4003fe0b1fea33cb3ea4902af71d49a1fad..14f55df3c0ac3715be9a7357e9023dc5a0561be5 100644 (file)
@@ -173,6 +173,11 @@ _PROTOTYPE( int do_setalarm, (message *m_ptr) );
 
 _PROTOTYPE( int do_stime, (message *m_ptr) );  
 
+_PROTOTYPE( int do_vtimer, (message *m_ptr) );
+#if ! USE_VTIMER
+#define do_vtimer do_unused
+#endif
+
 _PROTOTYPE( int do_safecopy, (message *m_ptr) );       
 _PROTOTYPE( int do_vsafecopy, (message *m_ptr) );      
 _PROTOTYPE( int do_iopenable, (message *m_ptr) );      
index 02a7e0fe8cd2104d9679347251b46c661f9dc0a4..496663d1d57e0433de610be5d6333e30bfd0ff69 100644 (file)
@@ -32,6 +32,7 @@ OBJECTS       = \
        $(SYSTEM)(do_times.o) \
        $(SYSTEM)(do_setalarm.o) \
        $(SYSTEM)(do_stime.o) \
+       $(SYSTEM)(do_vtimer.o) \
        $(SYSTEM)(do_irqctl.o) \
        $(SYSTEM)(do_devio.o) \
        $(SYSTEM)(do_vdevio.o) \
@@ -101,6 +102,9 @@ $(SYSTEM)(do_setalarm.o):   do_setalarm.c
 $(SYSTEM)(do_stime.o): do_stime.c
        $(CC) do_stime.c
 
+$(SYSTEM)(do_vtimer.o):        do_vtimer.c
+       $(CC) do_vtimer.c
+
 $(SYSTEM)(do_irqctl.o):        do_irqctl.c
        $(CC) do_irqctl.c
 
index a365ab77a313238e24bcc7b860d883c6e353ef80..29b49c03ed962678bad2aeff886e610472a5b7d8 100644 (file)
@@ -57,6 +57,10 @@ register message *m_ptr;     /* pointer to request message */
 
   rpc->p_reg.psw &= ~TRACEBIT;         /* clear trace bit */
 
+  rpc->p_misc_flags &= ~(VIRT_TIMER | PROF_TIMER);
+  rpc->p_virt_left = 0;                /* disable, clear the process-virtual timers */
+  rpc->p_prof_left = 0;
+
   /* Parent and child have to share the quantum that the forked process had,
    * so that queued processes do not have to wait longer because of the fork.
    * If the time left is odd, the child gets an extra tick.
diff --git a/kernel/system/do_vtimer.c b/kernel/system/do_vtimer.c
new file mode 100644 (file)
index 0000000..8904ab6
--- /dev/null
@@ -0,0 +1,116 @@
+/* The kernel call implemented in this file:
+ *   m_type:   SYS_VTIMER
+ *
+ * The parameters for this kernel call are:
+ *    m2_i1:   VT_WHICH                (the timer: VT_VIRTUAL or VT_PROF)
+ *    m2_i2:   VT_SET                  (whether to set, or just retrieve)
+ *    m2_l1:   VT_VALUE                (new/old expiration time, in ticks)
+ *    m2_l2:   VT_ENDPT                (process to which the timer belongs)
+ */
+
+#include "../system.h"
+
+#include <signal.h>
+#include <minix/endpoint.h>
+
+#if USE_VTIMER
+
+/*===========================================================================*
+ *                             do_vtimer                                    *
+ *===========================================================================*/
+PUBLIC int do_vtimer(m_ptr)
+message *m_ptr;                        /* pointer to request message */
+{
+/* Set and/or retrieve the value of one of a process' virtual timers. */
+  struct proc *rrp;            /* pointer to requesting process */
+  struct proc *rp;             /* pointer to process the timer belongs to */
+  register int pt_flag;                /* the misc on/off flag for the req.d timer */
+  register clock_t *pt_left;   /* pointer to the process' ticks-left field */ 
+  clock_t old_value;           /* the previous number of ticks left */
+  int proc_nr, proc_nr_e;
+
+  /* The requesting process must be privileged. */
+  rrp = proc_addr(who_p);
+  if (! (priv(rrp)->s_flags & SYS_PROC)) return(EPERM);
+
+  if (m_ptr->VT_WHICH != VT_VIRTUAL && m_ptr->VT_WHICH != VT_PROF)
+      return(EINVAL);
+
+  /* The target process must be valid. */
+  proc_nr_e = (m_ptr->VT_ENDPT == SELF) ? m_ptr->m_source : m_ptr->VT_ENDPT;
+  if (!isokendpt(proc_nr_e, &proc_nr)) return(EINVAL);
+  rp = proc_addr(proc_nr);
+
+  /* Determine which flag and which field in the proc structure we want to
+   * retrieve and/or modify. This saves us having to differentiate between
+   * VT_VIRTUAL and VT_PROF multiple times below.
+   */
+  if (m_ptr->VT_WHICH == VT_VIRTUAL) {
+      pt_flag = VIRT_TIMER;
+      pt_left = &rp->p_virt_left;
+  } else { /* VT_PROF */
+      pt_flag = PROF_TIMER;
+      pt_left = &rp->p_prof_left;
+  }
+
+  /* Retrieve the old value. */
+  if (rp->p_misc_flags & pt_flag) {
+      old_value = *pt_left;
+
+      if (old_value < 0) old_value = 0;
+  } else {
+      old_value = 0;
+  }
+
+  /* Set the new value, if requested. This is called from the system task, so
+   * we can be interrupted by the clock interrupt, but not by the clock task.
+   * Therefore we only have to protect against interference from clock.c's
+   * clock_handler(). We can do this without disabling interrupts, by removing
+   * the timer's flag before changing the ticks-left field; in that case the
+   * clock interrupt will not touch the latter anymore.
+   */
+  if (m_ptr->VT_SET) {
+      rp->p_misc_flags &= ~pt_flag;    /* disable virtual timer */
+
+      if (m_ptr->VT_VALUE > 0) {
+          *pt_left = m_ptr->VT_VALUE;  /* set new timer value */
+          rp->p_misc_flags |= pt_flag; /* (re)enable virtual timer */
+      } else {
+          *pt_left = 0;                        /* clear timer value */
+      }
+  }
+
+  m_ptr->VT_VALUE = old_value;
+
+  return(OK);
+}
+
+#endif /* USE_VTIMER */
+
+/*===========================================================================*
+ *                             vtimer_check                                 *
+ *===========================================================================*/
+PUBLIC void vtimer_check(rp)
+struct proc *rp;                       /* pointer to the process */
+{
+  /* This is called from the clock task, so we can be interrupted by the clock
+   * interrupt, but not by the system task. Therefore we only have to protect
+   * against interference from the clock handler. We can safely perform the
+   * following actions without locking as well though, as the clock handler
+   * never alters p_misc_flags, and only decreases p_virt_left/p_prof_left.
+   */
+
+  /* Check if the virtual timer expired. If so, send a SIGVTALRM signal. */
+  if ((rp->p_misc_flags & VIRT_TIMER) && rp->p_virt_left <= 0) {
+      rp->p_misc_flags &= ~VIRT_TIMER;
+      rp->p_virt_left = 0;
+      cause_sig(rp->p_nr, SIGVTALRM);
+  }
+
+  /* Check if the profile timer expired. If so, send a SIGPROF signal. */
+  if ((rp->p_misc_flags & PROF_TIMER) && rp->p_prof_left <= 0) {
+      rp->p_misc_flags &= ~PROF_TIMER;
+      rp->p_prof_left = 0;
+      cause_sig(rp->p_nr, SIGPROF);
+  }
+}
index 8f4f339e6e4c7089c1e249c0e85f8e1b26c52ea7..c8882352f3d4ae2dd504288f47830bdca57ecdda 100644 (file)
@@ -71,6 +71,7 @@ libsys_FILES=" \
        sys_voutb.c \
        sys_voutl.c \
        sys_voutw.c \
+       sys_vtimer.c \
        taskcall.c \
        ds.c    \
        vm_allocmem.c \
diff --git a/lib/syslib/sys_vtimer.c b/lib/syslib/sys_vtimer.c
new file mode 100644 (file)
index 0000000..9ec2efc
--- /dev/null
@@ -0,0 +1,28 @@
+#include "syslib.h"
+
+PUBLIC int sys_vtimer(proc, which, newval, oldval)
+endpoint_t proc;               /* proc to retrieve/set the timer for */
+int which;                     /* timer to retrieve/set */
+clock_t *newval;               /* if non-NULL, set to this new value */
+clock_t *oldval;               /* if non-NULL, old value is stored here */
+{
+  message m;
+  int r;
+
+  m.VT_ENDPT = proc;
+  m.VT_WHICH = which;
+  if (newval != NULL) {
+      m.VT_SET = 1;
+      m.VT_VALUE = *newval;
+  } else {
+      m.VT_SET = 0;
+  }
+
+  r = _taskcall(SYSTASK, SYS_VTIMER, &m);
+
+  if (oldval != NULL) {
+      *oldval = m.VT_VALUE;
+  }
+
+  return(r);
+}
index c30d2cd7b90297cd507b440ebd183a61125e9881..76c783445e31f63818ad963e3bcbd49c90039c9b 100644 (file)
@@ -32,10 +32,14 @@ A timer that is decremented in realtime. When it expires, a
 signal is delivered to the process.
 .TP
 .B ITIMER_VIRTUAL
-Not supported on Minix.
+A timer that is decremented in process user time. When it expires, a
+.BR SIGVTALRM
+signal is delivered to the process.
 .TP
 .B ITIMER_PROF
-Not supported on Minix.
+A timer that is decremented in process user+system time. When it expires, a
+.BR SIGPROF
+signal is delivered to the process.
 .PP
 The specified timer will first expire after the time specified in the 'it_value' field of the itimerval structure. Similarly, upon retrieval the 'it_value' field will contain the time after which the timer will expire.
 .PP
@@ -58,9 +62,6 @@ Either \fIwhich\fP is not one of the ITIMER_* constants above, or one of the tim
 .TP
 .B EFAULT
 Bad \fIvalue\fP or \fIovalue\fP address. 
-.TP
-.B ENOSYS
-The value of \fIwhich\fP is not ITIMER_REAL.
 .SH SEE ALSO
 .BR alarm (2)
 .SH AUTHOR
index fdb6ad088705074eef0985ef253903a979f9a742..67dd272fbf23fad4b71d679a488455ae5d2b3de7 100644 (file)
@@ -148,6 +148,8 @@ SIGTSTP     20      ps      Interactive stop signal
 SIGTTIN        21      ps      Background read
 SIGTTOU        22      ps      Background write
 SIGWINCH       23      xvi     Window size change
+SIGVTALRM      24      xk      Virtual alarm clock
+SIGPROF        25      xk      Profiler alarm clock
 .ft R
 .fi
 .PP
index 0fcc1d15147f15b85d6a9ae21da146a100f27279..33a44176f43e9c5095d421a5000999fc83670697 100644 (file)
@@ -6,6 +6,7 @@
  *   do_itimer: perform the ITIMER system call
  *   do_alarm: perform the ALARM system call
  *   set_alarm: tell the timer interface to start or stop a process timer
+ *   check_vtimer: check if one of the virtual timers needs to be restarted
  */
 
 #include "pm.h"
@@ -22,6 +23,8 @@ FORWARD _PROTOTYPE( clock_t ticks_from_timeval, (struct timeval *tv)  );
 FORWARD _PROTOTYPE( void timeval_from_ticks, (struct timeval *tv,
                        clock_t ticks)                                  );
 FORWARD _PROTOTYPE( int is_sane_timeval, (struct timeval *tv)          );
+FORWARD _PROTOTYPE( void getset_vtimer, (struct mproc *mp, int nwhich,
+               struct itimerval *value, struct itimerval *ovalue)      );
 FORWARD _PROTOTYPE( void get_realtimer, (struct mproc *mp,
                                         struct itimerval *value)       );
 FORWARD _PROTOTYPE( void set_realtimer, (struct mproc *mp,
@@ -98,7 +101,7 @@ PUBLIC int do_itimer()
   int r;
 
   /* Make sure 'which' is one of the defined timers. */
-  if (m_in.which_timer < ITIMER_REAL || m_in.which_timer > ITIMER_PROF)
+  if (m_in.which_timer < 0 || m_in.which_timer >= NR_ITIMERS)
        return(EINVAL);
 
   /* Determine whether to set and/or return the given timer value, based on
@@ -134,8 +137,11 @@ PUBLIC int do_itimer()
 
        case ITIMER_VIRTUAL :
        case ITIMER_PROF :
-               /* Not implemented. */
-               r = ENOSYS;
+               getset_vtimer(mp, m_in.which_timer,
+                               (setval) ? &value : NULL,
+                               (getval) ? &ovalue : NULL);
+
+               r = OK;
                break;
   }
 
@@ -172,6 +178,97 @@ PUBLIC int do_alarm()
   return(remaining);
 }
 
+/*===========================================================================*
+ *                             getset_vtimer                                * 
+ *===========================================================================*/
+PRIVATE void getset_vtimer(rmp, which, value, ovalue)
+struct mproc *rmp;
+int which;
+struct itimerval *value;
+struct itimerval *ovalue;
+{
+  clock_t newticks, *nptr;             /* the new timer value, in ticks */
+  clock_t oldticks, *optr;             /* the old ticks value, in ticks */
+  int r, num;
+
+  /* The default is to provide sys_vtimer with two null pointers, i.e. to do
+   * nothing at all.
+   */
+  optr = nptr = NULL;
+
+  /* If the old timer value is to be retrieved, have 'optr' point to the
+   * location where the old value is to be stored, and copy the interval.
+   */
+  if (ovalue != NULL) {
+       optr = &oldticks;
+
+       timeval_from_ticks(&ovalue->it_interval, rmp->mp_interval[which]);
+  }
+
+  /* If a new timer value is to be set, store the new timer value and have
+   * 'nptr' point to it. Also, store the new interval.
+   */
+  if (value != NULL) {
+       newticks = ticks_from_timeval(&value->it_value);
+       nptr = &newticks;
+
+       /* If no timer is set, the interval must be zero. */
+       if (newticks <= 0)
+               rmp->mp_interval[which] = 0;
+       else 
+               rmp->mp_interval[which] =
+                       ticks_from_timeval(&value->it_interval);
+  }
+
+  /* Find out which kernel timer number to use. */
+  switch (which) {
+  case ITIMER_VIRTUAL: num = VT_VIRTUAL; break;
+  case ITIMER_PROF:    num = VT_PROF;    break;
+  default:             panic(__FILE__, "invalid vtimer type", which);
+  }
+
+  /* Make the kernel call. If requested, also retrieve and store
+   * the old timer value.
+   */
+  if ((r = sys_vtimer(rmp->mp_endpoint, num, nptr, optr)) != OK)
+       panic(__FILE__, "sys_vtimer failed", r);
+
+  if (ovalue != NULL) {
+       /* If the alarm expired already, we should take into account the
+        * interval. Return zero only if the interval is zero as well.
+        */
+       if (oldticks <= 0) oldticks = rmp->mp_interval[which];
+
+       timeval_from_ticks(&ovalue->it_value, oldticks);
+  }
+}
+
+/*===========================================================================*
+ *                             check_vtimer                                 * 
+ *===========================================================================*/
+PUBLIC void check_vtimer(proc_nr, sig)
+int proc_nr;
+int sig;
+{
+  register struct mproc *rmp;
+  int which, num;
+
+  rmp = &mproc[proc_nr];
+
+  /* Translate back the given signal to a timer type and kernel number. */
+  switch (sig) {
+  case SIGVTALRM: which = ITIMER_VIRTUAL; num = VT_VIRTUAL; break;
+  case SIGPROF:   which = ITIMER_PROF;    num = VT_PROF;    break;
+  default: panic(__FILE__, "invalid vtimer signal", sig);
+  }
+
+  /* If a repetition interval was set for this virtual timer, tell the
+   * kernel to set a new timeout for the virtual timer.
+   */
+  if (rmp->mp_interval[which] > 0)
+       sys_vtimer(rmp->mp_endpoint, num, &rmp->mp_interval[which], NULL);
+}
+
 /*===========================================================================*
  *                             get_realtimer                                * 
  *===========================================================================*/
@@ -195,7 +292,7 @@ struct itimerval *value;
        /* If the alarm expired already, we should take into account the
         * interval. Return zero only if the interval is zero as well.
         */
-       if (remaining <= 0) remaining = rmp->mp_interval;
+       if (remaining <= 0) remaining = rmp->mp_interval[ITIMER_REAL];
   } else {
        remaining = 0;
   }
@@ -204,7 +301,7 @@ struct itimerval *value;
   timeval_from_ticks(&value->it_value, remaining);
 
   /* Similarly convert and store the interval of the timer. */
-  timeval_from_ticks(&value->it_interval, rmp->mp_interval);
+  timeval_from_ticks(&value->it_interval, rmp->mp_interval[ITIMER_REAL]);
 }
 
 /*===========================================================================*
@@ -226,7 +323,7 @@ struct itimerval *value;
 
   /* Apply these values. */
   set_alarm(rmp, ticks);
-  rmp->mp_interval = interval;
+  rmp->mp_interval[ITIMER_REAL] = interval;
 }
 
 /*===========================================================================*
@@ -270,8 +367,8 @@ struct timer *tp;
    * The set_alarm call will be calling pm_set_timer from within this callback
    * from the pm_expire_timers function. This is safe, but we must not use the
    * "tp" structure below this point anymore. */
-  if (rmp->mp_interval > 0)
-       set_alarm(rmp, rmp->mp_interval);
+  if (rmp->mp_interval[ITIMER_REAL] > 0)
+       set_alarm(rmp, rmp->mp_interval[ITIMER_REAL]);
   else rmp->mp_flags &= ~ALARM_ON;
 
   check_sig(rmp->mp_pid, SIGALRM);
index 64558475e0491b1820f0e9b906b9e1816ea37305..cf75e72c5cac853c308692f966b774db5506684a 100644 (file)
@@ -12,3 +12,4 @@
 
 #define MAX_SECS (((1<<(sizeof(clock_t)*8-1))-1)/system_hz)
                                /* max.secs for setitimer() ((2^31-1)/HZ) */
+#define NR_ITIMERS        3    /* number of supported interval timers */
index da8d8a9f8295499f26e1ccf776073ea6aa98efad..5af2ac794605c36275ff8b709fcc3778b0787124 100644 (file)
@@ -41,7 +41,7 @@ PUBLIC int do_fork()
   register struct mproc *rmc;  /* pointer to child */
   pid_t new_pid;
   static int next_child;
-  int n = 0, r, s;
+  int i, n = 0, r, s;
   endpoint_t child_ep;
 
  /* If tables might fill up during FORK, don't even start since recovery half
@@ -86,7 +86,8 @@ PUBLIC int do_fork()
   rmc->mp_exitstatus = 0;
   rmc->mp_sigstatus = 0;
   rmc->mp_endpoint = child_ep;         /* passed back by VM */
-  rmc->mp_interval = 0;                        /* reset interval timer */
+  for (i = 0; i < NR_ITIMERS; i++)
+       rmc->mp_interval[i] = 0;        /* reset timer intervals */
 
   /* Find a free pid for the child and put it in the table. */
   new_pid = get_free_pid();
@@ -115,7 +116,7 @@ PUBLIC int do_fork_nb()
   int s;
   pid_t new_pid;
   static int next_child;
-  int n = 0, r;
+  int i, n = 0, r;
   endpoint_t child_ep;
 
   /* Only system processes are allowed to use fork_nb */
@@ -159,6 +160,8 @@ PUBLIC int do_fork_nb()
   rmc->mp_child_utime = 0;             /* reset administration */
   rmc->mp_child_stime = 0;             /* reset administration */
   rmc->mp_endpoint = child_ep;         /* passed back by VM */
+  for (i = 0; i < NR_ITIMERS; i++)
+       rmc->mp_interval[i] = 0;        /* reset timer intervals */
 
   /* Find a free pid for the child and put it in the table. */
   new_pid = get_free_pid();
index 7e113680ce16307cefe7d760700a76d96c37fcc4..c50b3764dbc99a9afaa56e63eb2f79dd3b40179d 100644 (file)
@@ -7,6 +7,8 @@
 #include <timers.h>
 #include <signal.h>
 
+#include "const.h"
+
 EXTERN struct mproc {
   char mp_exitstatus;          /* storage for status when process exits */
   char mp_sigstatus;           /* storage for signal # for killed procs */
@@ -39,7 +41,7 @@ EXTERN struct mproc {
                                 * PM_UNPAUSE request is delivered.
                                 */
   struct timer mp_timer;       /* watchdog timer for alarm(2), setitimer(2) */
-  clock_t mp_interval;         /* repetition interval for setitimer(2) */
+  clock_t mp_interval[NR_ITIMERS];     /* setitimer(2) repetition intervals */
 
   unsigned mp_flags;           /* flag bits */
   vir_bytes mp_procargs;        /* ptr to proc's initial stack arguments */
index 5bfee1eeb5ce7fbe101ffa128d9dd3ac56a2c4ab..3c091ea88c3966dc803e65be95935e46ea854909 100644 (file)
@@ -11,6 +11,7 @@ struct memory;
 _PROTOTYPE( int do_alarm, (void)                                       );
 _PROTOTYPE( int do_itimer, (void)                                      );
 _PROTOTYPE( void set_alarm, (struct mproc *rmp, clock_t ticks)         );
+_PROTOTYPE( void check_vtimer, (int proc_nr, int sig)                  );
 
 /* break.c */
 _PROTOTYPE( int do_brk, (void)                                         );
index 2a05b37233e5dd4756c5e0896e153579cba66fae..aec9a670c041a0b4db77362297e6d6ec61952ef5 100644 (file)
@@ -264,9 +264,10 @@ sigset_t sig_map;
   /* Check each bit in turn to see if a signal is to be sent.  Unlike
    * kill(), the kernel may collect several unrelated signals for a
    * process and pass them to PM in one blow.  Thus loop on the bit
-   * map. For SIGINT, SIGWINCH and SIGQUIT, use proc_id 0 to indicate
-   * a broadcast to the recipient's process group.  For SIGKILL, use
-   * proc_id -1 to indicate a systemwide broadcast.
+   * map. For SIGVTALRM and SIGPROF, see if we need to restart a
+   * virtual timer. For SIGINT, SIGWINCH and SIGQUIT, use proc_id 0
+   * to indicate a broadcast to the recipient's process group.  For
+   * SIGKILL, use proc_id -1 to indicate a systemwide broadcast.
    */
   for (i = 1; i <= _NSIG; i++) {
        if (!sigismember(&sig_map, i)) continue;
@@ -279,6 +280,10 @@ sigset_t sig_map;
            case SIGQUIT:
            case SIGWINCH:
                id = 0; break;  /* broadcast to process group */
+           case SIGVTALRM:
+           case SIGPROF:
+               check_vtimer(proc_nr, i);
+               /* fall-through */
            default:
                id = proc_id;
                break;
index 5f788ced7c815cf6339d39a225da4534bbb09540..dde86756a75b2be3eb84eae7b6cf55c753da05d7 100644 (file)
@@ -7,7 +7,7 @@ OBJ=    test1  test2  test3  test4  test5  test6  test7  test8  test9  \
        test10        test12 test13 test14 test15 test16 test17 test18 test19 \
               test21 test22 test23        test25 test26 test27 test28 test29 \
        test30 test31 test32        test34 test35 test36 test37 test38 \
-       test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f
+       test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41
 
 BIGOBJ=  test20 test24
 ROOTOBJ= test11 test33
@@ -28,7 +28,8 @@ $(ROOTOBJ):
 
 clean: 
        cd select && make clean
-       -rm -rf *.o *.s *.bak test? test?? t10a t11a t11b t40a t40b t40c t40d t40e t40f DIR*
+       -rm -rf *.o *.s *.bak test? test?? t10a t11a t11b \
+               t40a t40b t40c t40d t40e t40f DIR*
 
 test1: test1.c
 test2: test2.c
@@ -79,3 +80,4 @@ t40c: t40c.c
 t40d: t40d.c
 t40e: t40e.c
 t40f: t40f.c
+test41: test41.c
diff --git a/test/test41.c b/test/test41.c
new file mode 100644 (file)
index 0000000..2a0bc10
--- /dev/null
@@ -0,0 +1,409 @@
+/* Tests for getitimer(2)/setitimer(2) - by D.C. van Moolenbroek */
+/* Warning: this test deals with (real and virtual) time, and, lacking a proper
+ * point of reference, its correctness depends on circumstances like CPU speed
+ * and system load. A succeeding test run says a lot - failure not so much. */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <minix/config.h>
+#include <minix/sysinfo.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define ITERATIONS 3
+#define MAX_ERROR 4
+
+/* we have to keep in mind the millisecond values are rounded up */
+#define UPPERUSEC(us) ((us)+(1000000/system_hz))
+#define EQUSEC(l,r) \
+  ((l) <= ((r) + (1000000/system_hz)) && (l) >= ((r) - (1000000/system_hz)))
+
+#define FILLITIMER(it, vs, vu, is, iu) \
+  (it).it_value.tv_sec = (vs); \
+  (it).it_value.tv_usec = (vu); \
+  (it).it_interval.tv_sec = (is); \
+  (it).it_interval.tv_usec = (iu);
+
+/* these two macros are not fully working for all possible values;
+ * the tests only use values that the macros can deal with, though. */
+#define EQITIMER(it, vs, vu, is, iu) \
+  ((it).it_value.tv_sec == (vs) && EQUSEC((it).it_value.tv_usec,vu) && \
+  (it).it_interval.tv_sec == (is) && (it).it_interval.tv_usec == (iu))
+
+#define LEITIMER(it, vs, vu, is, iu) \
+  ((it).it_value.tv_sec > 0 && ((it).it_value.tv_sec < (vs) || \
+  ((it).it_value.tv_sec == (vs) && (it).it_value.tv_usec <= \
+   UPPERUSEC(vu))) && \
+  (it).it_interval.tv_sec == (is) && EQUSEC((it).it_interval.tv_usec,iu))
+
+_PROTOTYPE(int main, (int argc, char **argv));
+_PROTOTYPE(void test, (int m, int t));
+_PROTOTYPE(void test_which, (void));
+_PROTOTYPE(void test_getset, (void));
+_PROTOTYPE(void test_neglarge, (void));
+_PROTOTYPE(void test_zero, (void));
+_PROTOTYPE(void test_timer, (void));
+_PROTOTYPE(void test_alarm, (void));
+_PROTOTYPE(void test_fork, (void));
+_PROTOTYPE(void test_exec, (void));
+_PROTOTYPE(int do_check, (void));
+_PROTOTYPE(void got_alarm, (int sig));
+_PROTOTYPE(void busy_wait, (int secs));
+_PROTOTYPE(void e, (int n));
+_PROTOTYPE(void quit, (void));
+
+static char *executable;
+static int signals;
+static int timer;
+static int errct = 0, subtest;
+static long system_hz;
+
+static int sigs[] = { SIGALRM, SIGVTALRM, SIGPROF };
+static const char *names[] = { "REAL", "VIRTUAL", "PROF" };
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+  int i, m = 0xFFFF, n = 0xF;
+
+  getsysinfo_up(PM_PROC_NR, SIU_SYSTEMHZ, sizeof(system_hz), &system_hz);
+
+  if (strcmp(argv[0], "DO CHECK") == 0) {
+       timer = atoi(argv[1]);
+
+       exit(do_check());
+  }
+
+  printf("Test 41 ");
+  fflush(stdout);
+
+  executable = argv[0];
+
+  if (argc >= 2) m = atoi(argv[1]);
+  if (argc >= 3) n = atoi(argv[2]);
+
+  for (i = 0; i < ITERATIONS; i++) {
+       if (n & 1) test(m, ITIMER_REAL);
+       if (n & 2) test(m, ITIMER_VIRTUAL);
+       if (n & 4) test(m, ITIMER_PROF);
+  }
+
+  quit();
+  return(-1);                  /* impossible */
+}
+
+void test(m, t)
+int m;
+int t;
+{
+  timer = t;
+
+  if (m & 0001) test_which();
+  if (m & 0002) test_getset();
+  if (m & 0004) test_neglarge();
+  if (m & 0010) test_zero();
+  if (m & 0020) test_timer();
+  if (m & 0040) test_alarm();
+  if (m & 0100) test_fork();
+  if (m & 0200) test_exec();
+}
+
+/* test invalid and unsupported 'which' values */
+void test_which()
+{
+  struct itimerval it;
+
+  subtest = 0;
+
+  errno = 0; if (!getitimer(-1, &it) || errno != EINVAL) e(1);
+  errno = 0; if ( getitimer(timer, &it)                ) e(2);
+  errno = 0; if (!getitimer( 3, &it) || errno != EINVAL) e(3);
+}
+
+/* test if we get back what we set */
+void test_getset()
+{
+  struct itimerval it, oit;
+
+  subtest = 1;
+
+  /* no alarm should be set initially */
+  if (getitimer(timer, &it)) e(1);
+  if (!EQITIMER(it, 0, 0, 0, 0)) e(2);
+
+  if (setitimer(timer, &it, &oit)) e(3);
+  if (setitimer(timer, &oit, NULL)) e(4);
+  if (!EQITIMER(oit, 0, 0, 0, 0)) e(5);
+
+  FILLITIMER(it, 123, 0, 456, 0);
+  if (setitimer(timer, &it, NULL)) e(6);
+
+  FILLITIMER(it, 987, 0, 654, 0);
+  if (setitimer(timer, &it, &oit)) e(7);
+  if (!LEITIMER(oit, 123, 0, 456, 0)) e(8);
+
+  if (getitimer(timer, &oit)) e(9);
+  if (!LEITIMER(oit, 987, 0, 654, 0)) e(10);
+
+  FILLITIMER(it, 0, 0, 0, 0);
+  if (setitimer(timer, &it, &oit)) e(11);
+  if (!LEITIMER(oit, 987, 0, 654, 0)) e(12);
+
+  if (getitimer(timer, &oit)) e(13);
+  if (!EQITIMER(oit, 0, 0, 0, 0)) e(14);
+}
+
+/* test negative/large values */
+void test_neglarge()
+{
+  struct itimerval it;
+
+  subtest = 2;
+
+  FILLITIMER(it, 4, 0, 5, 0);
+  if (setitimer(timer, &it, NULL)) e(1);
+
+  FILLITIMER(it, 1000000000, 0, 0, 0);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(2);
+
+  FILLITIMER(it, 0, 1000000, 0, 0);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(3);
+
+  FILLITIMER(it, 0, 0, 0, 1000000);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(4);
+
+  FILLITIMER(it, -1, 0, 0, 0);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(5);
+
+  FILLITIMER(it, 0, -1, 0, 0);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(6);
+
+  FILLITIMER(it, 0, 0, -1, 0);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(7);
+
+  FILLITIMER(it, 0, 0, 0, -1);
+  if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(8);
+
+  if (getitimer(timer, &it)) e(9);
+  if (!LEITIMER(it, 4, 0, 5, 0)) e(10);
+}
+
+/* setitimer with a zero timer has to set the interval to zero as well */
+void test_zero()
+{
+  struct itimerval it;
+
+  subtest = 3;
+
+  it.it_value.tv_sec = 0;
+  it.it_value.tv_usec = 0;
+  it.it_interval.tv_sec = 1;
+  it.it_interval.tv_usec = 1;
+
+  if (setitimer(timer, &it, NULL)) e(1);
+  if (getitimer(timer, &it)) e(2);
+  if (!EQITIMER(it, 0, 0, 0, 0)) e(3);
+}
+
+/* test actual timer functioning */
+void test_timer()
+{
+  struct itimerval it;
+
+  subtest = 4;
+
+  if (signal(sigs[timer], got_alarm) == SIG_ERR) e(1);
+
+  FILLITIMER(it, 0, 1, 0, 1);
+
+  if (setitimer(timer, &it, NULL)) e(2);
+
+  signals = 0;
+  busy_wait(1);
+
+  FILLITIMER(it, 0, 0, 0, 0);
+  if (setitimer(timer, &it, NULL)) e(3);
+
+  /* we don't know how many signals we'll actually get in practice,
+   * so these checks more or less cover the extremes of the acceptable */
+  if (signals < 2) e(4);
+  if (signals > system_hz * 2) e(5);
+
+  /* only for REAL timer can we check against the clock */
+  if (timer == ITIMER_REAL) {
+       FILLITIMER(it, 1, 0, 0, 0);
+       if (setitimer(timer, &it, NULL)) e(6);
+
+       signals = 0;
+       busy_wait(1);
+
+       FILLITIMER(it, 0, 0, 0, 0);
+       if (setitimer(timer, &it, NULL)) e(7);
+
+       if (signals != 1) e(8);
+  }
+
+  signals = 0;
+  busy_wait(1);
+
+  if (signals != 0) e(9);
+}
+
+/* test itimer/alarm interaction */
+void test_alarm(void) {
+  struct itimerval it;
+
+  /* only applicable for ITIMER_REAL */
+  if (timer != ITIMER_REAL) return;
+
+  subtest = 5;
+
+  if (signal(SIGALRM, got_alarm) == SIG_ERR) e(1);
+
+  FILLITIMER(it, 3, 0, 1, 0);
+  if (setitimer(timer, &it, NULL)) e(2);
+
+  if (alarm(2) != 3) e(3);
+
+  if (getitimer(timer, &it)) e(4);
+  if (!LEITIMER(it, 2, 0, 0, 0)) e(5);
+
+  signals = 0;
+  busy_wait(5);
+
+  if (signals != 1) e(6);
+
+  if (getitimer(timer, &it)) e(7);
+  if (!EQITIMER(it, 0, 0, 0, 0)) e(8);
+}
+
+/* test that the timer is reset on forking */
+void test_fork(void) {
+  struct itimerval it, oit;
+  pid_t pid;
+  int status;
+
+  subtest = 6;
+
+  FILLITIMER(it, 12, 34, 56, 78);
+
+  if (setitimer(timer, &it, NULL)) e(1);
+
+  pid = fork();
+  if (pid < 0) e(2);
+
+  if (pid == 0) {
+    if (getitimer(timer, &it)) exit(5);
+    if (!EQITIMER(it, 0, 0, 0, 0)) exit(6);
+
+    exit(0);
+  }
+
+  if (wait(&status) != pid) e(3);
+  if (!WIFEXITED(status)) e(4);
+  if (WEXITSTATUS(status) != 0) e(WEXITSTATUS(status));
+
+  FILLITIMER(it, 0, 0, 0, 0);
+  if (setitimer(timer, &it, &oit)) e(7);
+  if (!LEITIMER(oit, 12, 34, 56, 78)) e(8);
+}
+
+/* test if timer is carried over to exec()'ed process */
+void test_exec(void) {
+  struct itimerval it;
+  pid_t pid;
+  int status;
+  char buf[2];
+
+  subtest = 7;
+
+  pid = fork();
+  if (pid < 0) e(1);
+
+  if (pid == 0) {
+    FILLITIMER(it, 3, 0, 1, 0);
+    if (setitimer(timer, &it, NULL)) exit(2);
+
+    sprintf(buf, "%d", timer);
+    execl(executable, "DO CHECK", buf, NULL);
+
+    exit(3);
+  }
+
+  if (wait(&status) != pid) e(4);
+  if (WIFSIGNALED(status)) {
+    /* process should have died from corresponding signal */
+    if (WTERMSIG(status) != sigs[timer]) e(5);
+  }
+  else {
+    if (WIFEXITED(status)) e(WEXITSTATUS(status));
+    else e(6);
+  }
+}
+
+/* procedure of the exec()'ed process */
+int do_check()
+{
+  struct itimerval it;
+
+  if (getitimer(timer, &it)) return(81);
+  if (!LEITIMER(it, 3, 0, 1, 0)) return(82);
+
+  busy_wait(60);
+
+  return(83);
+}
+
+void busy_wait(secs)
+int secs;
+{
+  time_t now, exp;
+  int i;
+
+  exp = time(&now) + secs + 1;
+
+  while (now < exp) {
+       for (i = 0; i < 100000; i++);
+
+       time(&now);
+  }
+}
+
+void got_alarm(sig)
+int sig;
+{
+  if (sig != sigs[timer]) e(1001);
+
+  signals++;
+}
+
+void e(n)
+int n;
+{
+
+  printf("Timer %s, subtest %d, error %d, errno %d: %s\n",
+       names[timer], subtest, n, errno, strerror(errno));
+
+  if (errct++ > MAX_ERROR) {
+       printf("Too many errors; test aborted\n");
+       exit(1);
+  }
+}
+
+void quit()
+{
+
+  if (errct == 0) {
+       printf("ok\n");
+       exit(0);
+  } else {
+       printf("%d errors\n", errct);
+       exit(1);
+  }
+}