2011. 9. 3. 22:35

재시작 라이브러리(restart library)는 순간적인 사건으로 요청한 작업을 완료하지 못했을 경우

스스로 재시작할 수 있는 함수들의 집합이다.

 

재시작 라이브러리는 시그널에 의한 중단과 불완전한 I/O 두 부분에 중점을 두고 있다.

예를 들어 read, write와 같은 라이브러리 함수들은 I/O 명령을 완전히 실행하기 전에

시그널에 의해 중단될 때 -1을 리턴하고, errno를 EINTR로 설정한다.

 

이러한 중단은 실제로 에러 상황이 아닌 프로그램이 블로킹 I/O를 사용할 때 시그널의 처리를 위한 자연스러운

사건이다. 함수가 -1을 리턴하고, errno가 EINTR로 설정된 경우 재시작 함수는 재시작 과정을 수행한다.

 

[ restart.c ]

/data/workspace/b-example/usp/appendixB/restart.c.html
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "restart.h"
#define BLKSIZE PIPE_BUF
#define MILLION 1000000L
#define D_MILLION 1000000.0

/* Private functions */

static int gettimeout(struct timeval end,
                               struct timeval *timeoutp) {
   gettimeofday(timeoutp, NULL);
   timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec;
   timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec;
   if (timeoutp->tv_usec >= MILLION) {
      timeoutp->tv_sec++;
      timeoutp->tv_usec -= MILLION;
   }
   if (timeoutp->tv_usec < 0) {
      timeoutp->tv_sec--;
      timeoutp->tv_usec += MILLION;
   }
   if ((timeoutp->tv_sec < 0) ||
       ((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) {
      errno = ETIME;
      return -1;
   }
   return 0;
}

/* Restart versions of traditional functions */

int r_close(int fildes) {
   int retval;
   while (retval = close(fildes), retval == -1 && errno == EINTR) ;
   return retval;
}

int r_dup2(int fildes, int fildes2) {
   int retval;
   while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR) ;
   return retval;
}


int r_open2(const char *path, int oflag) {
   int retval;
   while (retval = open(path, oflag), retval == -1 && errno == EINTR) ;
   return retval;
}

int r_open3(const char *path, int oflag, mode_t mode) {
   int retval;
   while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR) ;
   return retval;
}

ssize_t r_read(int fd, void *buf, size_t size) {
   ssize_t retval;
   while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ;
   return retval;
}

pid_t r_wait(int *stat_loc) {
   pid_t retval;
   while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ;
   return retval;
}

pid_t r_waitpid(pid_t pid, int *stat_loc, int options) {
   pid_t retval;
   while (((retval = waitpid(pid, stat_loc, options)) == -1) &&
           (errno == EINTR)) ;
   return retval;
}

ssize_t r_write(int fd, void *buf, size_t size) {
   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;

   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) {
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten == -1) && (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   }
   return totalbytes;
}

/* Utility functions */

struct timeval add2currenttime(double seconds) {
   struct timeval newtime;

   gettimeofday(&newtime, NULL);
   newtime.tv_sec += (int)seconds;
   newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5);
   if (newtime.tv_usec >= MILLION) {
      newtime.tv_sec++;
      newtime.tv_usec -= MILLION;
   }
   return newtime;
}

int copyfile(int fromfd, int tofd) {
   int bytesread;
   int totalbytes = 0;

   while ((bytesread = readwrite(fromfd, tofd)) > 0)
      totalbytes += bytesread;
   return totalbytes;
}

ssize_t readblock(int fd, void *buf, size_t size) {
   char *bufp;
   ssize_t bytesread;
   size_t bytestoread;
   size_t totalbytes;

   for (bufp = buf, bytestoread = size, totalbytes = 0;
        bytestoread > 0;
        bufp += bytesread, bytestoread -= bytesread) {
      bytesread = read(fd, bufp, bytestoread);
      if ((bytesread == 0) && (totalbytes == 0))
         return 0;
      if (bytesread == 0) {
         errno = EINVAL;
         return -1;
      }
      if ((bytesread) == -1 && (errno != EINTR))
         return -1;
      if (bytesread == -1)
         bytesread = 0;
      totalbytes += bytesread;
   }
   return totalbytes;
}

int readline(int fd, char *buf, int nbytes) {
   int numread = 0;
   int returnval;

   while (numread < nbytes - 1) {
      returnval = read(fd, buf + numread, 1);
      if ((returnval == -1) && (errno == EINTR))
         continue;
      if ((returnval == 0) && (numread == 0))
         return 0;
      if (returnval == 0)
         break;
      if (returnval == -1)
         return -1;
      numread++;
      if (buf[numread-1] == '\n') {
         buf[numread] = '\0';
         return numread;
      }
   }
   errno = EINVAL;
   return -1;
}

ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) {
   struct timeval timedone;

   timedone = add2currenttime(seconds);
   if (waitfdtimed(fd, timedone) == -1)
      return (ssize_t)(-1);
   return r_read(fd, buf, nbyte);
}

int readwrite(int fromfd, int tofd) {
   char buf[BLKSIZE];
   int bytesread;

   if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0)
      return -1;
   if (bytesread == 0)
      return 0;
   if (r_write(tofd, buf, bytesread) < 0)
      return -1;
   return bytesread;
}

int readwriteblock(int fromfd, int tofd, char *buf, int size) {
   int bytesread;

   bytesread = readblock(fromfd, buf, size);
   if (bytesread != size)         /* can only be 0 or -1 */
      return bytesread;
   return r_write(tofd, buf, size);
}

int waitfdtimed(int fd, struct timeval end) {
   fd_set readset;
   int retval;
   struct timeval timeout;

   if ((fd < 0) || (fd >= FD_SETSIZE)) {
      errno = EINVAL;
      return -1;
   }
   FD_ZERO(&readset);
   FD_SET(fd, &readset);
   if (gettimeout(end, &timeout) == -1)
      return -1;
   while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1)
           && (errno == EINTR)) {
      if (gettimeout(end, &timeout) == -1)
         return -1;
      FD_ZERO(&readset);
      FD_SET(fd, &readset);
   }
   if (retval == 0) {
      errno = ETIME;
      return -1;
   }
   if (retval == -1)
      return -1;
   return 0;
}

 

[ restart.h ]

/data/workspace/b-example/usp/appendixB/restart.h.html
#ifndef __RESTART_H_
#define __RESTART_H_

#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#ifndef ETIME
#define ETIME ETIMEDOUT
#endif

struct timeval add2currenttime(double seconds);
int copyfile(int fromfd, int tofd);
int r_close(int fildes);
int r_dup2(int fildes, int fildes2);
int r_open2(const char *path, int oflag);
int r_open3(const char *path, int oflag, mode_t mode);
ssize_t r_read(int fd, void *buf, size_t size);
pid_t r_wait(int *stat_loc);
pid_t r_waitpid(pid_t pid, int *stat_loc, int options);
ssize_t r_write(int fd, void *buf, size_t size);
ssize_t readblock(int fd, void *buf, size_t size);
int readline(int fd, char *buf, int nbytes);
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds);
int readwrite(int fromfd, int tofd);
int readwriteblock(int fromfd, int tofd, char *buf, int size);
int waitfdtimed(int fd, struct timeval end);

#endif /* __RESTART_H_ */

 

Posted by devanix
2011. 9. 3. 02:32

Catexit 함수는 사용자가 정의한 종료 핸들러를 등록한다.

종료 핸들러는 프로그램이 main 함수로부터 리턴 하거나 exit 함수를 호출하였을 때

마지막에 등록된 핸들러가 먼저 실행되는 순서로 실행. (여러 번 호출하여 핸들러 등록 가능)

 

 

showtimes 예제는 showtimes 함수를 exit 핸들러로 등록하여

프로그램과 그 자식들이 사용한 시간에 대한 통계를 표준 에러로 출력한다.

 

[ 예제 코드 : showtimes.c ]

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/times.h>
#include <sys/wait.h>

int wastetime(int maxus);

static void showtimes(void) {
   double ticks;
   struct tms tinfo;

   if ((ticks = (double) sysconf(_SC_CLK_TCK)) == -1)
      perror("Failed to determine clock ticks per second");
   else if (times(&tinfo) == (clock_t)-1)
      perror("Failed to get times information");
   else {
      fprintf(stderr, "User time:              %8.3f seconds\n",
         tinfo.tms_utime/ticks);
      fprintf(stderr, "System time:            %8.3f seconds\n",
         tinfo.tms_stime/ticks);
      fprintf(stderr, "Children's user time:   %8.3f seconds\n",
         tinfo.tms_cutime/ticks);
      fprintf(stderr, "Children's system time: %8.3f seconds\n",
         tinfo.tms_cstime/ticks);
   }
}
 
int main(void) {
   if (atexit(showtimes))  {
      fprintf(stderr, "Failed to install showtimes exit handler\n");
      return 1; 
   }
   if (fork())
      wastetime(2900000);
   else
      wastetime(5400000);
   if (wait(NULL) > 0)
      fprintf(stderr, "\nChild has exited, parent stats follow:\n");
   return 0;
}

times 함수는 시간 틱(tick) 수의 시간 정보를 나타낸다.

 

 

[ 예제 코드 : wastetime.c ]

#include <stdio.h>
#include <sys/time.h>
#define MILLION 1000000L

/* waste maxus microseconds of time */
int wastetime(int maxus) {
   long timedif;
   struct timeval tp1, tp2;

   if (gettimeofday(&tp1, NULL)) {
      fprintf(stderr, "Failed to get initial time\n");
      return 1;
   }
   timedif = 0;
   while (timedif < maxus) {
      if (gettimeofday(&tp2, NULL)) {
         fprintf(stderr, "Failed to get check time\n");
         return 1;
      }
      timedif = MILLION*(tp2.tv_sec - tp1.tv_sec) +
                   tp2.tv_usec - tp1.tv_usec;
      if (timedif < 0)
         break;
   }
   return 0;
}

 

▶ 사용 예:

$ ./showtimes

User time:                         1.270 seconds

System time:                    2.660 seconds

Children's user time:      0.000 seconds

Children's system time: 0.000 seconds

 

Child has exited, parent stats follow:

User time:                        0.460 seconds

System time:                    0.970 seconds

Children's user time:      1.270 seconds

Children's system time: 2.660 seconds

 

Posted by devanix