标签 程序员 下的文章

专访稿:兴趣才是第一生产力

本文是公司“运营拍档”公众号的专访文稿,这里转载一下^0^。原文链接在这里

十年生死两茫茫,白天忙,晚上忙,写程序,到天亮。千行代码,Bug何处藏。纵使上线又怎样,朝令改,夕断肠……


img{512x368}

白 明

东软云科技架构师
2017年Gopher China大会讲师
《七周七语言》译者之一
拥有10年编程工作经验
多年电信领域产品研发和技术管理经验
目前主要研究领域包括:
Go、Kubernetes、Docker和儿童编程教育等

一直以来,大家对程序员的固有印象是什么?刻板低调游戏宅?钱多话少无情趣?还是格子衬衫双肩包?无论哪种印象,都是对这个群体最偏激的总结,就像没见过海,总以为海是蔚蓝的。

一百位程序员有一百种样子,如果非要给程序员群体划分出派系,那白大师非禁欲系莫属。

img{512x368}

话少有内涵,明明帅到颠倒众生,却高冷禁欲不屑一顾

Go语言 – 未来企业级软件领域第一语言

就在上周,2017年Gopher China大会结束后,我们趁机“抓”住了被大会邀请担任讲师的白大师。希望能从这位颜值高、品味高、气质高的“三高”程序员身上,了解一些Gopher China这个“神秘组织”的信息。

img{512x368}

Q:上周被邀请作为2017年Gopher China大会的讲师,能向我们简单介绍一下Gopher China的江湖地位吗?

A:Gopher China是中国大陆地区规模最大,也是最具影响力的Go语言技术大会。这个大会从2015年开始举办,今年已经是第三届了,其影响力已经扩展到了港澳台和东南亚地区。今年在大会上就有一些来自宝岛台湾、中国香港以及东南亚地区的Gopher。Gopher在Go圈里专指Go程序员,因为Go语言的吉祥物是Gopher(地鼠)。

img{512x368}
 
另外,关于Go语言这门开源编程语言未来的发展,白大师也给出来了自己的看法。

“Go语言在中国大陆受欢迎程度非常高,甚至超出了全球平均水平。目前很火热的区块链技术的底层基础框架和平台很多也是由Go语言实现的,比如:以太坊。个人认为Go语言在未来发展前景一片光明,更有取代Java语言成为企业级软件领域第一语言的势头。”

兴趣才是第一生产力

像汤姆汉克斯为表演而生,恰克·帕拉尼克为写作而生一样,白大师为代码而生。他们这群人是天生的创造者,具有强烈的好奇心,且能把兴趣转化成终生事业。

img{512x368}

Q:是 什么原因让你决定从事编程工作?

A: “兴趣。”

Q:您觉得对于一位软件工程师来说,最重要的特质是什么?

A:“热爱编程。”

在外行人看起来枯燥无味的代码,对他们来说也是满屏的成就感。

“编程工作是为数不多的创造性智力劳动,未来世界编程将会变成普通公民的基本技能和能力,就像现在的语文、数学、物理、化学一样。”

都说“知识改变人生”,但现实往往是“兴趣改变人生”。如果说动力是一个人坚持下去的力量,那兴趣就是为动力提供能量的永动机。而白大师的“编程十年”就是源于最初的兴趣以及兴趣产生的无限能量。

代码外的世界

一位优秀的人之所以能称为优秀,最重要的原因就是你能从他身上不断寻找到惊喜。

img{512x368}

惊喜不断的白大师

当我以为白大师用了十年的时间钻研编程,沉浸在代码的世界里,他告诉我他也用了十年的时间写博客,现在仍孜孜不倦。

当我以为他只是用文字记录生活、消磨时间,他却告诉我自己也翻译过一本编程类的书,叫《七周七语言》。

img{512x368}

当我以为这些经历已足够填满他这十年的时间,他说自己还是个“绝对梅吹”。喜欢阿根廷队,关注阿根廷球星,尤其在2005年看到了天赋异禀的梅西后,就一直膜拜至今。

img{512x368}

白大师和他的那些经典理论

当然,白大师带给我的惊喜,远远不止于他的爱好,还有一些“经典理论”。

比如“年轻心态产生论”。

Q:“为什么会选择加入运营拍档技术团队呢?哪些地方吸引了您?“

A:“因为运营拍档技术团队所在的部门女同学平均年龄最低、平均颜值最高。在这样的环境下工作,心态都会变得年轻。这才是年轻心态产生的根本原因。
(说好的高冷禁欲系呢?)

比如,“挑战收获论”。

Q:“您之前参与翻译过《七周七语言》,为什么会去做这项工作?这样的经历给您带来哪些收获?“

A:“参与翻译工作,主要是想挑战一下自己。收获自然是有的,最大的收获是让我认识到翻译书这事儿真的很难,投入产出比很低。“

其实,兴趣不只是对事物表面的关心,任何一种兴趣都是由于获得这方面的知识或成就感使人的体验在情绪上得到满足而循环产生的。很多时候我们觉得,一旦将兴趣变成工作,它最单纯的本质就变了样。可事实上,白大师用自己的经历向我们证明了:兴趣,才是第一生产力。

img{512x368}


微博:@tonybai_cn
微信公众号:iamtonybai
github.com: https://github.com/bigwhite

探讨Docker容器中修改系统变量的方法

探讨完Docker对共享内存状态持久化的支持状况后,我将遗留产品build到一个pre-production image中,测试启动是否OK。很显然,我过于乐观了,Docker之路并不平坦。我收到了shmget报出的EINVAL错误码,提示参数非法。 shmget的manual对EINVAL错误码的说明如下:

EINVAL:
A  new  segment  was  to  be  created  and size < SHMMIN or size > SHMMAX, or no new segment was to be created, a segment with given key existed, but size is greater than the size of that segment.

显然我们要创建的shared memory的size很可能大于SHMMAX这个系统变量了。那么一个从base image创建出的容器中的系统变量到底是什么值呢?我们来查看一下,我们基于"centos:centos6"启动一个Docker容器,并检查其中的 系统变量值设置:

$ sudo docker run -it "centos:centos6" /bin/bash
bash-4.1# cat /proc/sys/kernel/shmmax
33554432
bash-4.1# sysctl -a|grep shmmax
kernel.shmmax = 33554432

可以看出默认情况下,当前容器中root账号看到的shmmax值我33554432, 我的程序要创建的shm size的确要大于这个值,报出EINVAL错误也就无可厚非了。我尝试按照物理机上的方法临时修改一下该值:

bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax
bash: /proc/sys/kernel/shmmax: Read-only file system

/proc/sys/kernel/shmmax居然是只读的,无法修改。

我又尝试修改/etc/sysctl.conf这个持久化系统变量的地方,但打开/etc/sysctl.conf文件,我发现我又错了,这 个文件中shmmax的值如下:

# Controls the maximum shared segment size, in bytes
kernel.shmmax = 68719476736

/etc/sysctl.conf文件 中的系统变量shmmax的值是68719476736,而系统当前的实际值则是33554432,难道是/etc /sysctl.conf中的值没有生效,于是我手工重新加载一次该文件:

-bash-4.1# sysctl -p
error: "Read-only file system" setting key "net.ipv4.ip_forward"
error: "Read-only file system" setting key "net.ipv4.conf.default.rp_filter"
error: "Read-only file system" setting key "net.ipv4.conf.default.accept_source_route"
error: "Read-only file system" setting key "kernel.sysrq"
error: "Read-only file system" setting key "kernel.core_uses_pid"
error: "net.ipv4.tcp_syncookies" is an unknown key
error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key
error: "net.bridge.bridge-nf-call-iptables" is an unknown key
error: "net.bridge.bridge-nf-call-arptables" is an unknown key
error: "Read-only file system" setting key "kernel.msgmnb"
error: "Read-only file system" setting key "kernel.msgmax"
error: "Read-only file system" setting key "kernel.shmmax"
error: "Read-only file system" setting key "kernel.shmall"

我得到了和之前类似的错误结果:只读文件系统,无法修改。于是乎两个问题萦绕在我的面前:
1、为什么容器内当前系统变量值与sysctl.conf中的不一致?
2、为什么无法修改当前系统变量值?

在翻阅了Stackoverflow, github docker issues后,我得到了的答案如下:

1、Docker的base image做的很精简,甚至都没有init进程,原本在OS启动时执行生效系统变量的过程(sysctl -p)也给省略了,导致这些系统变量依旧保留着kernel默认值。以CentOs为例,在linux kernel boot后,init都会执行/etc/rc.d/rc.sysinit,后者会加载/etc/sysctl.conf中的系统变量值。下面是 CentOs5.6中的rc.sysinit代码摘录:

… …
# Configure kernel parameters
update_boot_stage RCkernelparam
sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
… …

2、Docker容器中的系统变量在non-priviledged模式下目前(我使用的时docker 1.2.0版本)就无法修改,这 和resolv.conf、hosts等文件映射到宿主机对应的文件有不同。

$ mount -l
…. ….
/dev/mapper/ubuntu–Server–14–vg-root on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/ubuntu–Server–14–vg-root on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/ubuntu–Server–14–vg-root on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)
… …

那么我们该如何修改系统变量值来满足遗留产品的需求呢?

一、使用–privileged选项

我们使用–privileged这个特权选项来启动一个基于centos:centos6的新容器,看看是否能对shmmax这样的系统变量值 进行修改:

$ sudo docker run -it –privileged  "centos:centos6" /bin/bash
bash-4.1# cat /proc/sys/kernel/shmmax
33554432
bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax
bash-4.1# cat /proc/sys/kernel/shmmax
68719476736

bash-4.1# sysctl -p
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
… …
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296

可以看出,通过–privileged选项,容器获得了额外的特权,并且可以对系统变量的值进行修改了。不过这样的修改是不能保存在容器里的, 我们stop 容器,再重启该容器就能看出来:

$ sudo docker start 3e22d65a7845
$ sudo docker attach 3e22d65a7845
bash-4.1# cat /proc/sys/kernel/shmmax
33554432

shmmax的值在容器重启后又变回了原先的那个默认值。不过重启后的容器依旧具有privileged的特权,我们还可以重新手工执行命令对系 统变量进行修改:

bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax
bash-4.1# cat /proc/sys/kernel/shmmax
68719476736

但即便这样,也无法满足我们的需求,我们总不能每次都在容器中手工执行系统变量值修改的操作吧。privileged选项的能力能否带到 image中呢?答案是目前还不能,我们无法在build image时通过privileged选项修改系统变量值。

这样一来,我们能做的只有把产品启动与系统变量值修改放在一个脚本中了,并将该脚本作为docker 容器的cmd命令来执行,比如我们构建一个Dockerfile:

FROM centos:centos6
MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
RUN yum install python-setuptools -y
RUN easy_install supervisor
RUN mkdir -p /var/log/supervisor
COPY ./supervisord.conf /etc/supervisord.conf
COPY ./start.sh /bin/start.sh
RUN chmod +x /bin/start.sh
CMD ["/bin/start.sh]

//start.sh
sysctl -p
/usr/bin/supervisord

这样,start.sh在supervisord启动前将系统变量值重新加载,而supervisord后续启动的程序就可以看到这些新系统变量 的值了。不过别忘了利用这个image启动容器时要加上–priviledged选项,否则容器启动就会失败。

二、使用phusion/baseimage

前面说过/etc/sysctl.conf中的值没有生效是因为docker官方提供的centos:centos6把init进程的初始化过程给精 简掉了。phusion/baseimage是目前docker registery上仅次于ubuntu和centos两个之后的base image,其提供了/sbin/my_init这个init进程,用于在container充当init进程的角色。那么my_init是否可以用于执行sysctl -p呢?我们试验一下:

我们先pull这个base image下来:sudo docker pull phusion/baseimage。pull成功后,我们先基于“phusion/baseimage”启动一个容器做一些explore工作:

$ sudo docker run -i -t "phusion/baseimage"
*** Running /etc/my_init.d/00_regen_ssh_host_keys.sh…
No SSH host key available. Generating one…
Creating SSH2 RSA key; this may take some time …
Creating SSH2 DSA key; this may take some time …
Creating SSH2 ECDSA key; this may take some time …
Creating SSH2 ED25519 key; this may take some time …
invoke-rc.d: policy-rc.d denied execution of restart.
*** Running /etc/rc.local…
*** Booting runit daemon…
*** Runit started as PID 100

通过nsenter进去,查看一下/sbin/my_init的源码,我们发现这是一个python脚本,不过从头到尾浏览一遍,没有发现sysctl加载/etc/sysctl.conf系统变量的操作。

不过,phusion文档中说my_init可以在初始化过程中执行/etc/my_init.d下的脚本。那是不是我们将一个执行sysctl -p的脚本放入/etc/my_init.d下就可以实现我们的目的了呢?试试。

我们编写一个脚本:load_sys_varibles.sh

#!/bin/sh
sysctl -p > init.txt

下面是制作image的Dockerfile:

FROM phusion/baseimage:latest
MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
RUN echo "kernel.shmmax = 68719476736" >> /etc/sysctl.conf
RUN mkdir -p /etc/my_init.d
ADD load_sys_varibles.sh /etc/my_init.d/load_sys_varibles.sh
RUN chmod +x /etc/my_init.d/load_sys_varibles.sh
CMD ["/sbin/my_init"]

phusion/baseimage是基于ubuntu的OS,其sysctl.conf默认情况下没啥内容,所以我们在Dockerfile中向这个文件写入我们需要的系统变量值。构建image并启动容器:

$ sudo docker build -t "myphusion:v1" ./
Sending build context to Docker daemon 13.12 MB
Sending build context to Docker daemon
Step 0 : FROM phusion/baseimage:latest
 —> cf39b476aeec
Step 1 : MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
 —> Using cache
 —> d0e9b51a3e4f
Step 2 : RUN echo "kernel.shmmax = 68719476736" >> /etc/sysctl.conf
 —> Using cache
 —> 2c800687cc83
Step 3 : RUN mkdir -p /etc/my_init.d
 —> Using cache
 —> fe366eea5eb4
Step 4 : ADD load_sys_varibles.sh /etc/my_init.d/load_sys_varibles.sh
 —> a641bb595fb9
Removing intermediate container c381b9f001c2
Step 5 : RUN chmod +x /etc/my_init.d/load_sys_varibles.sh
 —> Running in 764866552f25
 —> eae3d7f1eac5
Removing intermediate container 764866552f25
Step 6 : CMD ["/sbin/my_init"]
 —> Running in 9ab8d0b717a7
 —> 8be4e7b6b174
Removing intermediate container 9ab8d0b717a7
Successfully built 8be4e7b6b174

$ sudo docker run -it "myphusion:v1"
*** Running /etc/my_init.d/00_regen_ssh_host_keys.sh…
No SSH host key available. Generating one…
Creating SSH2 RSA key; this may take some time …
Creating SSH2 DSA key; this may take some time …
Creating SSH2 ECDSA key; this may take some time …
Creating SSH2 ED25519 key; this may take some time …
invoke-rc.d: policy-rc.d denied execution of restart.
*** Running /etc/my_init.d/load_sys_varibles.sh…
sysctl: setting key "kernel.shmmax": Read-only file system
*** /etc/my_init.d/load_sys_varibles.sh failed with status 255

*** Killing all processes…

唉,还是老问题!即便是在my_init中执行,依旧无法逾越Read-only file system,查看Phusion/baseimage的Dockerfile才知道,它也是From ubuntu:14.04的,根不变,上层再怎么折腾也没用。

换一种容器run方法吧,加上–privileged:

$ sudo docker run -it –privileged  "myphusion:v1"
*** Running /etc/my_init.d/00_regen_ssh_host_keys.sh…
No SSH host key available. Generating one…
Creating SSH2 RSA key; this may take some time …
Creating SSH2 DSA key; this may take some time …
Creating SSH2 ECDSA key; this may take some time …
Creating SSH2 ED25519 key; this may take some time …
invoke-rc.d: policy-rc.d denied execution of restart.
*** Running /etc/my_init.d/load_sys_varibles.sh…
*** Running /etc/rc.local…
*** Booting runit daemon…
*** Runit started as PID 102

这回灵光了。enter到容器里看看设置的值是否生效了:

root@9e399f46372a:~#cat /proc/sys/kernel/shmmax
68719476736

结果如预期。这样来看phusion/baseimage算是为sysctl -p加载系统变量值提供了一个便利,但依旧无法脱离–privileged,且依旧无法在image中持久化这个设置。

在Docker github的issue中有人提出建议在Dockerfile中加入类似RUNP这样的带有特权的指令语法,但不知何时才能在Docker中加入这一功能。

总而言之,基于目前docker官网提供的base image,我们很难找到特别理想的修改系统变量值的方法,除非自己制作base image,这个还没尝试过,待后续继续研究。

探讨docker容器对共享内存的支持情况

我们的遗留系统广泛使用了性能最佳的IPC方式 – 共享内存,而且用到了两种共享内存的实现方式:System V共享内存(shmget、shmat、shmdt)以及Mmap映射Regular File。System V共享内存支持一定程度上的内存数据持久化,即当程序创建共享内存对象后,如果不显式删除或物理主机重启,该IPC对象会一直保留,其中的数据也不会丢 失;mmap映射Regular File的方式支持内存数据持久化到文件中,即便物理主机重启,这部分数据依旧不会丢失,除非显式删除文件。这两个共享内存机制,尤其是其持久化的特性是 我们的系统所依赖的。但是在Docker容器中,这两种共享内存机制依旧能被很好的支持吗?我们通过试验来分析一下。

一、System V共享内存

一个启动的Docker容器就是一个拥有了自己的内核名字空间的进程,其pid、net、ipc、mnt、uts、user等均与其他进程隔离,对于运行于该容器内的程序而言,它仿佛会觉得它独占了一台“主机”。对于这类“主机”,我们首先来测试一下其中的system v共享内存是否依旧能像物理主机上一样,在程序退出后依旧能保持持久化?在容器退出后能保持么?

我们先来写两个测试程序,一个用于创建system v共享内存,并写入一些数据,另外一个程序则映射该共享内存并尝试读出内存中的数据。由于Golang目前仍未提供对System V共享内存的高级封装接口,通过syscall包的Syscall调用又太繁琐,因此我们直接使用C代码与Go代码结合的方式实现这两个测试程序。之前写 过一篇名为《Go与C语言互操作》的博文,看不懂下面代码的朋友,可以先阅读一下这篇文章。

//systemv_shm_wr.go
package main

//#include <sys/types.h>
//#include <sys/ipc.h>
//#include <sys/shm.h>
//#include <stdio.h>
//
//#define SHMSZ     27
//
//int shm_wr() {
//    char c;
//    int shmid;
//    key_t key;
//    char *shm, *s;
//
//    key = 5678;
//
//    if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
//        return -1;
//    }
//
//    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
//        return -2;
//    }
//
//    s = shm;
//    for (c = 'a'; c <= 'z'; c++)
//        *s++ = c;
//    s = NULL;
//
//    return 0;
//}
import "C"

import "fmt"

func main() {
        i := C.shm_wr()
        if i != 0 {
                fmt.Println("SystemV Share Memory Create and Write Error:", i)
                return
        }
        fmt.Println("SystemV Share Memory Create and Write Ok")
}

//systemv_shm_rd.go

package main

//#include <sys/types.h>
//#include <sys/ipc.h>
//#include <sys/shm.h>
//#include <stdio.h>
//
//#define SHMSZ     27
//
//int shm_rd() {
//    char c;
//    int shmid;
//    key_t key;
//    char *shm, *s;
//
//    key = 5678;
//
//    if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
//        return -1;
//    }
//
//    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
//        return -2;
//    }
//
//    s = shm;
//
//    int i = 0;
//    for (i = 0; i < SHMSZ-1; i++)
//        printf("%c ", *(s+i));
//    printf("\n");
//    s = NULL;
//
//    return 0;
//}
import "C"

import "fmt"

import "fmt"

func main() {
        i := C.shm_rd()
        if i != 0 {
                fmt.Println("SystemV Share Memory Create and Read Error:", i)
                return
        }
        fmt.Println("SystemV Share Memory Create and Read Ok")
}

我们通过go build构建上面两个程序,得到两个测试用可执行程序:systemv_shm_wr和systemv_shm_rd。下面我们来构建我们的测试用docker image,Dockerfile内容如下:

FROM centos:centos6
MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
COPY ./systemv_shm_wr /bin/
COPY ./systemv_shm_rd /bin/

构建Docker image:“shmemtest:v1”:

$ sudo docker build -t="shmemtest:v1" ./
Sending build context to Docker daemon 16.81 MB
Sending build context to Docker daemon
Step 0 : FROM centos:centos6
 —> 68edf809afe7
Step 1 : MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
 —> Using cache
 —> c617b456934a
Step 2 : COPY ./systemv_shm_wr /bin/
 —> ea59fb767573
Removing intermediate container 4ce91720897b
Step 3 : COPY ./systemv_shm_rd /bin/
 —> 1ceb207b1009
Removing intermediate container 7ace7ad53a3f
Successfully built 1ceb207b1009

启动一个基于该image的容器:
$ sudo docker run -it "shmemtest:v1" /bin/bash

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
0a2f37bee6eb        shmemtest:v1        "/bin/bash"         28 seconds ago      Up 28 seconds                           elegant_hawking

进入容器,先后执行systemv_shm_wr和systemv_shm_rd,我们得到如下结果:

bash-4.1# systemv_shm_wr
SystemV Share Memory Create and Write Ok
bash-4.1# systemv_shm_rd
a b c d e f g h i j k l m n o p q r s t u v w x y z
SystemV Share Memory Create and Read Ok

在容器运行过程中,SystemV共享内存对象是可以持久化的。systemv_shm_wr退出后,数据依旧得以保留。我们接下来尝试一下重启container后是否还能读出数据:

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
0a2f37bee6eb        shmemtest:v1        "/bin/bash"         8 minutes ago       Up 8 minutes                            elegant_hawking    
$ sudo docker stop 0a2f37bee6eb
0a2f37bee6eb
$ sudo docker start 0a2f37bee6eb
0a2f37bee6eb
$ sudo docker attach 0a2f37bee6eb
bash-4.1# systemv_shm_rd
SystemV Share Memory Create and Read Error: -1

程序返回-1,显然在shmget时就出错了,系统已经没有了key为"5678"的这个共享内存IPC对象了。也就是说当容器stop时,就好比我们的物理主机关机,docker将该容器对应的共享内存IPC对象删除了。

从原理上分析,似乎我们也能得出此结论:毕竟Docker container是通过kernel namespace隔离的,容器中的进程在IPC资源申请时需要加入namespace信息。打个比方,如果我们启动容器的进程pid(物理主机视角)是 1234,那么这容器内进程申请的共享内存IPC资源(比如key=5678)的标识应该类似于“1234:5678”这样的形式。重启容器 后,Docker Daemon无法给该容器分配与上次启动相同的pid,因此pid发生了变化,之前容器中的"1234:5678"保留下来也是毫无意义的,还无端占用系 统资源。因此,System V IPC在Docker容器中的运用与物理机有不同,这方面要小心,目前似乎没有很好的方法,也许以后Docker会加入全局IPC,这个我们只能等待。

二、Mmap映射共享内存

接下来我们探讨mmap共享内存在容器中的支持情况。mmap常见的有两类共享内存映射方式,一种映射到/dev/zero,另外一种则是映射到 Regular Fiile。前者在程序退出后数据自动释放,后者则保留在映射的文件中。后者对我们更有意义,这次测试的也是后者。

同样,我们也先来编写两个测试程序。

//mmap_shm_wr.go
package main

//#include <stdio.h>
//#include <sys/types.h>
//#include <sys/mman.h>
//#include <fcntl.h>
//
//#define SHMSZ     27
//
//int shm_wr()
//{
//      char c;
//      char *shm = NULL;
//      char *s = NULL;
//      int fd;
//      if ((fd = open("./shm.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) == -1)  {
//              return -1;
//      }
//
//      lseek(fd, 500, SEEK_CUR);
//      write(fd, "\0", 1);
//      lseek(fd, 0, SEEK_SET);
//
//      shm = (char*)mmap(shm, SHMSZ, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
//      if (!shm) {
//              return -2;
//
//      }
//
//      close(fd);
//      s = shm;
//      for (c = 'a'; c <= 'z'; c++) {
//              *(s+(int)(c – 'a')) = c;
//      }
//      return 0;
//}
import "C"

import "fmt"

func main() {
        i := C.shm_wr()
        if i != 0 {
                fmt.Println("Mmap Share Memory Create and Write Error:", i)
                return
        }
        fmt.Println("Mmap Share Memory Create and Write Ok")
}

//mmap_shm_rd.go
package main

//#include <stdio.h>
//#include <sys/types.h>
//#include <sys/mman.h>
//#include <fcntl.h>
//
//#define SHMSZ     27
//
//int shm_rd()
//{
//      char c;
//      char *shm = NULL;
//      char *s = NULL;
//      int fd;
//      if ((fd = open("./shm.txt", O_RDONLY)) == -1)  {
//              return -1;
//      }
//
//      shm = (char*)mmap(shm, SHMSZ, PROT_READ, MAP_SHARED, fd, 0);
//      if (!shm) {
//              return -2;
//      }
//
//      close(fd);
//      s = shm;
//      int i = 0;
//      for (i = 0; i < SHMSZ – 1; i++) {
//              printf("%c ", *(s + i));
//      }
//      printf("\n");
//
//      return 0;
//}
import "C"

import "fmt"

func main() {
        i := C.shm_rd()
        if i != 0 {
                fmt.Println("Mmap Share Memory Read Error:", i)
                return
        }
        fmt.Println("Mmap Share Memory Read Ok")
}

我们通过go build构建上面两个程序,得到两个测试用可执行程序:mmap_shm_wr和mmap_shm_rd。下面我们来构建我们的测试用docker image,Dockerfile内容如下:

FROM centos:centos6
MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
COPY ./mmap_shm_wr /bin/
COPY ./mmap_shm_rd /bin/

构建Docker image:“shmemtest:v2”:

$ sudo docker build -t="shmemtest:v2" ./
Sending build context to Docker daemon 16.81 MB
Sending build context to Docker daemon
Step 0 : FROM centos:centos6
 —> 68edf809afe7
Step 1 : MAINTAINER Tony Bai <bigwhite.cn@gmail.com>
 —> Using cache
 —> c617b456934a
Step 2 : COPY ./mmap_shm_wr /bin/
 —> Using cache
 —> 01e2f6bc7606
Step 3 : COPY ./mmap_shm_rd /bin/
 —> 0de95503c851
Removing intermediate container 0c472e92809f
Successfully built 0de95503c851

启动一个基于该image的容器:
$ sudo docker run -it "shmemtest:v2" /bin/bash

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1182f9eca367        shmemtest:v2        "/bin/bash"         11 seconds ago      Up 11 seconds                           distracted_elion

进入容器,先后执行mmap_shm_wr和mmap_shm_rd,我们得到如下结果:

bash-4.1# mmap_shm_wr
Mmap Share Memory Create and Write Ok
bash-4.1# mmap_shm_rd
a b c d e f g h i j k l m n o p q r s t u v w x y z
Mmap Share Memory Read Ok

我们接下来尝试一下重启container后是否还能读出数据:

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
1182f9eca367        shmemtest:v2        "/bin/bash"         About a minute ago   Up About a minute                       distracted_elion   
$ sudo docker stop 1182f9eca367
1182f9eca367
$ sudo docker start 1182f9eca367
1182f9eca367
$ sudo docker attach 1182f9eca36
7

bash-4.1# mmap_shm_rd
a b c d e f g h i j k l m n o p q r s t u v w x y z
Mmap Share Memory Read Ok

通过执行结果可以看出,通过mmap映射文件方式,共享内存的数据即便在容器重启后依旧可以得到保留。从原理上看,shm.txt是容器内 的一个文件,该文件存储在容器的可写文件系统layer中,从物理主机上看,其位置在/var/lib/docker/aufs/mnt /container_full_id/下,即便容器重启,该文件也不会被删除,而是作为容器文件系统的一部分:

$ sudo docker inspect -f '{{.Id}}' 1182f9eca367
1182f9eca36756219537f9a1c7cd1b62c6439930cc54bc69e87915c5dc8f7b97
$ sudo ls /var/lib/docker/aufs/mnt/1182f9eca36756219537f9a1c7cd1b62c6439930cc54bc69e87915c5dc8f7b97
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  sbin    selinux  shm.txt  srv  sys  tmp  usr  var




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多