用ptrace实现进程间通信--after的实现
时间:2006-04-22 来源:xiaosuo
背景:
在有的情况下我们不得不监视一个已经运行的进程的结束,比如说在睡觉之前我想升级我的Gentoo系统,所以我运行了emerge -aDNtuv world,可是在我想睡觉的时候发现升级还没有完成,当然这和我事先对时间估计不足有关,为了节省电能,我不得不监视进程的结束,然后运行shutdown -h now命令。如果不这样,直接打断进程的执行,将会更加麻烦。比较普遍的做法是,自己写一个脚本监视进程的结束,如下:
基本要求实现了,但现在有两个问题:
ptrace:
ptrace是内核为调试程序留的系统调试接口,gdb,strace都是用这个系统调用实现的。ptrace不仅能够跟踪调试进程的子进程,还能attach到一个正在运行的进程上,进程实时监控。当ptrace请求attach到一个进程时,kernel先进程权限的检查,如果权限允许,那么attach成功,被attach的进程在形式上将成为执行attach调用的子进程。其效果和执行了PTRACE_TRACEME的效果一样,当其接收到除SIGKILL外的任何信号时,他将向监视进程发送SIGCHLD信号报告自己收到了信号,然后停止运行等待监视进程做出决策,监视进程可以用信号SIGCHLD的信号处理函数和wait调用得到这个Notify,然后决策!另外当其执行类似exec的系统调用转而执行别的命令的时候,如果监视进程想要,它会向监视进程传递SIGTRAP信号。
实现:
我们可应用ptrace实现我们的目的。具体代码如下:
局限性:
这里只是给出了ptrace调用在等待一个进程退出时的应用,开动脑筋,应该还能应用于其它非父子关系的进程间通信!
在有的情况下我们不得不监视一个已经运行的进程的结束,比如说在睡觉之前我想升级我的Gentoo系统,所以我运行了emerge -aDNtuv world,可是在我想睡觉的时候发现升级还没有完成,当然这和我事先对时间估计不足有关,为了节省电能,我不得不监视进程的结束,然后运行shutdown -h now命令。如果不这样,直接打断进程的执行,将会更加麻烦。比较普遍的做法是,自己写一个脚本监视进程的结束,如下:
while : ; do ps -C $prog &> /dev/null if [ $? -ne 0 ]; then break; fi echo $prog is running! try it 5 minutes later! sleep 300 done |
- 用轮询的方式效率不高,浪费系统资源。
- 不能够及时的发现进程的终止,精度不够高。
ptrace:
ptrace是内核为调试程序留的系统调试接口,gdb,strace都是用这个系统调用实现的。ptrace不仅能够跟踪调试进程的子进程,还能attach到一个正在运行的进程上,进程实时监控。当ptrace请求attach到一个进程时,kernel先进程权限的检查,如果权限允许,那么attach成功,被attach的进程在形式上将成为执行attach调用的子进程。其效果和执行了PTRACE_TRACEME的效果一样,当其接收到除SIGKILL外的任何信号时,他将向监视进程发送SIGCHLD信号报告自己收到了信号,然后停止运行等待监视进程做出决策,监视进程可以用信号SIGCHLD的信号处理函数和wait调用得到这个Notify,然后决策!另外当其执行类似exec的系统调用转而执行别的命令的时候,如果监视进程想要,它会向监视进程传递SIGTRAP信号。
实现:
我们可应用ptrace实现我们的目的。具体代码如下:
#include <stdio.h> #include <unistd.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> #include <errno.h> static int sig_received = 0; void sig_handler(int signo) { sig_received = 1; } int main(int argc, char *argv[]) { pid_t ret; pid_t child; int i; if(argc != 2){ fprintf(stderr, "Usage: %s pid\n", argv[0]); exit(1); } signal(SIGCHLD, sig_handler); /* check the parameter */ for(i = 0; argv[1][i] >= '0' && argv[1][i] <= '9'; i ++) ; if(argv[1][i] != '\0'){ fprintf(stderr, "Usage: %s pid\n", argv[0]); exit(1); } child = atoi(argv[1]); if(ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1){ perror("ptrace"); exit(1); } while(1){ if(sig_received){ /* check if the process exists */ ret = waitpid(child, NULL, WNOHANG); if(ret == -1){ if(errno == ECHILD){ /* the child has exit */ return 0; } perror("waitpid"); exit(1); } /* child is running, let it continue */ sig_received = 0; /* must before ptrace */ if(ptrace(PTRACE_CONT, child, NULL, SIGCHLD) == -1){ perror("ptrace"); exit(1); } } ret = wait(NULL); /* wait a signal */ if(ret == -1){ if(errno == EINTR) continue; perror("wait"); exit(1); }else{ continue; /* received signal from the child */ } } return 0; } |
- 不能监视没有attach权限的进程。
- 如果被监视进程中有很多信号出现的话,可能会导致其执行效率下降。
这里只是给出了ptrace调用在等待一个进程退出时的应用,开动脑筋,应该还能应用于其它非父子关系的进程间通信!
相关阅读 更多 +