【runC】04-runC-源码分析-[pause, resume]

1,941 阅读2分钟

回顾

runc 比较重要的容器启动流程前面已经基本分析完毕了,今天是最后一章主要分析runc的pause/resume;然后顺带Q一下cgroup;

技术点

  • pause/resume 的作用是挂起与恢复进程组。大致原理主要使用cgroup 的 freeze 对进程信号拦截处理;在信号处理的开始挂了一个类似于钩子的东西,把挂起进程交给信号处理来做,freeze框架要做的就是设置一些标志位来指示信号处理要冻结它了,然后设置此进程的信号附着位,这样该进程在返回用户空间的时候就乖乖进入到冻结状态了。

  • 下面的链接有 freezer 在linux中设置的方法,freezer 分为3个状态: THAWED FREEZING FROZEN

    参考: www.kernel.org/doc/Documen…

pause 命令

  • runc/libcontainer/container_linux.go

    通过cgroup manager 去管理所有类型的cgroup子系统,而且已经支持cgroupV2版本了;通过一点判定机制选用cgroupV1 还是 cgroupV2

    func (c *linuxContainer) Pause() error {
        c.m.Lock()
        defer c.m.Unlock()
        status, err := c.currentStatus()
        if err != nil {
            return err
        }
        switch status {
        case Running, Created:
        	// 设置进程的cgroup为 Frozen 冻结进程
            if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
                return err
            }
            return c.state.transition(&pausedState{
                c: c,
            })
        }
        return newGenericError(fmt.Errorf("container not running or created: %s", status), ContainerNotRunning)
    }
    
  • runc/libcontainer/cgroups/fs/freezer.go

    写入cgroup文件

    func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error {
        switch cgroup.Resources.Freezer {
        case configs.Frozen, configs.Thawed:
            for {
    	// 最终可以看到是通过写文件进行设置freezer的状态
                if err := fscommon.WriteFile(path, "freezer.state", string(cgroup.Resources.Freezer)); err != nil {
                    return err
                }
    
                state, err := s.GetState(path)
                if err != nil {
                    return err
                }
                if state == cgroup.Resources.Freezer {
                    break
                }
    
                time.Sleep(1 * time.Millisecond)
            }
        case configs.Undefined:
            return nil
        default:
            return fmt.Errorf("Invalid argument '%s' to freezer.state", string(cgroup.Resources.Freezer))
        }
    
        return nil
    }
    
    

resume 命令

  • /runc/libcontainer/container_linux.go

    设置进程的cgroup为 Thawed 解冻进程, 最终也是调用上面的freezer.go写入文件

    func (c *linuxContainer) Resume() error {
        c.m.Lock()
        defer c.m.Unlock()
        status, err := c.currentStatus()
        if err != nil {
            return err
        }
        if status != Paused {
            return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
        }
        // 设置进程的cgroup为 Thawed 解冻进程, 最终也是调用上面
        if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
            return err
        }
        return c.state.transition(&runningState{
            c: c,
        })
    }
    
    

cgroups 推荐文章

深入理解 Linux Cgroup 系列(一):基本概念

深入理解 Linux Cgroup 系列(二):玩转 CPU

深入理解 Linux Cgroup 系列(三):内存

Linux Cgroup 入门教程:cpuset

总结

  • runc的源码分析告一段落,这次写的系列算是自己的一个里程碑;回想起努力搞懂每个小的知识点还是感到小有成就的,而且对自己的golang语言也有所提升;

  • 后续计划:

  • 明年见,人生路短~~,抓紧时间了~~

    加油吧~~~少年 !!!

    On the road ~~~