- 论坛徽章:
- 1
|
原 C/C++ 论坛 FAQ,尚未整理。
- 6.5 我能在一个运行着的程序中生成堆栈映象吗?
- ============================================
- 一些系统提供库函数可以提取(unwinding)出堆栈,从而(比如)你可以在一个错误
- 处理函数中生成一个堆栈映象,但是只有一小部分系统有这些函数。
- 一个可能的变通方法(workaround)是让你的程序执行一个调试器调试*它自己* -
- 详细方法仍然根据不同系统稍有不同,但一般的概念是这样:
- void dump_stack(void)
- {
- char s[160];
- sprintf(s, "/bin/echo 'where\ndetach' | dbx -a %d", getpid());
- system(s);
- return;
- }
- 你需要根据你不同的系统对dbx的参数和命令进行加工,或者甚至换另一个调试
- 器,比如‘gdb’,但这仍然是我见过的对于这种问题最普遍的解决方法。为此,
- 荣誉授予Ralph Corderoy。
- 下面列表包含在一些系统上需要用到的命令行:
- 大多数使用dbx的系统
- `"/bin/echo 'where\ndetach' | dbx /path/to/program %d"'
- AIX
- `"/bin/echo 'where\ndetach' | dbx -a %d"'
- IRIX
- `"/bin/echo 'where\ndetach' | dbx -p %d"'
- ?
- 范例程序
- ********
- 捕获 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 信号处理函数的原形(prototype) */
-
- int main()
- {
- struct sigaction act;
- pid_t pid;
-
- /* 设定sig_chld函数作为我们SIGCHLD信号的处理函数 */
- act.sa_handler = sig_chld;
-
- /* 在这个范例程序里,我们不想阻塞其它信号 */
- sigemptyset(&act.sa_mask);
-
- /*
- * 我们只关谋恢罩沟淖咏?蹋??皇潜恢卸? * 的子进程 (比如用户在终端上按Control-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(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;
-
- /* 读取标题行 */
- 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))
- { /* 我们找到COMMAND所在列 */
- cmd = token;
- break;
- }
- }
-
- /* 读取 ps(1) 输出行 */
- if ((char *)0 == fgets(line, sizeof line, fp))
- {
- pclose(fp);
- return (char *)0;
- }
-
- /* 抓COMMAND标题下面的词 ... */
- 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() -- 关闭所有>;=给定值的文件描述符 */
-
- void closeall(int fd)
- {
- int fdlimit = sysconf(_SC_OPEN_MAX);
-
- while (fd < fdlimit)
- close(fd++);
- }
-
- /* daemon() - 将进程从用户端脱离并消失进入后台,若失败返回-1,
- * 但是在那种情况下你只能退出,因为我们可能已经生成了子进程。
- * 这是基于BSD的版本,所以调用方需负责类似umask等等其它的工作。
- */
-
- /* 相信在所有Posix系统上都能工作 */
-
- int daemon(int nochdir, int noclose)
- {
- switch (fork())
- {
- case 0: break;
- case -1: return -1;
- default: _exit(0); /* 原进程退出 */
- }
-
- if (setsid() < 0) /* 不应该失败 */
- return -1;
-
- /* 如果你希望将来获得一个控制tty,则排除(dyke)以下的switch语句 */
- /* -- 正常情况不建议用于守护程序 */
-
- 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给父进程,不是任何有意义的进程号.
- * 父进程不能使用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; /* 唉,类似这个 :-) */
-
- 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;
- }
- 调制解调器控制范例程序
- ======================
- /* 发出一些简单调制解调器命令
- * 需要串行设备的设备名 (最好是拨出设备,
- * 或者是非调制解调器控制设备) 作为它唯一的参数.
- * 如果你没有可共使用的拨出设备, 那么以CFLAGS_TO_SET取代CLOCAL。
- */
-
- #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;
- }
-
- /* 一些简单的计时工具函数 */
-
- /* 向*TV加 SECS 和USECS */
-
- 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)
- * 超时以微妙计
- * 一个更通常的做法是使用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;
- }
- 事务控制范例程序
- ================
- /* 生成前台/后台事务的函数 */
-
- #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和调用方不在前台而失败。
- * 第一种情况时,我们假设一个前台程序会有为标准输入,标准输出或标准错误输出打开的ctty,
- * 而如果没有则返回ENOTTY。
- * 第二种情况时,除foreground_self()函数的特殊情况以外,
- * 若一个非前台程序打算输出一些东西到前台,我们返回EPERM。
- * (也许想得太多了)
- */
-
-
- /* 为给定的pgrp安排一个终端 (打开一个ctty) .
- * 这个tcsetpgrp()外壳程序只是因为POSIX中特别错误(bogusity)的地方而需要;
- * 遵照标准的系统在一个非前台进程调用tcsetpgrp函数时传递SIGTTOU
- * 信号(差不多总是这样)。这是虚假的一致性之于一般想法的胜利。
- */
-
- 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函数,但做事务控制。如果新建立的进程放在前台则设fg为真。
- * (这样隐式地将调用方进程放置到后台,所以做完这个后要当心tty的输入/输出)
- * 设定pgrp为-1以创建一个新事务,在此情况下返回的进程号即是新事务的进程组号,
- * 或者设定一个同一会话中存在的事务(一般只用来启动管道操作的第二个或第二个以后
- * 的进程)。
- */
-
- pid_t spawn_job(int fg, pid_t pgrp)
- {
- int ctty = -1;
- pid_t pid;
-
- /* 如果生成一个*新*的前台事务,起码要求标准输入,标准输出或
- * 标准错误输出的其中一个指向的是控制tty,并且当前进程在前台。
- * 只有当在存在事务中开始一个新前台进程时才检查控制中的tty。
- * 一个没有控制tty的会话只能有后台事务。
- */
-
- 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: /* 子进程 */
-
- /* 建立新进程组, 如果需要则将我们放到前台
- * 不知道如果setpgid函数调用失败该怎么办(“不会发生”)
- */
-
- if (pgrp < 0)
- pgrp = getpid();
-
- if (setpgid(0,pgrp) == 0 && fg)
- assign_terminal(ctty, pgrp);
-
- return 0;
-
- default: /* 父进程 */
-
- /* 这里也建立自进程组. */
-
- if (pgrp < 0)
- pgrp = pid;
-
- setpgid(pid, pgrp);
-
- return pid;
- }
-
- /*不会执行到这里*/
- }
-
-
- /* 用SIGNO表示的信号杀死PGRP表示的事务 */
-
- int kill_job(pid_t pgrp, int signo)
- {
- return kill(-pgrp, signo);
- }
-
-
- /* 中断PGRP表示的事务 */
-
- int suspend_job(pid_t pgrp)
- {
- return kill_job(pgrp, SIGSTOP);
- }
-
-
- /* 继续在后台执行PGRP表示的事务 */
-
- int resume_job_bg(pid_t pgrp)
- {
- return kill_job(pgrp, SIGCONT);
- }
-
-
- /* 继续在前台执行PGRP表示的事务 */
-
- 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);
- }
-
-
- /* 将我们自己放置到前台,比如在中断一个前台事务之后调用
- */
-
- 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()函数,但将给定的命令作为后台事务执行,返回shell进程
- * 的进程号(并且也是这个事务的进程组号,适用于kill_job等等)。
- * 如果参数INFD,OUTFD或ERRFD为非NULL,则打开一个管道和一个文件描述
- * 符保存与该管道有关的父进程端,然后在子进程中将被从定向到/dev/null。
- * 并且在子进程中关闭所有>;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失败 */
- error = errno;
- break;
-
- case 0: /* 子进程 */
-
- 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: /* 父进程 */
-
- 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) 但也不总是这样),或者就赌没有core dumps(
- * 就象这个程序所做)
- */
- # 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;
- }
- =========================================================================
- Andrew (译者注:Andrew为原英文版编辑者Andrew Gierth,中文版由Edward Jiang
- <Edward.Jiang@oracle.com>; 发起并组织翻译和编辑)
复制代码 |
|