Здесь приведен код примеров для 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 для поддержки отладки. |