精华内容
下载资源
问答
  • 容器进程(QQExternal)

    2019-07-18 11:03:59
    容器进程的意义在于这个进程并没有指定的功能,他只是负责来加载其他模块(dll),相对于普通进程的优势就是,不用单独去做一个进程,在VS中我们只用关注实现逻辑就好,当需要这个功能单独运行在一个进程中的时候,...

    容器进程的意义在于这个进程并没有指定的功能,他只是负责来加载其他模块(dll),相对于普通进程的优势就是,不用单独去做一个进程,在VS中我们只用关注实现逻辑就好,当需要这个功能单独运行在一个进程中的时候,我们可以方便的用容器进程去加载他,类似于Windows自带的rundll功能。但是我们的容器进程可以有更强大的辅助功能,可以提供进程间通信接口,让多进程编程更加容易。如果关注过QQ的QQExternal进程,就会发现这个进程往往有多个,而且个数不定,这个就是QQ的容器进程,他可能会运行web的模块也可能是其他插件模块。

    一个简单的容器进程,非常好实现,一个main函数,解析main参数,因为对要加载的dll接口参数变成了从命令行传入。解析命令行参数的dll地址,加载该dll,再将剩余的参数传入dll的固定接口。以为我们是一个容器进程,所以对dll的调用接口有要求,可以固定为void Start(std::vector<std::string> param); 这样的,方便传入多个参数,当然这块可以根据情况设计,但是其他需要被加载的dll模块导出的接口需要遵循这同一个规则。

     下面是一个用QT工程实现的基本的容器进程模块:

    #include <QApplication>
    #include <QLibrary>
    typedef void(*StartFunc)(QStringList param);
    int main(int argc, char *argv[])
    {
    	// 开启高DPI支持
    	QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    	QApplication a(argc, argv);
    	QStringList args = QApplication::arguments();
    	QString dll = args[1].toUtf8();
    	QStringList param;
    	for (int i=2; i< args.size(); ++i)
    	{
    		param << args[i];
    	}
    	QLibrary lib(dll);
    	StartFunc startFunc = (StartFunc)lib.resolve("start");
    	if (startFunc)
    	{
    		startFunc(param);
    	}
    	return a.exec();
    }

    需要该容器加载dll,需要导出typedef void(*StartFunc)(QStringList param);该接口供容器启动时调用。

    展开全文
  • 容器进程Core Dump处理

    2019-03-07 17:06:26
    本文主要介绍了Core Dump实现容器进程的方法和相关内容。 上篇文章回顾:IPv6入门教程 引子 在我们调试程序时经常会使用到core dump功能,使用调试器(如gdb)分析其产生的Core Dump文件(以下称"core文件&...
    本文主要介绍了Core Dump实现容器进程的方法和相关内容。
    上篇文章回顾:IPv6入门教程

    引子

    在我们调试程序时经常会使用到core dump功能,使用调试器(如gdb)分析其产生的Core Dump文件(以下称"core文件"),对于排查问题、定位bug堪称无往不利的利器。当前容器技术使用愈加普遍,线上大量业务使用容器技术部署,那我们的业务进程在容器环境下core文件是如何产生、与在宿主机中有什么不同呢?本文针对这个问题简略说明,抛砖引玉。

    什么是Core文件

    Core文件是当一个进程在收到某些信号后终止时产生的文件,其中包含进程终止时刻进程内存的镜像。我们可以使用gdb从该镜像中观察进程终止时处于什么状态,用于追踪排查定位问题。

    如下示例,其中/usr/share/core_pipe/test是crash程序,core.29873就是core文件,其中29873是crash进程的PID。

    # gdb /usr/share/core_pipe/test core.29873GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-gitCopyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from /usr/share/core_pipe/test...done.
    [New LWP 34]
    
    warning: .dynamic section for "/lib64/ld-linux-x86-64.so.2" is not at the expected address (wrong library or version mismatch?)warning: Could not load shared library symbols for /lib64/libc.so.6.
    Do you need "set solib-search-path" or "set sysroot"?
    Core was generated by `./test'.
    Program terminated with signal SIGSEGV, Segmentation fault.
    #0  0x0000556039e756aa in main () at main.c:7
    7     printf("this is null p %d \n", *intp);
    (gdb)

    宿主机进程Core Dump

    在讲容器进程Core Dump之前,我们先来简单回顾下宿主机进程Core Dump在什么条件下产生。这里的宿主机泛指物理机和虚拟主机,宿主机进程特指运行在系统主机Initial Namespace的进程,

    容器进程特指运行在容器Namespace中的进程。

    前文说到当进程收到某种信号才会产生,那么什么才是"某种信号"呢?这些信号大约有十多个,这里只列出我们常见比较熟悉的几个:

    - SIGQUIT  数值2  从键盘输入 Ctrl+'\'可以产生此信号
    
    - SIGILL   数值4  非法指令
    
    - SIGABRT  数值6  abort调用
    
    - SIGSEGV  数值11 非法内存访问
    
    - SIGTRAP  数值5  调试程序时使用的断点

    其中SIGSEGV应该是我们最常接触的,尤其是使用C/C++程序的同学更是常见。

    main.c
    #include <stdio.h>int main(){  int *p = NULL;  
      printf("hello world! \n");
      printf("this will cause core dump p %d", *p);
    }

    使用下面命名编译即可产生运行时触发非法内存访问SIGSEGV信号,其中-g选项是编译添加调试信息,对于gdb调试非常有用。

    # gcc -g main.c -o test

    除了上述产生信号的方法外,使用我们经常使用的kill命令可以非常方便的产生这些信号,另外还有gcore命令可以在不终止进程的前提下产生core文件。

    那么只要进程收到这些信号就一定会有core文件吗?显然不是,linux在这方面提供了相关配置。那么这些配置都有哪些呢?

    1、设置Core文件大小上限

    ulimit
    # ulimit -c 0 // 0表示不产生core文件# ulimit -c 100 // 100表示设置core文件最大为100k,当然可以根据自己需要设置,注意设置单位是KB# ulimit -c unlimited // 不限制core文件大小

    使用上述命令设置core文件大小只对当前shell环境有效,系统重启或者退出当前shell设置就会失效,恢复默认设置。

    若想永久生效可以把该shell放到/etc/profile文件中,系统重新启动初始化环境时会执行其中的命令,该设置就会在全局范围内生效,达到永久生效的效果。也可以使用 source /etc/profile命令立即全局生效。

    # echo "unlimit -c unlimited" >> /etc/profile // 配置添加到/etc/profile中# source /etc/profile // 立即生效

    2、设置Core文件存储目录

    # echo "/var/core-dir/core-%e-%p-%t" >  /proc/sys/kernel/core_pattern

    该命令可以控制core文件保存位置和文件名格式。注意需要使用root权限执行,并且存储路径必须是绝对路径,不能是相对路径

    其中%e表示添加用户程序名称,%p表示添加程序进程PID,%t表示添加产生core文件时的时间戳,还有其他一些非常有用的格式,可以参阅CORE(5) 文档。

    这样修改后系统重启后也会消失,同样也有永久生效的办法

    修改/etc/sysctl.conf文件在其中修改或者添加

    /etc/sysctl.conf
    kernel.core_pattern = /var/core-dir/core-%e-%p-%t

    然后执行下面命令配置立即生效,这样系统重启后配置依然有效

    # sysctl –p /etc/sysctl.conf

    3、宿主机Core文件小结

    宿主机产生core文件可以使用如下步骤

    1. ulimit -c 命令设置core文件大小2. 修改core_pattern选项配置core文件存储目录

    具备上述两个条件后,当进程在一定条件core后,就会存储core文件到我们配置的目录中。

    Core文件产生流程

    大概说一下从进程出现异常到生成core文件的流程,不会涉及太多Linux系统实现细节,具体细节可以参考相关文档和linux内核源码。

    1. 进程运行时发生一个异常,比如非法内存地址访问(即段错误),相应硬件会上报该异常,CPU检测到该异常时会进入异常处理流程,包括保存当前上下文,跳转到对应的中断向量执行入口等

    2. 在异常处理流程中如果判断该异常发生时是处于用户态,则该异常只会影响当前进程,此时向用户态进程发送相应的信号,如段错误就会发送SIGSEGV信号

    3. 当用户态进程被调度时会检查pending的信号,如果发现pending的信号是SIG_KERNEL_COREDUMP_MASK中的一个,就会进入core文件内容收集存储流程,然后根据配置(core_pattern等)生成core文件

    容器进程Core Dump

    那么如果我们要收集在容器里运行的进程的core文件应该如何设置呢?

    答案是上述宿主机针对core文件的设置对容器中的进程依然有效。

    众所周知,宿主机上所有的容器都是共享系统内核的,/proc/sys文件下只有一小部分支持namespace隔离,而core_pattern恰巧不支持隔离的,所以无论是从宿主机还是容器里修改core_pattern,最终修改的是同一个设置,并且全局生效,不管是对宿主机还是对容器都是有效的。

    一般情况下每个容器都有自己的mount namespace,其中的文件系统与宿主机和其他容器相隔离,那么在core_pattern指定的core文件存储目录是容器中的文件目录还是宿主机中呢?不妨推测一二,刚才我们已经说过这个core_pattern是全局生效,如果该目录是针对某个容器的文件目录,那么肯定是不合理的,因为如果宿主机上进程Core Dump时就会找不到对应的目录,无法保存。

    实际上有效的core_pattern中的目录必须是宿主机中的绝对目录,更准确的描述是宿主机Initial Namespace中的绝对路径。

    另外一个问题是,每个容器都有自己pid namespace,我们再core_pattern中设置的获取crash进程的各种信息比如PID,可执行文件名,是容器namespace中的还是宿主机namespace中的呢?从相关文档和实验得知,可以同时获取crash进程在容器中的PID(通过%p格式指定)和在宿主机Initial Namespace中的PID(通过%P格式指定),可执行文件名称(通过%e或%E格式指定)是容器的namespace中的。

    之所以造成上述情况,根本原因是Core Dump流程中内核进程最后负责处理core文件存储的,而内核进程运行在宿主机Initial Namespace中,实际上所有的容器进程在宿主机Initial Namespace都有映射,对内核来讲,宿主机进程和容器进程可以统一处理,并没有本质区别。

    1、使用管道解决容器进程Core Dummp问题

    上文中我们得知了容器进程core文件产生的方法,但是有一个问题就是上述方法的设置是对宿主机和容器内所有的进程都生效的。无法针对特定容器进程特定设置。比如说我们希望宿主机进程core文件保存到/var/crash目录,而对容器的core文件保存在/var/container/crash目录,或者我要限制某个容器产生core文件的总存储大小,而不是单个core文件的大小;如果我们做一个服务平台对其他用户开放Core Dump功能的话,我们肯定还希望获取一下crash进程的其他额外信息比如进程当前环境变量、当前用户、当前进程有效UID和GID、任务名称属性;如果我们希望针对core事件进行统计分析的话,可能还需要各种回调通知等等操作。

    显然上述简单的设置core文件存储目录的方法无法满足我们的需求的,那么我们还有另外一个选择,就是使用linux的piping技术转储core文件。

    从linux内核版本2.6.19之后,内核就开支支持在/proc/sys/kernel/core_pattern文件中指定一个管道程序来实际处理core文件存储。core文件内容会作为该管道程序的标准输入传输给管道程序,管道程序就接管了接下来的core文件内容的所有处理。如下设置可以使用piping技术转储core文件

    # echo "|/usr/share/core_pipe/core_pipe core -H=%h -p=%p -i=%i -s=%s  -c=%c > /proc/sys/kernel/core_pattern
    # cat /proc/sys/kernel/core_pattern
    |/usr/share/core_pipe/core_pipe core -H=%h -p=%p -i=%i -s=%s  -c=%c

    其中/usr/share/core_pipe/core_pipe是我们的管道程序,需要注意的是必须以|开发, |之后必须紧接管道程序的路径,没有空格。当有进程core时,就会调用该管道程序进行处理。

    我们可以开发自己的管道处理程序,从管道程序启动的参数获取crash的进程信息,从管道程序的标准输入获取core文件的内容。

    我们现在知晓该管道程序什么时候被调用(进程Core Dump时),那么管道程序是由谁来调用呢?

    既然管道程序是我们自己开发的,我们就可以获取管道程序的父进程是谁,也就是被谁调用的,通过实验我们可一知道父进程的PID是2,当我们再看该进程的父进程是谁:

    # ps -ef -q 2UID         PID   PPID  C STIME TTY          TIME CMD
    root          2      0  0 Jan20 ?        00:00:00 [kthreadd]

    进程PID2的父进程是PID 0,而PID 0代表的是linux系统内核idle进程,Linux系统中共有三个特殊进程,分别是idle(PID 0), init(PID 1), kthreadd(PID 2),而kthreadd是所有内核进程的父进程,也就是说我们的管道程序是作为内核线程在运行的,运行在内核态,并且在宿主机Initial Namespace中以root用户身份运行,不在任何容器内。

    2、Socket Activation应用到容器进程Core Dummp

    上文说了管道程序运行在内核态,而且是在宿主机的Initial Namespace中运行,容器的各种限制对其不起作用,比如core文件大小有可能超过容器的硬盘空间限制。当然我们管道程序可以通过crash进程的PID拿到crash进程的容器namespace以及各种cgroup限制,然后针对性处理。这样显然对容器极有侵入性,代码写起来也不够优雅。如果处理core文件存储程序在容器中运行,就能较优雅的解决好这个问题。管道程序已经作为内核线程运行在宿主机的Initial Namespace了,虽然有办法可以动态的加入和退出某个namespace和cgroup,但是考虑的边界条件多,易出错,并不优雅。

    如果管道程序能够和容器内某个程序进行交互,可以解决上述问题,同一个宿主机进程通信的方式有很多,比如共享内存,管道,消息队列等。但是这里的两个程序是分布在不同的namespace中,而且彼此并不知道什么时候可以交互,我们为了低概率的core文件长时间让容器内某个进程空跑占用资源吗?那么socket activation技术可以用来解决这个问题。

    socket activation并不是一种新技术,其技术理念和原理早就被应用到Linux和MacOS中,关于socket activation技术原理细节又是需要另一篇的长篇大论,这里暂且不再详述,简单来说,就是由系统init进程(对于目前大多数linux系统来说是systemd)来为普通应用进程监听特定socket,此时应用进程并未启动,当有连接到达该socket后,由init进程接管该连接并跟进配置文件启动相应的应用进程,然后把连接传递给应用进程来处理,主要好处是当没有连接到达时,应用进程无需常驻后台空跑耗费系统资源。非常适合像Core Dump这种低频服务。

    我们可以设置一个unix socket来把管道程序的文件描述符传递到容器内进程,完成传递后, 管道程序就可以退出,由容器内进程处理core文件的存储。

    下面是一个socket activation示例,其中/usr/share/core_pipe/core_pipe是我们的core 文件处理程序, /run/core_pipe.socket是我们unix socket文件,存在容器中,该文件我们在Initial Namespace中的管道程序可以通过/proc/${crash pid}/root/run/core_pipe.socket拿到,然后与之交互。

    core_pipe-forward.socket
    # 此为Unit文件,保存内容为文件到 /etc/systemd/system/core_pipe-forward.socket
    [Unit]
    Description=Unix socket for core_pipe crash forwarding
    ConditionVirtualization=container
    
    [Socket]
    ListenStream=/run/core_pipe.socket
    SocketMode=0600Accept=yes
    MaxConnections=10Backlog=5PassCredentials=true[Install]
    WantedBy=sockets.target
    # 此为service文件,保存内容到 /etc/systemd/system/core_pipe-forward.service
    [Unit]
    Description=Core Pipe crash forwarding receiver
    Requires=core_pipe-forward.socket
    
    [Service]
    Type=oneshot
    ExecStart=/usr/share/core_pipe/core_pipe

    core_pipe-forward.socket

    执行下面命令使得socket生效

    # systemctl enable core_pipe-forward.socket
    # systemctl start core_pipe-forward.socket
    # systemctl status core_pipe-forward.socket

    上述命令如果是在容器内的init进程不是systemd情况下会出错,大多数情况下容器内的init进程并不是systemd,此时可以退一步使用容器内常驻进程的方式来实现core文件的处理。

    总结

    本文简单说明了实现容器进程Core Dump的方法,概况一下主要有三点:

    1. 使用ulimit -c和/proc/sys/kernel/core_pattern设置Core Dump文件大小限制和存储位置

    2. 使用管道程序增强Core Dump文件处理能力

    3. 使用管道程序和容器内进程结合的方式完成内核态转到用户态,在容器内处理Core文件存储

    参考文献:

    CORE(5) http://man7.org/linux/man-pages/man5/core.5.html

    GETRLIMIT(2) http://man7.org/linux/man-pages/man2/setrlimit.2.html

    GDB(1) http://man7.org/linux/man-pages/man1/gdb.1.html

    KILL(2) http://man7.org/linux/man-pages/man2/kill.2.html

    SIGNAL(7) http://man7.org/linux/man-pages/man7/signal.7.html

    NAMESPACE(7) http://man7.org/linux/man-pages/man7/namespaces.7.html

    BASH(1) http://man7.org/linux/man-pages/man1/bash.1.html

    go-systemd https://github.com/coreos/go-systemd

    systemd-socket-activation-in-go https://www.darkcoding.net/software/systemd-socket-activation-in-go/

    Core Dump 流程分析 https://blog.csdn.net/omnispace/article/details/77600721

    Socket activation in systemd http://hustcat.github.io/socket-activation-in-systemd/

    socket activation http://0pointer.de/blog/projects/socket-activation2.html

    文章首发于公众号“小米运维”,点击查看原文

    展开全文
  • 如何获得Docker容器进程ID?

    千次阅读 2020-03-02 22:29:58
    在某些情况下,比如系统负载很高 docker stop 无法关闭某个容器(无响应),这时可以根据容器进程的ID找到宿主机进程ID,然后强制kill掉这个容器,最好已经使用了数据卷保证数据持久化,否则强制关闭容器可能会导致...

    在某些情况下,比如系统负载很高 docker stop 无法关闭某个容器(无响应),这时可以根据容器进程的ID找到宿主机进程ID,然后强制kill掉这个容器,最好已经使用了数据卷保证数据持久化,否则强制关闭容器可能会导致容器内数据丢失(关于数据卷后续专门写一篇文章)。

    操作步骤

    1. 列出当前容器
    docker ps --format '{{.ID}} {{ .Names }}'
    
    CONTAINER ID    NAMES
    1201281cb959    web
    425a6234df74    db
    
    1. 获得容器进程ID
    docker inspect -f '{{ .State.Pid }}' web
    9834
    
    1. 进程信息
    tail /proc/9834/cmdline ;echo 
    nginx: master process nginx -g daemon off;
    
    1. 最后如有必要可以kill这个进程ID,这等于强制杀掉容器进程。
    kill -9 9834
    

    容器进程
    内容摘自 <<Docker — 从入门到实践>> 一书。

    镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

    容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID空间。

    容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。

    小结
    最后来总结下文章中的知识点

    Docker容器本质上是宿主机上的进程。
    容器进程ID就是宿主机进程ID,因为它们是相同的。
    如果Docker守护进程正常响应命令,请不要使用直接kill进程的方法。

    展开全文
  • 本文主要介绍了Core Dump实现容器进程的方法和相关内容。上篇文章回顾:IPv6入门教程引子在我们调试程序时经常会使用到Core Dump(https://en.wikipedia.org/wiki/Core_dump)功能,使用调试器(如gdb...

    271afe6a51ad34cac6a1a56b7398b091.gif

    本文主要介绍了Core Dump实现容器进程的方法和相关内容。

    上篇文章回顾:IPv6入门教程

    引子

    在我们调试程序时经常会使用到Core Dump(https://en.wikipedia.org/wiki/Core_dump)功能,使用调试器(如gdb(https://www.gnu.org/software/gdb/))分析其产生的Core Dump文件(以下称"core文件"),对于排查问题、定位bug堪称无往不利的利器。当前容器技术使用愈加普遍,线上大量业务使用容器技术部署,那我们的业务进程在容器环境下core文件是如何产生、与在宿主机中有什么不同呢?本文针对这个问题简略说明,抛砖引玉。

    什么是Core文件

    Core文件是当一个进程在收到某些信号后终止时产生的文件,其中包含进程终止时刻进程内存的镜像。我们可以使用gdb从该镜像中观察进程终止时处于什么状态,用于追踪排查定位问题。

    如下示例,其中/usr/share/core_pipe/test是crash程序,core.29873就是core文件,其中29873是crash进程的PID。

    # gdb /usr/share/core_pipe/test core.29873GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-gitCopyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later //gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law. Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    //www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at://www.gnu.org/software/gdb/documentation/>.For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from /usr/share/core_pipe/test...done.
    [New LWP 34]
    warning: .dynamic section for "/lib64/ld-linux-x86-64.so.2" is not at the expected address (wrong library or version mismatch?)warning: Could not load shared library symbols for /lib64/libc.so.6.
    Do you need "set solib-search-path" or "set sysroot"?
    Core was generated by `./test'.
    Program terminated with signal SIGSEGV, Segmentation fault.
    #0 0x0000556039e756aa in main () at main.c:7
    7 printf("this is null p %d \n", *intp);
    (gdb)

    宿主机进程Core Dump

    在讲容器进程Core Dump之前,我们先来简单回顾下宿主机进程Core Dump在什么条件下产生。这里的宿主机泛指物理机和虚拟主机,宿主机进程特指运行在系统主机Initial Namespace的进程,

    容器进程特指运行在容器Namespace中的进程。

    前文说到当进程收到某种信号才会产生,那么什么才是"某种信号"呢?这些信号大约有十多个,这里只列出我们常见比较熟悉的几个:

    - SIGQUIT  数值2  从键盘输入 Ctrl+'\'可以产生此信号

    - SIGILL 数值4 非法指令

    - SIGABRT 数值6 abort调用

    - SIGSEGV 数值11 非法内存访问

    - SIGTRAP 数值5 调试程序时使用的断点

    其中SIGSEGV应该是我们最常接触的,尤其是使用C/C++程序的同学更是常见。

    main.c
    #include int main(){ int *p = NULL;
    printf("hello world! \n");
    printf("this will cause core dump p %d", *p);
    }

    使用下面命名编译即可产生运行时触发非法内存访问SIGSEGV信号,其中-g选项是编译添加调试信息,对于gdb调试非常有用。

    # gcc -g main.c -o test

    除了上述产生信号的方法外,使用我们经常使用的kill命令可以非常方便的产生这些信号,另外还有gcore命令可以在不终止进程的前提下产生core文件。

    那么只要进程收到这些信号就一定会有core文件吗?显然不是,linux在这方面提供了相关配置。那么这些配置都有哪些呢?

    1设置Core文件大小上限

    ulimit
    # ulimit -c 0 // 0表示不产生core文件# ulimit -c 100 // 100表示设置core文件最大为100k,当然可以根据自己需要设置,注意设置单位是KB# ulimit -c unlimited // 不限制core文件大小

    使用上述命令设置core文件大小只对当前shell环境有效,系统重启或者退出当前shell设置就会失效,恢复默认设置。

    若想永久生效可以把该shell放到/etc/profile文件中,系统重新启动初始化环境时会执行其中的命令,该设置就会在全局范围内生效,达到永久生效的效果。也可以使用 source /etc/profile命令立即全局生效。

    # echo "unlimit -c unlimited" >> /etc/profile // 配置添加到/etc/profile中# source /etc/profile // 立即生效

    2设置Core文件存储目录

    # echo "/var/core-dir/core-%e-%p-%t" >  /proc/sys/kernel/core_pattern

    该命令可以控制core文件保存位置和文件名格式。注意需要使用root权限执行,并且存储路径必须是绝对路径,不能是相对路径

    其中%e表示添加用户程序名称,%p表示添加程序进程PID,%t表示添加产生core文件时的时间戳,还有其他一些非常有用的格式,可以参阅CORE(5)文档。

    这样修改后系统重启后也会消失,同样也有永久生效的办法

    修改/etc/sysctl.conf文件在其中修改或者添加

    /etc/sysctl.conf
    kernel.core_pattern = /var/core-dir/core-%e-%p-%t

    然后执行下面命令配置立即生效,这样系统重启后配置依然有效

    # sysctl –p /etc/sysctl.conf

    3宿主机Core文件小结

    宿主机产生core文件可以使用如下步骤

    1. ulimit -c 命令设置core文件大小2. 修改core_pattern选项配置core文件存储目录

    具备上述两个条件后,当进程在一定条件core后,就会存储core文件到我们配置的目录中。

    Core文件产生流程

    大概说一下从进程出现异常到生成core文件的流程,不会涉及太多Linux系统实现细节,具体细节可以参考相关文档和linux内核源码。

    1. 进程运行时发生一个异常,比如非法内存地址访问(即段错误),相应硬件会上报该异常,CPU检测到该异常时会进入异常处理流程,包括保存当前上下文,跳转到对应的中断向量执行入口等

    2. 在异常处理流程中如果判断该异常发生时是处于用户态,则该异常只会影响当前进程,此时向用户态进程发送相应的信号,如段错误就会发送SIGSEGV信号

    3. 当用户态进程被调度时会检查pending的信号,如果发现pending的信号是SIG_KERNEL_COREDUMP_MASK中的一个,就会进入core文件内容收集存储流程,然后根据配置(core_pattern等)生成core文件

    容器进程Core Dump

    那么如果我们要收集在容器里运行的进程的core文件应该如何设置呢?

    答案是上述宿主机针对core文件的设置对容器中的进程依然有效。

    众所周知,宿主机上所有的容器都是共享系统内核的,/proc/sys文件下只有一小部分支持namespace隔离,而core_pattern恰巧不支持隔离的,所以无论是从宿主机还是容器里修改core_pattern,最终修改的是同一个设置,并且全局生效,不管是对宿主机还是对容器都是有效的。

    一般情况下每个容器都有自己的mount namespace(http://man7.org/linux/man-pages/man7/mount_namespaces.7.html),其中的文件系统与宿主机和其他容器相隔离,那么在core_pattern指定的core文件存储目录是容器中的文件目录还是宿主机中呢?不妨推测一二,刚才我们已经说过这个core_pattern是全局生效,如果该目录是针对某个容器的文件目录,那么肯定是不合理的,因为如果宿主机上进程Core Dump时就会找不到对应的目录,无法保存。

    实际上有效的core_pattern中的目录必须是宿主机中的绝对目录,更准确的描述是宿主机Initial Namespace中的绝对路径。

    另外一个问题是,每个容器都有自己pid namespace(http://man7.org/linux/man-pages/man7/pid_namespaces.7.html),我们再core_pattern中设置的获取crash进程的各种信息比如PID,可执行文件名,是容器namespace中的还是宿主机namespace中的呢?从相关文档和实验得知,可以同时获取crash进程在容器中的PID(通过%p格式指定)和在宿主机Initial Namespace中的PID(通过%P格式指定),可执行文件名称(通过%e或%E格式指定)是容器的namespace中的。

    之所以造成上述情况,根本原因是Core Dump流程中内核进程最后负责处理core文件存储的,而内核进程运行在宿主机Initial Namespace中,实际上所有的容器进程在宿主机Initial Namespace都有映射,对内核来讲,宿主机进程和容器进程可以统一处理,并没有本质区别。

    1使用管道解决容器进程Core Dummp问题

    上文中我们得知了容器进程core文件产生的方法,但是有一个问题就是上述方法的设置是对宿主机和容器内所有的进程都生效的。无法针对特定容器进程特定设置。比如说我们希望宿主机进程core文件保存到/var/crash目录,而对容器的core文件保存在/var/container/crash目录,或者我要限制某个容器产生core文件的总存储大小,而不是单个core文件的大小;如果我们做一个服务平台对其他用户开放Core Dump功能的话,我们肯定还希望获取一下crash进程的其他额外信息比如进程当前环境变量、当前用户、当前进程有效UID和GID、任务名称属性;如果我们希望针对core事件进行统计分析的话,可能还需要各种回调通知等等操作。

    显然上述简单的设置core文件存储目录的方法无法满足我们的需求的,那么我们还有另外一个选择,就是使用linux的piping技术转储core文件。

    从linux内核版本2.6.19之后,内核就开支支持在/proc/sys/kernel/core_pattern文件中指定一个管道程序来实际处理core文件存储。core文件内容会作为该管道程序的标准输入传输给管道程序,管道程序就接管了接下来的core文件内容的所有处理。如下设置可以使用piping技术转储core文件

    # echo "|/usr/share/core_pipe/core_pipe core -H=%h -p=%p -i=%i -s=%s  -c=%c > /proc/sys/kernel/core_pattern
    # cat /proc/sys/kernel/core_pattern
    |/usr/share/core_pipe/core_pipe core -H=%h -p=%p -i=%i -s=%s -c=%c

    其中/usr/share/core_pipe/core_pipe是我们的管道程序,需要注意的是必须以|开发, |之后必须紧接管道程序的路径,没有空格。当有进程core时,就会调用该管道程序进行处理。

    我们可以开发自己的管道处理程序,从管道程序启动的参数获取crash的进程信息,从管道程序的标准输入获取core文件的内容。

    我们现在知晓该管道程序什么时候被调用(进程Core Dump时),那么管道程序是由谁来调用呢?

    既然管道程序是我们自己开发的,我们就可以获取管道程序的父进程是谁,也就是被谁调用的,通过实验我们可一知道父进程的PID是2,当我们再看该进程的父进程是谁:

    # ps -ef -q 2UID         PID   PPID  C STIME TTY          TIME CMD
    root 2 0 0 Jan20 ? 00:00:00 [kthreadd]

    进程PID2的父进程是PID 0,而PID 0代表的是linux系统内核idle进程,Linux系统中共有三个特殊进程,分别是idle(PID 0), init(PID 1), kthreadd(PID 2),而kthreadd是所有内核进程的父进程,也就是说我们的管道程序是作为内核线程在运行的,运行在内核态,并且在宿主机Initial Namespace中以root用户身份运行,不在任何容器内。

    2Socket Activation应用到容器进程Core Dummp

    上文说了管道程序运行在内核态,而且是在宿主机的Initial Namespace中运行,容器的各种限制对其不起作用,比如core文件大小有可能超过容器的硬盘空间限制。当然我们管道程序可以通过crash进程的PID拿到crash进程的容器namespace以及各种cgroup限制,然后针对性处理。这样显然对容器极有侵入性,代码写起来也不够优雅。如果处理core文件存储程序在容器中运行,就能较优雅的解决好这个问题。管道程序已经作为内核线程运行在宿主机的Initial Namespace了,虽然有办法可以动态的加入和退出某个namespace和cgroup,但是考虑的边界条件多,易出错,并不优雅。 

    如果管道程序能够和容器内某个程序进行交互,可以解决上述问题,同一个宿主机进程通信的方式有很多,比如共享内存,管道,消息队列等。但是这里的两个程序是分布在不同的namespace中,而且彼此并不知道什么时候可以交互,我们为了低概率的core文件长时间让容器内某个进程空跑占用资源吗?那么socket activation技术可以用来解决这个问题。

    socket activation并不是一种新技术,其技术理念和原理早就被应用到Linux和MacOS中,关于socket activation技术原理细节又是需要另一篇的长篇大论,这里暂且不再详述,简单来说,就是由系统init进程(对于目前大多数linux系统来说是systemd)来为普通应用进程监听特定socket,此时应用进程并未启动,当有连接到达该socket后,由init进程接管该连接并跟进配置文件启动相应的应用进程,然后把连接传递给应用进程来处理,主要好处是当没有连接到达时,应用进程无需常驻后台空跑耗费系统资源。非常适合像Core Dump这种低频服务。

    我们可以设置一个unix socket来把管道程序的文件描述符传递到容器内进程,完成传递后, 管道程序就可以退出,由容器内进程处理core文件的存储。

    下面是一个socket activation示例,其中/usr/share/core_pipe/core_pipe是我们的core 文件处理程序, /run/core_pipe.socket是我们unix socket文件,存在容器中,该文件我们在Initial Namespace中的管道程序可以通过/proc/${crash pid}/root/run/core_pipe.socket拿到,然后与之交互。

    core_pipe-forward.socket
    # 此为Unit文件,保存内容为文件到 /etc/systemd/system/core_pipe-forward.socket
    [Unit]
    Description=Unix socket for core_pipe crash forwarding
    ConditionVirtualization=container

    [Socket]
    ListenStream=/run/core_pipe.socket
    SocketMode=0600Accept=yes
    MaxConnections=10Backlog=5PassCredentials=true[Install]
    WantedBy=sockets.target
    # 此为service文件,保存内容到 /etc/systemd/system/core_pipe-forward.service
    [Unit]
    Description=Core Pipe crash forwarding receiver
    Requires=core_pipe-forward.socket

    [Service]
    Type=oneshot
    ExecStart=/usr/share/core_pipe/core_pipe

    core_pipe-forward.socket

    执行下面命令使得socket生效

    # systemctl enable core_pipe-forward.socket
    # systemctl start core_pipe-forward.socket
    # systemctl status core_pipe-forward.socket

    上述命令如果是在容器内的init进程不是systemd情况下会出错,大多数情况下容器内的init进程并不是systemd,此时可以退一步使用容器内常驻进程的方式来实现core文件的处理。

    总结

    本文简单说明了实现容器进程Core Dump的方法,概况一下主要有三点:

    1. 使用ulimit -c和/proc/sys/kernel/core_pattern设置Core Dump文件大小限制和存储位置

    2. 使用管道程序增强Core Dump文件处理能力

    3. 使用管道程序和容器内进程结合的方式完成内核态转到用户态,在容器内处理Core文件存储

    参考文献:

    CORE(5) http://man7.org/linux/man-pages/man5/core.5.html

    GETRLIMIT(2)http://man7.org/linux/man-pages/man2/setrlimit.2.html

    GDB(1)http://man7.org/linux/man-pages/man1/gdb.1.html

    KILL(2)http://man7.org/linux/man-pages/man2/kill.2.html

    SIGNAL(7)http://man7.org/linux/man-pages/man7/signal.7.html

    NAMESPACE(7)http://man7.org/linux/man-pages/man7/namespaces.7.html

    BASH(1) http://man7.org/linux/man-pages/man1/bash.1.html

    go-systemd  https://github.com/coreos/go-systemd

    systemd-socket-activation-in go 

    https://www.darkcoding.net/software/systemd-socket-activation-in-go/

    Core Dump流程分析

    https://blog.csdn.net/omnispace/article/details/77600721

    Socket activation in systemd 

    http://hustcat.github.io/socket-activation-in-systemd/

    socket activation 

    http://0pointer.de/blog/projects/socket-activation2.html

    792bcb0339f8515602d3bf7811d9ee1d.gifEND

    2ebde639e150cb7b2e4d7b62d0a8eddc.png

    好文!点个赞吧
    展开全文
  • 本文主要介绍了Core Dump实现容器进程的方法和相关内容。上篇文章回顾:IPv6入门教程引子在我们调试程序时经常会使用到Core Dump(https://en.wikipedia.org/wiki/Core_dump)功能,使用调试器(如gdb...
  • Docker查看容器进程

    千次阅读 2020-02-02 12:19:39
    docker top 容器ID 例如: 1、docker ps 2、docker top 4599c7。。。
  • docker 删除容器进程

    千次阅读 2019-07-12 14:23:24
    查看现有 [root@lch software]# docker ps -a 删除 [root@lch software]# docker rm -f e7bf15dfb241 d313d836f552 再次查看
  • Docker查看容器进程 docker top 容器ID Docker 查看容器日志docker logs $ docker logs [OPTIONS] CONTAINER Options: --details 显示更多的信息 -f, --follow 跟踪实时日志 --since string 显示自...
  • docker 容器进程被kill的原因查看

    千次阅读 2019-12-03 14:52:53
    最近的docker容器经常被kill掉,k8s中该节点的pod也被驱赶,因而使用以下命令查看被kill的所有进程 dmesg | grep -i -B100 'killed process' 查看到pod被驱赶的原因: [3899860.525793] Out of memory: Kill ...
  • 如果启动了Docker容器,比如这样: 1 docker run -itd -p 3000:3000 --name my-web -v "$(pwd)":/webapp -w /webapp node npm start 我启动了一...
  • 1.docker进程管理  docker的进程管理命令ps的用法基本和ubuntu系统的用法一致 1.1. 查看docker进程  sudo docker ps –a   1.2. 附着到容器上  Sudo docker attach be7743e86306   1.3. 取消附着到...
  • 默认情况下,容器是没有资源限制的,它会尽可能地使用宿主机能够分配给它的资源。 linux系统内存资源不足的时候就会抛出OOM,之后会开始随机释放用户进程资源,很可能会造成系统崩溃。(提供足够的
  • 转载自http://www.open-open.com/lib/view/open1455412749917.html
  • 问题描述 进入容器后,无法在容器内查看进程启动情况,那能在容器外...也就是说,如果在容器内主进程属于用户uid=1000,那么这个容器进程在宿主机器上也属于用户uid=1000。容器内的用户uid=1000就是容器外的用户uid=...
  • 进程容器

    2015-04-13 08:53:11
    进程是准虚拟化的虚拟机—容器虚拟化的原理  by 沈东良
  • 容器进程

    2020-10-14 10:39:27
    首先,大家可能会有个疑问,容器,到底是怎么一回事儿? 我们说容器其实是一种沙盒技术。顾名思义,沙盒就是能够像一个集装箱一样,把你的应用“装”起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互...
  • 容器技术发展进程

    2021-04-29 16:50:28
    第一章:容器技术发展进程 文章目录 系列文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 前言 容器已成为现在软件的基础架构 一、容器技术发展进程 时间 容器...
  • 最近看了这篇文章:Understanding how uid and gid work in Docker containers,了解到: docker容器内的一个进程对应于宿主机器上的一...也就是说,如果在容器内主进程属于用户uid=1000,那么这个容器进程在宿主...
  • 容器基础-进程

    2019-05-21 23:02:44
    进程:程序被执行时,它就从磁盘上的二进制文件,变成了计算机内存中的数据、寄存器里的值、堆栈中的指令、被打开的文件,...容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”...
  • 理解Docker容器进程管理摘要:Docker在进程管理上有一些特殊之处,如果不注意这些细节中的魔鬼就会带来一些隐患。另外Docker鼓励“一个容器一个进程(one process per container)”的方式。这种方式非常适合以单...
  • 查看docker容器运行进程信息

    千次阅读 2019-04-15 22:02:10
    查看docker容器运行进程信息: [root@fuqiang ~]# docker top grafana UID PID PPID C STIME TTY TIME CMD...
  • 一、关于Docker容器里的进程的前台运行 Docker 不是虚拟机,容器就是进程。 Docker 启动的是进程,因此所谓的后台服务应该放到前台,一个docker容器同时只能管理一个进程,Docker容器仅在它的1号进程(PID为1)...
  • docker 容器 defunct 僵尸进程

    千次阅读 2020-02-01 17:38:48
    在构建 Docker 容器时,我们需要注意子进程的“僵尸化”问题(PID 1 Zombie Reaping Problem)。这会导致不可预知的和看起来匪夷所思的问题。本文解释了这个问题,也解释了如何去解决,并提供了一种预构建方案:Base...
  • 进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。 当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。 孤儿进程 父...
  • docker容器启动之后会有相应的Linux进程容器进程是怎么样的对应关系?一个容器一个进程吗,还是一个容器多个进程容器进程 docker ps 查看所有已经启动的容器,从第一列找到一个[容器ID] ,下一步备用; ps ...
  • 如何关闭docker容器里的进程 1、使用docker exec 容器名 ps -ef命令查看进程信息 示例: 创建名为"redis"的容器,并在容器内部和宿主机中查看容器中的进程信息: 2、然后进入该容器中,执行如下命令即可停掉进程: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,505
精华内容 4,202
关键字:

容器进程