1、问题:
通过docker exec产生的进程bash(5704)看ppid是docker-containe(5564),但是通过ptrace进程号5564没有关于clone的系统调用,就算ptrace进程号3594、3542也没有什么有意义的。所以我有一个疑问,这个bash(5704)是由谁产生的?以下通过实验来说明。
2、解决:
利用kprobes中的探测技术kretprobe(基于kprobe实现),探测sys_clone函数的返回值(此返回值为新进程的pid),同时也输出当前进程号
#include <linux/kernel.h> #include <linux/module.h> #include <linux/kprobes.h> #include <linux/ktime.h> #include <linux/limits.h> #include <linux/sched.h> static char func_name[NAME_MAX] = "sys_clone"; module_param_string(func, func_name, NAME_MAX, S_IRUGO); MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the" " function's execution time"); /* per-instance private data */ struct my_data { ktime_t entry_stamp; }; /* Here we use the entry_hanlder to timestamp function entry */ static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { struct my_data *data; if (!current->mm) return 1; /* Skip kernel threads */ data = (struct my_data *)ri->data; data->entry_stamp = ktime_get(); return 0; } /* * Return-probe handler: Log the return value and duration. Duration may turn * out to be zero consistently, depending upon the granularity of time * accounting on the platform. */ static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { int retval = regs_return_value(regs); struct my_data *data = (struct my_data *)ri->data; s64 delta; ktime_t now; now = ktime_get(); delta = ktime_to_ns(ktime_sub(now, data->entry_stamp)); printk(KERN_INFO "current task(%d) is %s, %s returned %d and took %lld ns to execute ", current->pid, current->comm, func_name, retval, (long long)delta); return 0; } static struct kretprobe my_kretprobe = { .handler = ret_handler, .entry_handler = entry_handler, .data_size = sizeof(struct my_data), /* Probe up to 20 instances concurrently. */ .maxactive = 20, }; static int __init kretprobe_init(void) { int ret; my_kretprobe.kp.symbol_name = func_name; ret = register_kretprobe(&my_kretprobe); if (ret < 0) { printk(KERN_INFO "register_kretprobe failed, returned %d ", ret); return -1; } printk(KERN_INFO "Planted return probe at %s: %p ", my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr); return 0; } static void __exit kretprobe_exit(void) { unregister_kretprobe(&my_kretprobe); printk(KERN_INFO "kretprobe at %p unregistered ", my_kretprobe.kp.addr); /* nmissed > 0 suggests that maxactive was set too low. */ printk(KERN_INFO "Missed probing %d instances of %s ", my_kretprobe.nmissed, my_kretprobe.kp.symbol_name); } module_init(kretprobe_init) module_exit(kretprobe_exit) MODULE_LICENSE("GPL");
3、结果
探针输出结果如下:
current task(3884) is docker-containe, sys_clone returned 5564 current task(5564) is docker-containe, sys_clone returned 5565 current task(5564) is docker-containe, sys_clone returned 5566 current task(5564) is docker-containe, sys_clone returned 5567 current task(5564) is docker-containe, sys_clone returned 5568 current task(5564) is docker-containe, sys_clone returned 5569 current task(5565) is docker-containe, sys_clone returned 5570 ....... current task(5570) is docker-containe, sys_clone returned 5696 current task(5696) is docker-runc, sys_clone returned 5697 current task(5696) is docker-runc, sys_clone returned 5698 current task(5696) is docker-runc, sys_clone returned 5699 current task(5698) is docker-runc, sys_clone returned 5700 current task(5699) is docker-runc, sys_clone returned 5701 current task(5701) is docker-runc, sys_clone returned 5702 current task(5702) is runc:[0:PARENT], sys_clone returned 5703 current task(5703) is runc:[1:CHILD], sys_clone returned 5704 current task(5704) is runc:[2:INIT], sys_clone returned 70
通过pstree -p看到进程树
├─dockerd(3542)─┬─docker-containe(3594)─┬─docker-containe(5564)─┬─bash(5704) │ │ │ ├─bash(6053) │ │ │ ├─mysqld(5580)─┬─{mysqld}(5640) │ │ │ │ ├─{mysqld}(5641) │ │ │ │ ├─{mysqld}(5642) │ │ │ │ ├─{mysqld}(5643) │ │ │ │ ├─{mysqld}(5644) │ │ │ │ ├─{mysqld}(5645) │ │ │ │ ├─{mysqld}(5646) │ │ │ │ ├─{mysqld}(5647) │ │ │ │ ├─{mysqld}(5648) │ │ │ │ ├─{mysqld}(5649) │ │ │ │ ├─{mysqld}(5650) │ │ │ │ ├─{mysqld}(5651) │ │ │ │ ├─{mysqld}(5652) │ │ │ │ ├─{mysqld}(5653) │ │ │ │ ├─{mysqld}(5654) │ │ │ │ ├─{mysqld}(5655) │ │ │ │ ├─{mysqld}(5656) │ │ │ │ ├─{mysqld}(5657) │ │ │ │ ├─{mysqld}(5658) │ │ │ │ ├─{mysqld}(5659) │ │ │ │ ├─{mysqld}(5660) │ │ │ │ ├─{mysqld}(5661) │ │ │ │ ├─{mysqld}(5662) │ │ │ │ ├─{mysqld}(5663) │ │ │ │ ├─{mysqld}(5664) │ │ │ │ ├─{mysqld}(5665) │ │ │ │ ├─{mysqld}(5666) │ │ │ │ ├─{mysqld}(5667) │ │ │ │ ├─{mysqld}(5671) │ │ │ │ ├─{mysqld}(5672) │ │ │ │ ├─{mysqld}(5673) │ │ │ │ ├─{mysqld}(5674) │ │ │ │ ├─{mysqld}(5675) │ │ │ │ ├─{mysqld}(5676) │ │ │ │ └─{mysqld}(5677) │ │ │ ├─{docker-containe}(5565) │ │ │ ├─{docker-containe}(5566) │ │ │ ├─{docker-containe}(5567) │ │ │ ├─{docker-containe}(5568) │ │ │ ├─{docker-containe}(5569) │ │ │ ├─{docker-containe}(5570) │ │ │ ├─{docker-containe}(5571) │ │ │ └─{docker-containe}(5715) │ │ ├─{docker-containe}(3595) │ │ ├─{docker-containe}(3596) │ │ ├─{docker-containe}(3597) │ │ ├─{docker-containe}(3598) │ │ ├─{docker-containe}(3599) │ │ ├─{docker-containe}(3600) │ │ ├─{docker-containe}(3601) │ │ ├─{docker-containe}(3602) │ │ ├─{docker-containe}(3883) │ │ ├─{docker-containe}(3884) │ │ └─{docker-containe}(5993)
一个图总结,其中红线部分运行完就退出了,所以pstree -p看不到:
所以根本就不是由docker-contained-shim(5564)产生的bash进程,所以ptrace没什么结果。
一个问题:以上红框内runc退出后其子进程bash不是应该由init进程接管吗?如何变成docker-containerd-shim的子进程呢?
解答:从Linux 3.4开始,prctl增加了对PR_SET_CHILD_SUBREAPER的支持,这样就可以控制孤儿进程可以被谁接管,而不是像以前一样只能由init进程接管。
4、后续
左边是strace结果,右边是kret结果。ppid是通过status查看的,可以看到很多不是28323产生的进程,都通过prctl由它接管了
但左边28330显示是parent,而右边显示28330是child
即为:红线内是kret视角(kret一起监控clone和execve,发现parent等的确没有发生execve)
原因猜测:
先clone原进程,新进程刚要运行就会因为被标记的延迟信号(SIGSTOP)要处理,所以第一时间就陷入到strace(strace跟踪多线程与内核的交互),strace此时wait到的pid是新进程的,而name是刚才clone的父进程的。
而后该新进程正常运行,改名(可以通过prctl(PR_SET_NAME, new_name)。参考:linux下修改进程名称),所以该进程发生clone时name和strace看到的已经不同了。
以上是一种根据现象猜测的原因,具体是不是这样需要看docker的在这部分的实现,为啥改名呢?