前一篇日志从Checkpoint的角度分析了BLCR的软件架构,没有写最核心的做dump操作的那部分代码,这部分代码完全是在内核态运行的,涉及到进程的各种状态,包括进程的PID/PGID,虚拟内存映射,打开的文件,寄存器的状态,credentials,timers,信号状态等等。
要提一下的是昨天淘宝内核组的炳天大神给我看了一篇LWN上的文章,http://lwn.net/Articles/525675/,这个东西叫CRIU(CheckPoint/Restart in user space),是在用户态实现进程的Checkpoint/Restart,虽然是用户态的CR,但这种事情没有kernel的配合肯定是做不来的,我没有仔细去研究CRIU的实现机制,只是看了下简单的介绍,貌似是添加了一些相关的系统调用,否则这种事情单靠用户态程序肯定是办不到的,比方说PID做Checkpoint的时候有getpid()这样式syscall,可以得到进程的pid然后保存在文件里面就可以,但Restart的时候可没有setpid()这样的syscall可以用,当然CRIU和kernel进行通信也用到了/proc文件系统,相比之下BLCR和kernel通信完全只用了/proc文件系统,之后就把CR所有的工作都扔给内核去做了,在kernel中进行Checkpoint最核心的函数是cr_dump_self(),也就是进程通过ioctl向kernel发送CR_OP_HAND_CHKPT请求之后kernel对应的处理函数,接下来仔细讨论下这个函数。
对应于进程的每一个子进程或者线程都会执行这个cr_dump_self(),也就是每个task_struct对于一个cr_dump_self(),对于共享mm_struct的线程而言,这些函数只有其中一个dump mm即可,其它的只是保存一下进程的寄存器状态便可以了。
首先要获取将要dump到的文件的file struct
dest_filp = cr_loc_get(&req->dest, &shared); if (IS_ERR(dest_filp)) { return PTR_ERR(dest_filp); }
关于这个shared标志,意思就是多个进程是否共享这一个目标文件,cr_checkpoint提供了一个-d参数用来指定dump出来的文件可以放在这个目录下面,并且对每个进程都单独创建一个文件,这种时候shared就不是共享的了,每个进程对应的目标文件都是相互独立的,也就无须进行同步了,同时我也发现这个-d参数虽然有,但却标志着unimplemented,也就是说我前面说的这几句话都是废话,这个功能BLCR虽然打算提供但尚未实现,默认只能将所有进程的信息都dump到同一个文件中,shared标志为1,这时候在完成初始化之后在dump进程数据的时候就需要加锁了。
接下来BLCR把除SIGKILL之外的所有信号都block了(包括SIGSTOP),否则来个什么信号进程上下文又变了,那我们Checkpoint的数据就不可靠了。
初始化工作,给这个dump文件写一个file header,这个头写一个就够了,所有这些进程都去抢req->serial_mutex这个锁,谁抢到谁干这个事情:
down(&req->serial_mutex); if (!test_and_set_bit(0, &req->done_header)) { result = cr_save_file_header(req, dest_filp); if (result < 0) { req->result = result; } } up(&req->serial_mutex);
至于这个文件的内容就没什么好说的了,几个标志。
接下来做的工作是把共享同一个mm_struct的进程同步,保证大家在dump前停在同一个位置,这个同步就是用到了proc_req里面的barrier变量来完成的,涉及到kernel函数就是wait_event_interruptible()
// Synchronize to ensure all tasks in the current process have stopped running. once = cr_signal_predump_barrier(cr_task, /* block= */ 1); if (once < 0) { goto cleanup_unlocked; }
第一个wakeup的进程once返回1,这个返回值在暂停itimers的时候会用到,也是为了保证只有一个人把itimers暂停就可以了,谁先醒来谁就去暂停一下,其它人就不要动了。
// One task pauses the itimers if (once) { cr_pause_itimers(proc_req->itimers); // vmadump_barrier ensures this is written before reading }
接下来根据刚才提到的dump file的shared标志加锁,进入cr_do_dump()这个函数来dump进程的信息,几个共享mm_struct还是会去抢一个锁,谁先抢到谁就会成为这组进程的leader,只有leader进程才会去保存mm_struct相关的一些信息,其它的进程只是保存一下各自的寄存器信息就可以了。
接下来针对于这个proc_req(一个proc_req对应一个mm_struct)也可保存一个header,这个header主要内容是一共有几个线程在用这个mm_struct,线程的clone_flags是多少,也是几个task去抢一把锁,谁先抢到谁写这个东东。
接下来保存process linkage信息,进程的pgid,pgrp,tgid,sessionid等等这些信息。完了后进入cr_freeze_threads()这个函数,这个函数做很多dump工作,主要工作其实都是刚才选出来的那个leader来做的,其它不是leader的进程只是保存了下寄存器信息,leader做如下工作,下面就涉及到非常多的细节了,每一个都可以拿出来写一篇文章,有些太过于细节的东西我也没仔细看,就简单介绍一下:
1. 写一个头信息:
static struct vmadump_header header ={VMAD_MAGIC, VMAD_FMT_VERS, VMAD_ARCH, (LINUX_VERSION_CODE >> 16) & 0xFF, (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF };
2. 保存PID
3. 保存Credentials, cr_save_creds()
4. 保存cpu状态信息, vmadump_store_cpu()
5. 保存signal信息,所有的进程都会把sigblock和sigpending信息dump出来,但只有leader进程会把sigaction给dump出来。
6. 保存current->clear_child_id, current->personality
7. 保存memory信息,这部分信息是只有leader进程才会去保存的,vmadump这块是比较复杂的,主要是遍历mm_struct的vma,BLCR可以控制哪里vma可以被dump出来,cr_checkpoint提供了如下的参数:
Options to save optional portions of memory:
–save-exe
save the executable file.–save-private
save private mapped files. (executables and libraries are mapped this way)–save-shared
save shared mapped files. (System V IPC is mapped this way).–save-all
save all of the above.–save-none
save none of the above (the default).
首先非0匿名页是肯定需要保存的,其它的像可执行文件,mmap进来的库文件,和mmap的共享文件都是可以根据用户指定的checkpoint选项来dump的。
从cr_freeze_threads()出来后继续在cr_do_vmadump()这个函数往下跑,接下来做如下几件事情:
1. 保存fs信息(cr_save_fs_struct()),主要保存umask,root path和current path.
2. 保存mmap() table (cr_save_mmaps_maps())
3. 保存itimers() (cr_save_itimers())
4. 保存mmaped() pages (cr_save_mmaps_data())
5. 保存打开的文件 (cr_save_all_files())
以上每一步都可以展开写很多东西,细节太多了,也鉴于我本人水平有限,有些东西也可能理解的不对就暂时不往细里写了,有时间我把vmadump这块仔细整理下。
跑到这里对于某一个proc_req的dump基本就完了,再退回到上层函数写一个tailer就OK了,然后把停掉的itimers打开,再把block的信号给恢复过来就完成了。
可以说整个这个过程需要对内核有非常深入的理解才能完成,我这种小白只是拿过来别人的代码学习一下,也确实通过这个东西对kernel多了很多理解,过段时间有空了把Restart过程写一下,相比于Checkpoint,Restart会有很多技巧