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. |