Docker镜像分层技术

简介

采用的sha256对每层编码。对image进行编码。

Docker对于镜像的维护类似于git对于repository的维护,都是只记录增量的。原有镜像是静态文件,基于这个静态文件可以创建的一个动态容器,在这个动态的容器中做任何你希望做的修改,然后退出容器后使用commit生成新的镜像,这个新的镜像就保留了你做的改动,从而生成新的一层。 未改变的文件还是使用原有镜像的文件, 被改动的文件作为新的layer被保存。基于同一个base image构建的两个不同的镜像,就类似于 基于同一个repository创建的两个不同的branch, 未修改的文件由两个镜像公用,每个镜像又独自保留他们特定的修改内容的增量。

从命令行上来说:

  • 一个commit指令对应一个新的layer
  • 一条RUN语句对应一个新的layer

Ubuntu系统的镜像

参考

-

Docker镜像的构建

如何创建docker镜像

直接pull

从Docker Hub下拉

现在docker官方共有仓库里面有大量的镜像,所以最基础的镜像,我们可以在共有仓库直接拉取,因为这些镜像都是原厂维护,可以得到即使的更新和修护。

Dockerfile:

我们如果想去定制这些镜像,我们可以去编写Dockerfile,然后重新bulid,最后把它打包成一个镜像,这种方式是最为推荐的方式包括我们以后去企业当中去实践应用的时候也是推荐这种方式。

Commit

当然还有另外一种方式,就是通过镜像启动一个容器,然后进行操作,最终通过commit这个命令commit一个镜像,但是不推荐这种方式,虽然说通过commit这个命令像是操作虚拟机的模式,但是容器毕竟是容器,它不是虚拟机,所以大家还是要去适应用Dockerfile去定制这些镜像这种习惯。

镜像的概念主要就是把把运行环境和业务代码进行镜像的打包,我们这个课重点是了解镜像的分层技术,我们先来看一个Ubuntu系统的镜像。

build images

从文件进行build

1
docker build -f Dockerfile . -t bitspeech/tensor2tensor:1.9.0-gpu

构建小容量Docker镜像的技巧

  • 使用较小的基础镜像
    • centos:7的大小就比ubuntu:14.04要大,而ubuntu:14.04比debian:jessie要大。
  • 安装完成后进行清理
  • apt 安装,编译,remove 要一气呵成。docker 镜像是 git 的机制,安装和移除必须写在一行。

你可以通过移除软件包和清理垃圾文件来减小镜像的大小。例如使用apt包管理器的系统可以通过apt-get purgeapt-get autoremove,及apt-get clean命令移除软件包,使用rm -rf /var/lib/apt/lists/*rm -rf /tmp清除一些临时文件。
这里需要注意的是,因为Docker的层次系统,Dockerfile中的每一个RUN命令都会创建一个新的copy-on-write层,从而增加了最终镜像的大小,即使是通过RUN指令运行移除文件的命令。所以上面的这些命令如果都单独地以Dockerfile中的RUN指令执行,最终的镜像大小不会减小,反而会增加。这就需要我们下面的对Dockerfile的进一步改进。

1.系统级镜像:如Ubuntu镜像,CentOS镜像以及Debian容器等;

2.工具栈镜像:如Golang镜像,Flask镜像,Tomcat镜像等;

3.服务级镜像:如MySQL镜像,MongoDB镜像,RabbitMQ镜像等;

4.应用级镜像:如WordPress镜像,DockerRegistry镜像等。

参考

图像分类--模型汇总

image classification

ImageNet分类模型

model 简介 conv_padding conv_activation pooling code 备注
LeNet5 3个卷积层+2个pooling层 valid sigmoid
AlexNet 8层 same ReLU Overlapping Pooling
VGG-19 19层 same ReLU keras
GoogLeNet 22层
ResNet 152层 keras

padding=same,是采用zero-padding的方式,使得input和output的维度一致。

上图来自cs231n,

计算机视觉--常用数据库

summary

dataset im_size class*num download task example code pretrained_model state of art 实例图片 备注
mnist 28*28 70000
CIFAR-10 32x32x3 10*6000 d Alex,Hinton发布,超小图片
CIFAR-100 32x32 170M 100*600
Pascal VOC (05-12) 2GB voc2012
coco 40GB
imagenet2012 尺寸不固定,但多数比较清晰 1000类,训练集1.2m,验证集50k,测试集100k 层级标签。 评价指标,top5的label包含正确label就算正确 AlexNet
imagenet2016 1400多万幅图片,涵盖2万多个类别 1TB
places
12306 约80*80 12306图片比cifar数据库大多了

分类结果汇总

task_code:

  1. 图像分类(image classification)
  2. 目标检测(object detection)
  3. 目标识别(object recognition)
  4. 语义分割(semantic segmentation)
  5. 实例分割(instance segmentation)

来自pytorch vision的data

来自tensorflow的data

##

java系列 - Error & Exception

definition

  • catch an error at compile time: before you even try to run the program (think in java)
    -

    java framework

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Throwable─┬─Exception─┬─RuntimeException─┬─IndexOutOfBoundsException
│ │ ├─NullPointerException
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ └─
│ │
│ ├─ ReflectiveOperationException─┬─NoSuchMethodException
│ │ ├─ClassNotFoundException
│ │ └─IllegalAccessException
│ │
│ │
│ │
│ ├─IOException─┬─FileNotFoundException
│ │ ├─EOFException

└─Error─┬─
RuntimeException non-RuntimeException
checked ×
occur in runtime runtime
must catch ×
caused by programmer,such as NullPointerException, IndexOutOfBoundsException external, such as FileNotFoundException
thrown during the normal operation of JVM

Compile-Time Checking of Exceptions

设计思想

RuntimeException
一般情况下,不要捕获或声明RuntimeException。因为问题在于你的程序逻辑本身有问题,
如果你用异常流程处理了,反而让正常流程问题一直存在。
程序应该从逻辑角度尽可能避免这类异常的发生;

non-RuntimeException
RuntimeException之外的异常我们统称为非运行时异常。
从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。

非运行时异常,编译期要做语法检查,即检查是否处理了catch。程序报Exception还是要运行期才能知道的。

An Exception is checked, and a RuntimeException is unchecked.

  • A checked exception must be handled explicitly by the code (catch or throw)
  • An un-checked exception does not need to be explicitly handled.

ambiguous, common errors, common misconceptions

Is non-RuntimeException CompiletimeException in java framework?
No, non-RuntimeException would be checked in compile time. But the Exception occurs in run time.

where did RuntimeException comes from

since JDK1.0

new/reasonable framework

In my view, the RuntimeException should be renamed as CheckedException

  • RuntimeException
    • CheckedException
    • UncheckedException
  • CompiletimeException
    • ..

reference

  • 《The Java™ Language Specification》11.2 Compile-Time Checking of Exceptions

other framework

  1. Some programming languages and their implementations react to such errors
    by peremptorily terminating the program; other programming languages allow an
    implementation to react in an arbitrary or unpredictable way. Neither of these
    approaches is compatible with the design goals of the Java SE platform: to provide
    portability and robustness.

Instead, the Java programming language specifies that an exception will be thrown
when semantic constraints are violated and will cause a non-local transfer of control
from the point where the exception occurred to a point that can be specified by the
programmer.

2.

python framework

reference: http://www.cnblogs.com/rubylouvre/archive/2011/06/22/2086644.html

c++ framework

【java源码系列】 - String

ss

String s = “abc”,并没有在堆上生成对象
Object o = new Object() 对应字节码为:

0: new           #2                  // class java/lang/Object
3: dup           
4: invokespecial #1                  // Method java/lang/Object."<init>":()V
7: astore_1   

String s = “abc” 对应字节码为:

0: ldc           #2                  // String abc
2: astore_1  

String s = new String() 对应字节码为:

0: new           #2                  // class java/lang/String
3: dup           
4: invokespecial #3                  // Method java/lang/String."<init>":()V
7: astore_1  

java系列 - class文件

introduction

目的:完整分析class文件。

java源码

package MyTest;

public class SimpleClass {
    void dfd() {
        int i;
        for (i = 0; i < 100; i++) {
        ; // Loop body is empty
        }
    }
}

class文件 16进制

编译得到MyTest.class

cafe babe  | 0000   0033      000c                   
magic num  | minor0  major51  常量池计数器11+1=12  

//////////// 常量池 /////////////

#1  07     0002
    class  #2

#2    01     0012         4d79 5465 7374 2f53 696d 706c 6543 6c61 7373  
    utf8   length=18    MyTest/SimpleClass                            |  |

#3    07     0004  
    class  #4           // java/lang/Object   

#4    01     0010         6a61 7661 2f6c 616e 672f 4f62 6a65 6374 |
    utf8   lengh16      java/lang/Object                        |

#5    01     0006         3c 696e 6974 3e     
    utf8   length=6     <init>

#6    01     0003         2829 56
    utf8   length=3     ()V

#7    01     0004         436f 6465
    utf8   length=3     Code

#8    0a     000300       09
    md_ref #3.          #9  //  java/lang/Object."<init>":()V

#9    0c     0005         0006
    nam&ty #5           #6   //  "<init>":()V

#10    01     0003         64 6664
    utf8   length=3     dfd

#11    01     000d         53 7461 636b 4d61 7054 6162 6c65
    utf8   length=13    StackMapTable

//////////// 常量池 /////////////

0021               0001         0003
可能是public       #1 类索引    #3 父类索引

// 0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER为真,其中ACC_PUBLIC大家好理解,ACC_SUPER是jdk1.2之后编译的类都会带有的标志。

0000            
接口计数器      接口表

0000       
fields_count

0002
methods_count

//////////////// 第一个method void <init>  ///////////

0001     0005       0006
public   #5<init>   #6()V      其值为()V,表示<init>方法没有参数和返回值, 其实这是编译器自动生成 的实例构造器方法

0001            
attrib_count    

0007        00000011     0001       0001        00000005     2ab70008b1  0000                    0000
#7Code属性  attrib_len   max_stack  max_locals  code_length  code[]      exception_table_length  attribute_count
// 代码是存储在Class文件中的method的code属性的code[]数组中



//////////////// 第二个method dfd()  ///////////

0000              000a     0006
没写access_flag   #10,dfd  ()V

0001
attrib_count

0007        00000028       0002       0002        0000000f          033ca700068401011b1064a1fffab1  
#7Code属性  属性表的长度   max_stack  max_locals  code_length=15    code[],解析见《jvm spec》41页

0000
exception_table_length

0001
attribute_count

000b                  00000007   0002              fc00 0501 0200 00
#11:StackMapTable     length     num_of_entries    7个length的stack_map_frame

疑问

.和:是怎么区分的?

其他阅读

http://coolshell.cn/articles/9229.html

【java源码系列】- Thread

疑问

Java的线程是如何创建的,是直接调用OS的API,还是有自己的“抽象线程”?

java线程是映射到操作系统的内核线程上的

跟踪Thread.start()

java

// Thread.java
public synchronized void start() {
    group.add(this);
    ...
    start0();
    ...
}

private native void start0();

native方法hotspot源码

// openjdk\jdk\src\share\native\java\lang\Thread.c
static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
   ...
};


// openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;
  ...
  native_thread = new JavaThread(&thread_entry, sz); // 重点
  ...
  Thread::start(native_thread); // 重点
JVM_END



// openjdk\hotspot\src\share\vm\runtime\thread.cpp
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread() {
   ...
   os::create_thread(this, thr_type, stack_sz);
   // 可以看出java线程是映射到操作系统的内核线程上的
   ...
}

void Thread::start(Thread* thread) {
    ...
    os::start_thread(thread);
  }
}


// 在hotspot\src\os目录下可以看到windows, linux, solaris和posix的实现,先检查linux\vm\os_linux.cpp

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  ...
  int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);  // linux中调用的pthread,POSIX中的api
  ...
}

【java源码系列】Atomic

简介

  • 原子量和普通变量相比,主要体现在读写的线程安全上。对原子量的是原子的(比如多线程下的共享变量i++就不是原子的),由CAS操作保证原子性。对原子量的读可以读到最新值,由volatile关键字来保证可见性。
  • 原子量多用于数据统计(如接口调用次数)、一些序列生成(多线程环境下)以及一些同步数据结构中
  • atomic是基于底层硬件的CAS做的
  • 区别于HashTable等线程安全类,这里面没有锁

以AtomicLong为例

java源码

// 1. from AtomicLong.java (rt.jar:java.util.concurrent.atomic.AtomicLong)
public final boolean compareAndSet(long expect, long update) {
    return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

// 2. from Unsafe.java (rt.jar:sun.misc.Unsafe)
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);

native方法的C++实现

// 3. from hotspot/src/share/vm/prims/unsafe.cpp (openjdk)
static JNINativeMethod methods[] = {
{CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z",      FN_PTR(Unsafe_CompareAndSwapLong)},
...
}
...
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  UnsafeWrapper("Unsafe_CompareAndSwapLong");
  Handle p (THREAD, JNIHandles::resolve(obj));
  jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  if (VM_Version::supports_cx8())  
    return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;  // 主要实现
  else { // 如果不支持cx8,那么就需要用到ObjectLocker锁
    jboolean success = false;
    ObjectLocker ol(p, THREAD);
    if (*addr == e) { *addr = x; success = true; }
    return success;
  }
UNSAFE_END

It is a JNI wrapper for the CAS API, with memory barriers for IA64 architecture.

It is an atomic operation which means no other processor can change the value of dest whilst the operation executes.

Atomic::cmpxchg(x, addr, e)
CAS需要三个参数 address,old_value, new_value.
modern CPU is required for this process.

CAS has its weakness as ABA problem, so memory barrier is necessary here to ensure the CAS is still correct in multithread environment.

  • native的实现

Atomic::cmpxchg方法的分支

依赖于OS & CPU & 32bit/64bit. 所以JVM在这里产生了分支

// 4. from  hotspot/src/share/vm/runtime/atomic.cpp
# include "atomic_windows_x86.inline.hpp"
# include "atomic_linux_x86.inline.hpp"     // x86生产商有Intel, AMD, IBM等
# include "atomic_solaris_sparc.inline.hpp" // Solaris系统,sparc处理器(都是sun的)
# include "atomic_linux_sparc.inline.hpp"   // linux系统,sparc处理器
...

linux_x86中:

// from openjdk/hotspot/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
#ifdef AMD64

inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  bool mp = os::is_MP();
  __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
                        : "=a" (exchange_value)
                        : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                        : "cc", "memory");
  return exchange_value;
}

#else // !AMD64
inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  return _Atomic_cmpxchg_long(exchange_value, dest, compare_value, os::is_MP());
}
#endif // AMD64

windows_x86中

// from openjdk\hotspot\src\os_cpu\windows_x86\vm
#ifdef AMD64
inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  return (*os::atomic_cmpxchg_long_func)(exchange_value, dest, compare_value);
}

#else // !AMD64
inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  int mp = os::is_MP();
  jint ex_lo  = (jint)exchange_value;
  jint ex_hi  = *( ((jint*)&exchange_value) + 1 );
  jint cmp_lo = (jint)compare_value;
  jint cmp_hi = *( ((jint*)&compare_value) + 1 );
  __asm {
    push ebx
    push edi
    mov eax, cmp_lo
    mov edx, cmp_hi
    mov edi, dest
    mov ebx, ex_lo
    mov ecx, ex_hi
    LOCK_IF_MP(mp)
    cmpxchg8b qword ptr [edi]
    pop edi
    pop ebx
  }
}
#endif // AMD64

其他平台…

可以看出,当CPU支持时,最终确实是直接用cmpxchg相关指令实现的。

ObjectLocker锁

这是synchronized锁吗?

Why would you use a CAS function?

名词解释

  • Solaris:原是太阳微系统公司研制的类Unix操作系统,在Sun公司被Oracle并购后,称作Oracle Solaris。早期的Solaris主要用于Sun工作站上

  • saparc: sun公司开发的处理器,用于Sun工作站等上。Solaris在SPARC上拥有强大的处理能力和硬件支持(相对Intel x86平台)

  • cx8:

  • ObjectLocker:

疑问

  • i = i + 1不是原子操作吗?为什么AtomicLong.incrementAndGet()的实现要这么复杂?直接用i = i + 1实现不行吗?(见《java并发编程实战》18页)

  • #include <atomic> C++自带的实现与atomic_linux_x86.inline.hpp有什么区别?应该前者是后者的进一步封装吧?

  • 这个native方法的实现为什么在JVM层而不在jdk层?JVM是用来run byte code的。这里的JVM代码是用来run哪个byte code呢?

  • 这里生成的byte code是啥样的?

再挖掘

参考