Программирование PC FAQ программирования Linux: примеры Sun, April 28 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

FAQ программирования Linux: примеры Печать
Добавил(а) microsin   

Здесь приведен код примеров для FAQ по программированию Linux [1]. Описание незнакомых терминов и аббревиатур см. в Словарике статьи [2].

Перехват SIGCHLD
================

   #include < sys/types.h>  /* Подключите это перед любым другим sys заголовком. */
   #include < sys/wait.h>   /* Для waitpid() и различных макросов. */
   #include < signal.h>     /* Для функций сигналов. */
   #include < stdio.h>      /* Для fprintf(). */
   #include < unistd.h>     /* Для fork(). */
   
   void sig_chld(int);     /* Прототип для нашего обработчика SIGCHLD. */
   
   int main()
   {
      struct sigaction act;
      pid_t pid;
   
      /* Назначение sig_chld в качестве обработчика SIGCHLD. */
      act.sa_handler = sig_chld;
   
      /* В этом примере мы не хотим блокироваться на любых других сигналах. */
      sigemptyset(&act.sa_mask);
   
      /*
       * Нам интересны только те дочерние процессы, которые завершены,
       * но не те, которые были остановлены (например, когда пользователь
       * нажал в терминале Ctrl-Z).
       */
      act.sa_flags = SA_NOCLDSTOP;
   
      /*
       * Активизация этих значений. Если мы пишем реальное приложение,
       * то вероятно мы сохранили бы старое значение вместо передачи NULL.
       */
      if (sigaction(SIGCHLD, &act, NULL) < 0)
      {
         fprintf(stderr, "sigaction failed\n");
         return 1;
      }
   
      /* Fork */
      switch (pid = fork())
      {
      case -1:
         fprintf(stderr, "fork failed\n");
         return 1;
   
      case 0:                         /* дочерний процесс - немедленное завершение */
         _exit(7);                   /* статус выхода = 7 */
   
      default:                        /* родительский процесс */
         sleep(10);                  /* даем дочернему процессу время для завершения */
      }
   
      return 0;
   }
   
   /*
    * Функция обработки сигнала - она будет вызвана только когда будет получен
    * сигнал SIGCHLD, например когда дочерний процесс завершился.
    */
   void sig_chld(int signo)
   {
      int status, child_val;
   
      /* Ожидание любого дочернего процесса без блокировки. */
      if (waitpid(-1, &status, WNOHANG) < 0)
      {
         /*
          * Не рекомендуется в обработчике делать вызов стандартных функций
          * ввода/вывода наподобие fprintf(), однако это вероятно допустимо
          * в таких демонстрационных программах, как эта.
          */
         fprintf(stderr, "waitpid failed\n");
         return;
      }
   
      /*
       * Теперь у нас есть информация в status, и можно этим манипулировать
       * с помощью макросов в wait.h.
       */
      if (WIFEXITED(status))              /* был нормальный выход из дочернего процесса? */
      {
         child_val = WEXITSTATUS(status); /* получение статуса выхода дочернего процесса */
         printf("child's exited normally with status %d\n", child_val);
      }
   }

Чтение таблицы процессов - версия SUNOS 4
=========================================

   #define _KMEMUSER
   #include < sys/proc.h>
   #include < kvm.h>
   #include < fcntl.h>
   
   char regexpstr[256];
   #define INIT            register char *sp=regexpstr;
   #define GETC()          (*sp++)
   #define PEEKC()         (*sp)
   #define UNGETC(c)       (--sp)
   #define RETURN(pointer) return(pointer);
   #define ERROR(val)
   #include < regexp.h>
   
   pid_t getpidbyname(char *name,pid_t skipit)
   {
      kvm_t *kd;
      char **arg;
      int error;
      char *p_name=NULL;
      char expbuf[256];
      char **freeme;
      int curpid;
      struct user * cur_user;
      struct user myuser;
      struct proc * cur_proc;
   
      if((kd=kvm_open(NULL,NULL,NULL,O_RDONLY,NULL))==NULL)
         return(-1);
      
      sprintf(regexpstr,"^.*/%s$",name);
      compile(NULL,expbuf,expbuf+256,'\0');
   
      while(cur_proc=kvm_nextproc(kd))
      {
         curpid = cur_proc->p_pid;
         if((cur_user=kvm_getu(kd,cur_proc))!=NULL)
         {
            error=kvm_getcmd(kd,cur_proc,cur_user,&arg,NULL);
            if(error==-1)
            {
               if(cur_user->u_comm[0]!='\0')
                  p_name=cur_user->u_comm;
            }
            else
               p_name=arg[0];
         }
         if(p_name)
         {
            if(!strcmp(p_name,name))
            {
               if(error!=-1)
                  free(arg);
               if(skipit!=-1 && ourretval==skipit)
                  ourretval=-1;
               else
               {
                  close(fd);
                  break;
               }
               break;
            }
            else
            {
               if(step(p_name,expbuf))
               {
                  if(error!=-1)
                     free(arg);
                  break;
               }
            }
         }
         if(error!=-1)
            free(arg);
         p_name=NULL;
      }
      kvm_close(kd);
      if(p_name!=NULL)
         return(curpid);
      return (-1);
   }

Чтение таблицы процессов - версия SYSV
======================================

   pid_t getpidbyname(char *name,pid_t skipit)
   {
      DIR  *dp;
      struct dirent *dirp;
      prpsinfo_t retval;
      int fd;
      pid_t ourretval=-1;
   
      if((dp=opendir("/proc"))==NULL)
         return -1;
   
      chdir("/proc");
      while((dirp=readdir(dp))!=NULL)
      {
         if(dirp->d_name[0]!='.')
         {
            if((fd=open(dirp->d_name,O_RDONLY))!=-1)
            {
               if(ioctl(fd,PIOCPSINFO,&retval)!=-1)
               {
                  if(!strcmp(retval.pr_fname,name))
                  {
                     ourretval=(pid_t)atoi(dirp->d_name);
                     if(skipit!=-1 && ourretval==skipit)
                        ourretval=-1;
                     else
                     {
                        close(fd);
                        break;
                     }
                  }
               }
               close(fd);
            }
         }
      }
      closedir(dp);
      return ourretval;
   }

Чтение таблицы процессов - версия AIX 4.2
=========================================

   #include < stdio.h>
   #include < procinfo.h>
   
   int getprocs(struct procsinfo *, int, struct fdsinfo *,
                int, pid_t *, int);
   
   pid_t getpidbyname(char *name, pid_t *nextPid)
   {
      struct procsinfo  pi;
      pid_t             retval = (pid_t) -1;
      pid_t             pid;
   
      pid = *nextPid;
   
      while(1)
      {
         if(getprocs(&pi, sizeof pi, 0, 0, &pid, 1) != 1)
            break;
   
         if(!strcmp(name, pi.pi_comm))
         {
            retval = pi.pi_pid;
            *nextPid = pid;
            break;
         }
      }
   
      return retval;
   }
   
   int main(int argc, char *argv[])
   {
      int   curArg;
      pid_t pid;
      pid_t nextPid;
   
      if(argc == 1)
      {
         printf("syntax: %s < program> [program ...]\n",argv[0]);
         exit(1);
      }
   
      for(curArg = 1; curArg < argc; curArg++)
      {
         printf("Process IDs for %s\n", argv[curArg]);
   
         for(nextPid = 0, pid = 0; pid != -1;)
            if((pid = getpidbyname(argv[curArg], &nextPid)) != -1)
               printf("\t%d\n", pid);
      }
   }

Чтение таблицы процессов с помощью popen и ps
=============================================

   #include < stdio.h>      /* FILE, sprintf, fgets, puts */
   #include < stdlib.h>     /* atoi, exit, EXIT_SUCCESS */
   #include < string.h>     /* strtok, strcmp */
   #include < sys/types.h>  /* pid_t */
   #include < sys/wait.h>   /* WIFEXITED, WEXITSTATUS */
   
   char *procname(pid_t pid)
   {
      static char line[133], command[80], *linep, *token, *cmd;
      FILE *fp;
      int status;
   
      if (0 == pid) return (char *)0;
   
      sprintf(command, "ps -p %d 2>/dev/null", pid);
      fp = popen(command, "r");
      if ((FILE *)0 == fp) return (char *)0;
   
      /* read the header line */
      if ((char *)0 == fgets(line, sizeof line, fp))
      {
         pclose(fp);
         return (char *)0;
      }
   
      /* Выяснение из заголовков столбцов, где находится имя команды (машины BSD
       * выводят COMMAND в 5-ом столбце, в том время как SysV выводят CMD
       * или COMMAND в 4-ом столбце).
       */
      for (linep = line; ; linep = (char *)0)
      {
         if ((char *)0 == (token = strtok(linep, " \t\n")))
         {
            pclose(fp);
            return (char *)0;
         }
         if (0 == strcmp("COMMAND", token) || 0 == strcmp("CMD", token))
         { /*  we found the COMMAND column */
            cmd = token;
            break;
         }
      }
   
      /* Чтение строки вывода ps(1) */
      if ((char *)0 == fgets(line, sizeof line, fp))
      {
         pclose(fp);
         return (char *)0;
      }
   
      /* Извлечение "слова" под заголовком команды... */
      if ((char *)0 == (token = strtok(cmd, " \t\n")))
      {
         pclose(fp);
         return (char *)0;
      }
   
      status = pclose(fp);
      if (!WIFEXITED(status) || 0 != WEXITSTATUS(status))
         return (char *)0;
   
      return token;
   }
   
   int main(int argc, char *argv[])
   {
      puts(procname(atoi(argv[1])));
      exit(EXIT_SUCCESS);
   }

Функции утилиты демона
======================

   #include < unistd.h>
   #include < stdlib.h>
   #include < fcntl.h>
   #include < signal.h>
   #include < sys/types.h>
   #include < sys/wait.h>
   #include < errno.h>
   
   /* closeall() -- закрытие всех FD >= указанного значения */     
   void closeall(int fd)
   {
      int fdlimit = sysconf(_SC_OPEN_MAX);
   
      while (fd < fdlimit)
         close(fd++);
   }
   
   /* daemon() - отсоединение процесса от пользователя и перемещение
    * процесса в background возвратит -1 в случае ошибки, но в этом
    * случае вы ничего не сможете сделать, кроме выхода, поскольку
    * fork уже произошел. Этот пример основан на версии BSD, поэтому
    * вызывающий код несет ответственность за такие вещи, как umask и т. д.
    */
   
   /* Вероятно это будет работать на всех POSIX-системах */
   int daemon(int nochdir, int noclose)
   {
      switch (fork())
      {
         case 0:  break;
         case -1: return -1;
         default: _exit(0);          /* exit оригинального процесса */
      }
   
      if (setsid() < 0)               /* не должно быть неудачей */
         return -1;
   
      /* Заполните этот switch, если хотите получить в будущем управление
         над tty - для демонов это обычно не рекомендуется. */
      switch (fork())
      {
         case 0:  break;
         case -1: return -1;
         default: _exit(0);
      }
   
      if (!nochdir)
         chdir("/");
   
      if (!noclose)
      {
         closeall(0);
         open("/dev/null",O_RDWR);
         dup(0); dup(0);
      }
   
      return 0;
   }
   
   /* fork2() - наподобие fork, но новый процесс сразу сиротеет (не оставляя зомби при выходе).
    * Вернет 1 в родительский процесс, не какой-то осмысленный pid.
    * Родительский процесс не может вызвать wait() для нового процесса (он не привязан).
    */
   
   /* Эта версия предполагает, что вы не перехватили, или проигнорировали SIGCHLD. */
   /* Если вы все же это делаете, то должны все равно вместо этого использовать fork(). */
   int fork2()
   {
      pid_t pid;
      int rc;
      int status;
   
      if (!(pid = fork()))
      {
         switch (fork())
         {
            case 0:  return 0;
            case -1: _exit(errno);    /* подразумевается, что все ошибки < 256 */
            default: _exit(0);
         }
      }
   
      if (pid < 0 || waitpid(pid,&status,0) < 0)
         return -1;
   
      if (WIFEXITED(status))
         if (WEXITSTATUS(status) == 0)
            return 1;
      else
         errno = WEXITSTATUS(status);
      else
         errno = EINTR;  /* well, sort of :-) */
   
      return -1;
   }

Пример использования показанных выше функций:

   #include < sys/types.h>
   #include < sys/socket.h>
   #include < netinet/in.h>
   #include < stdio.h>
   #include < stdlib.h>
   #include < syslog.h>
   #include < errno.h>
   
   int daemon(int,int);
   int fork2(void);
   void closeall(int);
   
   #define TCP_PORT 8888
   
   void errexit(const char *str)
   {
      syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
      exit(1);
   }
   
   void errreport(const char *str)
   {
      syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);
   }
   
   /* Здесь реальный дочерний процесс. */
   void run_child(int sock)
   {
      FILE *in = fdopen(sock,"r");
      FILE *out = fdopen(sock,"w");
      int ch;
   
      setvbuf(in, NULL, _IOFBF, 1024);
      setvbuf(out, NULL, _IOLBF, 1024);
   
      while ((ch = fgetc(in)) != EOF)
         fputc(toupper(ch), out);
   
      fclose(out);
   }
   
   /* Здесь демон выполняет основную работу - прослушивает и порождает соединения. */
   void process()
   {
      struct sockaddr_in addr;
      int addrlen = sizeof(addr);
      int sock = socket(AF_INET, SOCK_STREAM, 0);
      int flag = 1;
      int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                          &flag, sizeof(flag));
   
      if (rc < 0)
         errexit("setsockopt");
   
      addr.sin_family = AF_INET;
      addr.sin_port = htons(TCP_PORT);
      addr.sin_addr.s_addr = INADDR_ANY;
   
      rc = bind(sock, (struct sockaddr *) &addr, addrlen);
      if (rc < 0)
         errexit("bind");
   
      rc = listen(sock, 5);
      if (rc < 0)
         errexit("listen");
   
      for (;;)
      {
         rc = accept(sock, (struct sockaddr *) &addr, &addrlen);
   
         if (rc >= 0)
         {
            switch (fork2())
            {
            case 0:  close(sock); run_child(rc); _exit(0);
            case -1: errreport("fork2"); close(rc); break;
            default: close(rc);
            }
         }
      }
   }
   
   int main()
   {
      if (daemon(0,0) < 0)
      {
         perror("daemon");
         exit(2);
      }
   
      openlog("test", LOG_PID, LOG_DAEMON);
   
      process();
   
      return 0;
   }

Пример работы с модемом
=======================

   /* Пример выдачи некоторых простых команд для модема. Программа требует указания
    * имени последовательного устройства (предпочтительно устройство dial-out,
    * или устройство non-modem-control) в качестве своего единственного параметра.
    * Если у вас нет функциональных dial-out устройств, то вместо этого переместите
    * CLOCAL в CFLAGS_TO_SET.
    */
   #include < stdio.h>
   #include < stdlib.h>
   #include < fcntl.h>
   #include < unistd.h>
   #include < sys/types.h>
   #include < sys/time.h>
   #include < sys/ioctl.h>   /* может зависеть от системы */
   #include < termios.h>
   #include < errno.h>
   #include < string.h>
   #include < ctype.h>
   
   #define CFLAGS_TO_SET (CREAD | HUPCL)
   #define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL)
   
   enum flowmode { NoFlow, HardFlow, SoftFlow };
   
   /* Зависит от системы */
   #define CFLAGS_HARDFLOW (CRTSCTS)
   
   #define EXAMPLE_BAUD B19200
   #define EXAMPLE_FLOW HardFlow
   
   static void die(const char *msg)
   {
      fprintf(stderr, "%s\n", msg);
      exit(1);
   }
   
   static int close_and_complain(int fd, const char *msg, int err)
   {
      fprintf(stderr, "%s: %s\n", msg, strerror(err));
      if (fd >= 0)
         close(fd);
      errno = err;
      return -1;
   }
   
   int open_port(const char *name, speed_t baud, enum flowmode flow)
   {
      int flags;
      struct termios attr;
   
      int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);
   
      if (fd < 0)
         return close_and_complain(-1, "open", errno);
   
      /* Установка (возможно значимых?) настроек */
      if (tcgetattr(fd, &attr) < 0)
         return close_and_complain(fd, "tcgetattr", errno);
   
      /* Нет специальной обработки ввода или вывода */
      attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0;
      attr.c_oflag = 0;
   
      /* Установка 8-битной ширины символа и различных режимов управления */
      attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW);
      attr.c_cflag |= (CS8 | CFLAGS_TO_SET);
      if (flow == HardFlow)
         attr.c_cflag |= CFLAGS_HARDFLOW;
   
      /* Локальные режимы */
      attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);
   
      /* Специальные символы - большинство отключено в любом случае
         предыдущими настройками. */
      {
         int i;
   #ifdef _POSIX_VDISABLE
         attr.c_cc[0] = _POSIX_VDISABLE;
   #else
         attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE);
   #endif
         for (i = 1; i < NCCS; i++)
            attr.c_cc[i] = attr.c_cc[0];
      }
   
      attr.c_cc[VSTART] = 0x11;
      attr.c_cc[VSTOP] = 0x13;
   
      /* Управление интервалами времени для read() */
      attr.c_cc[VMIN] = 1;
      attr.c_cc[VTIME] = 0;
   
      /* Настройка скорости на ввод и на вывод */
      cfsetispeed(&attr, baud);
      cfsetospeed(&attr, baud);
   
      /* Настройки записи */
      if (tcsetattr(fd, TCSANOW, &attr) < 0)
         return close_and_complain(fd, "tcsetattr", errno);
   
      /* Выключение O_NONBLOCK, если устройство это запомнило */
      flags = fcntl(fd, F_GETFL, 0);
      if (flags < 0)
         return close_and_complain(fd, "fcntl(GETFL)", errno);
      if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
         return close_and_complain(fd, "fcntl(SETFL)", errno);
   
      return fd;
   }
   
   /* Несколько простых утилит тайминга */
   
   /* Добавляет секунды SECS и микросекунды USECS в *TV */
   static void timeradd(struct timeval *tv, long secs, long usecs)
   {
      tv->tv_sec += secs;
      if ((tv->tv_usec += usecs) >= 1000000)
      {
         tv->tv_sec += tv->tv_usec / 1000000;
         tv->tv_usec %= 1000000;
      }
   }
   
   /* Установит *RES = *A - *B, возвратит знак результата */
   static int timersub(struct timeval *res,
                       const struct timeval *a, const struct timeval *b)
   {
      long sec = a->tv_sec - b->tv_sec;
      long usec = a->tv_usec - b->tv_usec;
   
      if (usec < 0)
         usec += 1000000, --sec;
   
      res->tv_sec = sec;
      res->tv_usec = usec;
   
      return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);
   }
   
   /* Эта функция не пытается справиться с патологическими строками
    * (например ababc), таймаут timeo указывается в миллисекундах.
    * Более практично для отслеживания таймаута использовать alarm().
    * Этот пример избегает обработки сигнала для упрощения и
    * иллюстрации альтернативного подхода.
    */
   int expect(int fd, const char *str, int timeo)
   {
      int matchlen = 0;
      int len = strlen(str);
      struct timeval now,end,left;
      fd_set fds;
      char c;
   
      gettimeofday(&end, NULL);
      timeradd(&end, timeo/1000, timeo%1000);
   
      while (matchlen < len)
      {
         gettimeofday(&now, NULL);
         if (timersub(&left, &end, &now) < = 0)
            return -1;
   
         FD_ZERO(&fds);
         FD_SET(fd, &fds);
         if (select(fd+1, &fds, NULL, NULL, &left) < = 0)
            return -1;
   
         if (read(fd, &c, 1) != 1)
            return -1;
   
         if (isprint((unsigned char)c) || c == '\n' || c == '\r')
            putchar(c);
         else
            printf("\\x%02x", c);
   
         if (c == str[matchlen])
            ++matchlen;
         else
            matchlen = 0;
      }
   
      return 0;
   }
   
   int main(int argc, char **argv)
   {
      int fd;
      unsigned char c;
   
      if (argc < 2)
         die("no port specified");
   
      setvbuf(stdout, NULL, _IONBF, 0);
   
      fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW);
      if (fd < 0)
         die("cannot open port");
   
      write(fd, "AT\r", 3);
      if (expect(fd, "OK", 5000) < 0)
      {
         write(fd, "AT\r", 3);
         if (expect(fd, "OK", 5000) < 0)
         {
            tcflush(fd, TCIOFLUSH);
            close(fd);
            die("no response to AT");
         }
      }
   
      write(fd, "ATI4\r", 5);
      expect(fd, "OK", 10000);
   
      putchar('\n');
   
      tcflush(fd, TCIOFLUSH);
      close(fd);
   
      return 0;
   }

Пример Job Control (управление заданиями)
=========================================

   /* Функции для порождения foreground/background заданий */
   #include < stdio.h>
   #include < unistd.h>
   #include < stdlib.h>
   #include < fcntl.h>
   #include < signal.h>
   #include < sys/types.h>
   #include < sys/wait.h>
   #include < errno.h>
   
   /* Некоторые из этих функций не будут работать, если не может быть
    * обнаружено управление tty, или если вызывающий процесс не может
    * работать в foreground. В первом случае мы подразумеваем, что
    * в foreground процессе открыт ctty либо в stdin, либо в stdout
    * или stderr, и мы возвращаем ENOTTY, если это не так. Во втором
    * случае мы вернем EPERM, если non-foreground процесс попытается
    * поместить что-то в foreground (что вероятно слишком параноидально)
    * за исключением специального случая foreground_self().
    */
   
   /* Назначение терминала (открытого на ctty) определенной pgrp. Это
    * обертка вокруг tcsetpgrp() нужна только из-за крайней замороченности
    * со стороны POSIX; удобные системы доставляют STGTTOU, если tcsetpgrp
    * была вызвана из non-foreground процесса (что почти всегда так и есть).
    * Триумф ложной последовательности над здравым смыслом.
    */
   int assign_terminal(int ctty, pid_t pgrp)
   {
      sigset_t sigs;
      sigset_t oldsigs;
      int rc;
   
      sigemptyset(&sigs);
      sigaddset(&sigs,SIGTTOU);
      sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
   
      rc = tcsetpgrp(ctty, pgrp);
   
      sigprocmask(SIG_SETMASK, &oldsigs, NULL);
   
      return rc;
   }
   
   /* Наподобие fork(), но выполняет job control. FG == true, если новый
    * созданный процесс помещен в foreground (это неявно помещает вызывающий
    * процесс в background, так что после этого следите за tty I/O.
    * PGRP == -1 для создания нового job, в этом случае возвращенный pid
    * также pgrp нового job, или указывает существующий job в той же
    * сессии (обычно используется только для запуска второго или
    * последующего процесса в конвейере). */
   pid_t spawn_job(int fg, pid_t pgrp)
   {
      int ctty = -1;
      pid_t pid;
   
      /* Если порождается НОВЫЙ foreground job, то требуется, чтобы как минимум
       * один stdin, stdout или stderr ссылался на tty управления, и чтобы
       * текущий процесс находился в foreground. Проверка наличия tty управления
       * выполняется только при запуске нового процесса основной системы
       * в существующем задании. Сессия без управляющего tty может иметь
       * только фоновые задания (background jobs).
       */
      if (fg)
      {
         pid_t curpgrp;
   
         if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
             && (curpgrp = tcgetpgrp(ctty = 0)) < 0
             && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
            return errno = ENOTTY, (pid_t)-1;
   
         if (pgrp < 0 && curpgrp != getpgrp())
            return errno = EPERM, (pid_t)-1;
      }
   
      switch (pid = fork())
      {
      case -1: /* ошибка fork */
         return pid;
   
      case 0: /* дочерний процесс */
   
         /* Установка новой process group и помещение себя в foreground,
          * если необходимо. Неясно, что делать, если setpgid потерпит
          * неудачу ("не может произойти").
          */
         if (pgrp < 0)
            pgrp = getpid();
   
         if (setpgid(0,pgrp) == 0 && fg)
            assign_terminal(ctty, pgrp);
   
         return 0;
   
      default: /* родительский процесс */
   
         /* Здесь также установка дочерней process group. */
         if (pgrp < 0)
            pgrp = pid;
   
         setpgid(pid, pgrp);
   
         return pid;
      }
   
      /* Сюда не попадем */
   }
   
   
   /* Прибивает job PGRP с сигналом SIGNO */
   int kill_job(pid_t pgrp, int signo)
   {
      return kill(-pgrp, signo);
   }
   
   /* Приостановит job PGRP */
   int suspend_job(pid_t pgrp)
   {
      return kill_job(pgrp, SIGSTOP);
   }
   
   /* Возобновит PGRP в background */
   int resume_job_bg(pid_t pgrp)
   {
      return kill_job(pgrp, SIGCONT);
   }
   
   /* Возобновит job PGRP в foreground */
   int resume_job_fg(pid_t pgrp)
   {
      pid_t curpgrp;
      int ctty;
   
      if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
           && (curpgrp = tcgetpgrp(ctty = 0)) < 0
           && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
           return errno = ENOTTY, (pid_t)-1;
   
      if (curpgrp != getpgrp())
         return errno = EPERM, (pid_t)-1;
   
      if (assign_terminal(ctty, pgrp) < 0)
         return -1;
   
      return kill_job(pgrp, SIGCONT);
   }
   
   /* Поместит саму себя в foreground, например после приостановки
    * foreground job.
    */
   int foreground_self()
   {
      pid_t curpgrp;
      int ctty;
   
      if ((curpgrp = tcgetpgrp(ctty = 2)) < 0
          && (curpgrp = tcgetpgrp(ctty = 0)) < 0
          && (curpgrp = tcgetpgrp(ctty = 1)) < 0)
          return errno = ENOTTY, (pid_t)-1;
   
      return assign_terminal(ctty, getpgrp());
   }
   
   /* closeall() - закроет FD >= указанного значения */
   void closeall(int fd)
   {
      int fdlimit = sysconf(_SC_OPEN_MAX);
   
      while (fd < fdlimit)
         close(fd++);
   }
   
   /* Наподобие system(), но выполнит указанную команду как background job,
    * возвращая pid процесса шелла (который также является pgrp задания,
    * подходит для kill_job, и т. п.). Если INFD, OUTFD или ERRFD не-NULL,
    * то будет открыт pipe, и сюда будет сохранен дескриптор родительской
    * стороны соответствующего pipe. Если любой из них, то они будут
    * перенаправлены в /dev/null дочернего процесса.
    * Также закроет все FD > 2 в дочернем процессе (часто упускаемый
    * из виду момент).
    */
   pid_t spawn_background_command(const char *cmd,
                                  int *infd, int *outfd, int *errfd)
   {
      int nullfd = -1;
      int pipefds[3][2];
      int error = 0;
   
      if (!cmd)
         return errno = EINVAL, -1;
   
      pipefds[0][0] = pipefds[0][1] = -1;
      pipefds[1][0] = pipefds[1][1] = -1;
      pipefds[2][0] = pipefds[2][1] = -1;
   
      if (infd && pipe(pipefds[0]) < 0)
         error = errno;
      else if (outfd && pipe(pipefds[1]) < 0)
         error = errno;
      else if (errfd && pipe(pipefds[2]) < 0)
         error = errno;
   
      if (!error && !(infd && outfd && errfd))
      {
         nullfd = open("/dev/null",O_RDWR);
         if (nullfd < 0)
            error = errno;
      }
   
      if (!error)
      {
         pid_t pid = spawn_job(0, -1);
         switch (pid)
         {
         case -1: /* fork failure */
            error = errno;
            break;
   
         case 0: /* child proc */
            dup2(infd ? pipefds[0][0] : nullfd, 0);
            dup2(outfd ? pipefds[1][1] : nullfd, 1);
            dup2(errfd ? pipefds[2][1] : nullfd, 2);
            closeall(3);
   
            execl("/bin/sh","sh","-c",cmd,(char*)NULL);
   
            _exit(127);
   
         default: /* parent proc */
            close(nullfd);
            if (infd)
               close(pipefds[0][0]), *infd = pipefds[0][1];
            if (outfd)
               close(pipefds[1][1]), *outfd = pipefds[1][0];
            if (errfd)
               close(pipefds[2][1]), *errfd = pipefds[2][0];
   
            return pid;
         }
      }
   
      /* Сюда попадем только в случае ошибки */
      {
         int i,j;
         for (i = 0; i < 3; ++i)
            for (j = 0; j < 2; ++j)
               if (pipefds[i][j] >= 0)
                  close(pipefds[i][j]);
      }
   
      if (nullfd >= 0)
         close(nullfd);
   
      return errno = error, (pid_t) -1;
   }
   
   /*---------------------------------------------------------------------*/
   /* Это довольно тривиальный пример, использующий приведенный выше код. */
   pid_t bgjob = -1;
   volatile int signo = 0;
   
   #ifndef WCOREDUMP
    /* Если WCOREDUMP отсутствует, то вы можете захотеть предоставить
     * корректное определение для своей платформы (это обычно (status & 0x80),
     * но не всегда), или пропустите (как в этом примере) подразумевая
     * отсутствие дампов ядра.
     */
   # define WCOREDUMP(status) (0)
   #endif
   
   int check_children()
   {
      pid_t pid;
      int status;
      int count = 0;
   
      while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
      {
         if (pid == bgjob && !WIFSTOPPED(status))
             bgjob = -1;
   
         ++count;
   
         if (WIFEXITED(status))
            fprintf(stderr,"Process %ld exited with return code %d\n",
                    (long)pid, WEXITSTATUS(status));
         else if (WIFSIGNALED(status))
            fprintf(stderr,"Process %ld killed by signal %d%s\n",
                    (long)pid, WTERMSIG(status),
                    WCOREDUMP(status) ? " (core dumped)" : "");
         else if (WIFSTOPPED(status))
            fprintf(stderr,"Process %ld stopped by signal %d\n",
                    (long)pid, WSTOPSIG(status));
         else
            fprintf(stderr,"Unexpected status - pid=%ld, status=0x%x\n",
                    (long)pid, status);
      }
   
      return count;
   }
   
   void sighandler(int sig)
   {
      if (sig != SIGCHLD)
         signo = sig;
   }
   
   int main()
   {
      struct sigaction act;
      int sigcount = 0;
   
      act.sa_handler = sighandler;
      act.sa_flags = 0;
      sigemptyset(&act.sa_mask);
      sigaction(SIGINT,&act,NULL);
      sigaction(SIGQUIT,&act,NULL);
      sigaction(SIGTERM,&act,NULL);
      sigaction(SIGTSTP,&act,NULL);
      sigaction(SIGCHLD,&act,NULL);
   
      fprintf(stderr,"Starting background job 'sleep 60'\n");
      bgjob = spawn_background_command("sleep 60", NULL, NULL, NULL);
      if (bgjob < 0)
      {
         perror("spawn_background_command");
         exit(1);
      }
      fprintf(stderr,"Background job started with id %ld\n", (long)bgjob);
      while (bgjob >= 0)
      {
         if (signo)
         {
            fprintf(stderr,"Signal %d caught\n", signo);
            if (sigcount++)
               kill_job(bgjob, SIGKILL);
            else
            {
               kill_job(bgjob, SIGTERM);
               kill_job(bgjob, SIGCONT);
            }
         }
   
         if (!check_children())
            pause();
      }
   
      fprintf(stderr,"Done - exiting\n");
      return 0;
   }

[Ссылки]

1. Unix Programming FAQ (v1.37) site:opennet.ru.
2. FAQ программирования Linux: управление процессами.
3Опции GCC для поддержки отладки.

 

Добавить комментарий


Защитный код
Обновить

Top of Page