500行代码代码手写docker-将rootfs设置为只读镜像

(3)500行代码代码手写docker-将rootfs设置为只读镜像

本系列教程主要是为了弄清楚容器化的原理,纸上得来终觉浅,绝知此事要躬行,理论始终不及动手实践来的深刻,所以这个系列会用go语言实现一个类似docker的容器化功能,最终能够容器化的运行一个进程。

本章的源码已经上传到github,地址如下:

http://github.com/HobbyBear/tinydocker/tree/chapter3

前文提到,如果仅仅将ubuntu-base-16.04.6-base-amd64 目录作为容器的根目录, 那么当运行多个容器,就会同时修改到ubuntu-base-16.04.6-base-amd64目录,这样将达不到不同容器使用不同的根文件系统的目的。

所以这节我将会演示如何运行内核提供到联合文件系统的功能,来达到一份镜像,多次运行的目的。

这节代码运行效果:

image.png

可以看到我其实启动了两个容器 hello1 ,hello2 然后在hello1 下创建test目录,但是test目录在hello2容器里是不可见的。

联合文件系统原理

首先,来先简单的看看联合文件系统的概念。

??? 联合文件系统可以把其他文件系统的文件和目录挂载到同一个挂载点下,形成统一的文件系统,在挂载点下形成统一的文件视图

在linux内核里,自带了一种叫做overlay类型的文件系统类型,它是一种联合文件系统,类似的还有aufs,不过本文还是用overlay 类型进行举例。

如下是一个挂载overlay 文件系统的mount命令

sudo mount -t overlay overlay -o lowerdir=image-layer1:image-layer2,upperdir=container-layer,workdir=work mnt/

其中contailber-layer 后续会作为容器的读写层,image-layer会作为镜像层,mnt作为overlay联合文件系统的挂载目录,而work后续会作为overlay联合文件系统的工作目录,这个目录是overlay自己用的,对用户不可见。挂载目录为mnt。

也就是说后续进程可以统一访问mnt目录就能看到image-layer 和contailber-layer 这两个目录的内容,但是对mnt目录进行修改的话,则只会将修改体现在contailber-layer这个目录下,image-layer这个目录永远不会变。

关于联合文件系统更详细的解释和命令演示可以参考之前我的一篇博文容器镜像原理- 联合文件系统实践

如何用go代码实现

接着,我们来看看如何对前文的代码进行改造。

已经知道了,当挂载一个overlay文件系统时,镜像层的文件是永远不会变的,所以ubuntu-base-16.04.6-base-amd64这个roofs目录毫无疑问将会作为镜像层进行参数传递,而我们还需要为容器创建其自身的可写层和工作层目录。因为可以运行多个容器,如何区分这些容器各自的可写层呢?最简单的方法就是拥有一个容器名,通过容器名创建属于他们自己的目录。

所以,现在运行命令的方式变了,之前我们是这样运行一个容器:

./tinydocker run /bin/sh

现在将变成这样

./tinydocker run 容器名 /bin/sh

先统一浏览下目前main方法中的代码

func main() {

	switch os.Args[1] {
	case "run":
		initCmd, err := os.Readlink("/proc/self/exe")
		if err != nil {
			fmt.Println("get init process error ", err)
			return
		}
		// 获取容器名
		containerName := os.Args[2]
		os.Args[1] = "init"
		cmd := exec.Command(initCmd, os.Args[1:]...)
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
				syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
		}
		cmd.Env = os.Environ()
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			fmt.Println(err)
		}
		// 容器结束后要清理掉它的挂载点和目录
		workspace.DelMntNamespace(containerName)
		return
	case "init":
		var (
			containerName = os.Args[2]
			cmd           = os.Args[3]
		)
		// 创建挂载点和更换rootfs
		if err := workspace.SetMntNamespace(containerName); err != nil {
			fmt.Println(err)
			return
		}
		syscall.Chdir("/")
		defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
		syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
		err := syscall.Exec(cmd, os.Args[3:], os.Environ())
		if err != nil {
			fmt.Println("exec proc fail ", err)
			return
		}
		fmt.Println("forever exec it ")
		return
	default:
		fmt.Println("not valid cmd")
	}
}

可以看到,在以新命名空间启动一个子进程后,在workspace.SetMntNamespace 里将会进行相关目录的挂载,然后在执行cmd.Run 的父进程中,等待子进程结束后,调用了workspace.DelMntNamespace清理了子进程的挂载点和相关目录。

而workspace.SetMntNamespace 的源码如下:

func SetMntNamespace(containerName string) error {
	if err := os.MkdirAll(mntLayer(containerName), 0700); err != nil {
		return fmt.Errorf("mkdir mntlayer fail err=%s", err)
	}
	if err := os.MkdirAll(workerLayer(containerName), 0700); err != nil {
		return fmt.Errorf("mkdir work layer fail err=%s", err)
	}
	if err := os.MkdirAll(writeLayer(containerName), 0700); err != nil {
		return fmt.Errorf("mkdir write layer fail err=%s", err)
	}

	if err := syscall.Mount("overlay", mntLayer(containerName), "overlay", 0,
		fmt.Sprintf("upperdir=%s,lowerdir=%s,workdir=%s",
			writeLayer(containerName), imagePath, workerLayer(containerName))); err != nil {
		return fmt.Errorf("mount overlay fail err=%s", err)
	}

	if err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("reclare rootfs private fail err=%s", err)
	}

	if err := syscall.Mount(mntLayer(containerName), mntLayer(containerName), "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mount rootfs in new mnt space fail err=%s", err)
	}
	if err := os.MkdirAll(mntOldLayer(containerName), 0700); err != nil {
		return fmt.Errorf("mkdir mnt old layer fail err=%s", err)
	}
	if err := syscall.PivotRoot(mntLayer(containerName), mntOldLayer(containerName)); err != nil {
		return fmt.Errorf("pivot root  fail err=%s", err)
	}
	return nil
}

workspace.SetMntNamespace 先是根据容器名创建了执行overlay挂载所需要的目录,然后通过mount命令讲一个overlay类型的文件系统挂载到mntLayer(containerName)的路径下,然后mntLayer(containerName)路径下文件将作为容器的根文件系统,后续会对其进行pivot root调用,然后mntLayer(containerName)的目录将会成为新的mnt namespace的根目录了。

这样,不同容器名的容器将会有自己独立的根目录。即避免了镜像层文件的改变,又达到了各容器文件系统隔离的目的。

本文转载于网络 如有侵权请联系删除

相关文章

  • Building Android notifications 2.3 through to 6.0

    Sadly,Notification.setLatestEventInfo()isremovedinAPILevel23….ThebaseclassNotificationwasintroducedinAPIlevel1,fromtheverybeginning.Backthenyouwouldcreateyournotificationsbycreatinganewinstanceviatheconstructorandthensetthevariouspropertiesonit. IfyouwantedtodisplayfurtherdetailsonthenotificationyouwouldcallsetLatestEventInfo(Contextcontext,CharSequencecontentTitle,CharSequencecontentText,PendingIntentcontentIntent)复制InAPIlevel11aka“Honeycomb”AndroidintroducedtheNotification.BuilderAPIwhichcompl

  • [C语言] #、##、__VA_ARGS__的使用

    从网上借鉴了一些内容,然后整理了一下三者的区别,记录一下。#include<iostream> //#:用来把参数转换成字符串 #defineLOG1(x)do{printf("%s=%d\n",#x,x);}while(0) //##:用于将带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串;但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元; #defineLOG2(x)log##x() //__VA_ARGS__:用于在宏替换部分中,表示可变参数列表; #defineLOG(...)printf(__VA_ARGS__); voidlogA(){ printf("logfuncA\n"); } voidlogB(){ printf("logfuncB\n"); } intmain() { //测试# intscore=96; LOG1(score); LOG1(6); //测试## LOG2(A); //测试__VA_ARGS__ L

  • 从崩溃的选课系统,论为什么更安全的 HTTPS 协议没有被全面采用

    0.前言上一篇文章详细讲解了HTTP的相关原理,我们已经了解到了HTTP具有非常优秀和方便的一面,然而,HTTP并非一个安全的协议。大家平常浏览网页的时候应该也能注意到,使用HTTP协议的网站,浏览器都会认定这是一个不安全的网站,提醒用户注意防范(即便这是我们学校的选课系统)。当然,这个不安全的含义指的是你在该网页输入的信息可能会被外界攻击者以非正常手段窃取,而不是说会被这个网页的开发者获取,毕竟浏览器咋能判断这个网页开发者是否存有异心,这个需要用户自行判断(手动滑稽?)那么为了克服HTTP的缺点,确保Web的安全,HTTPS就应用而生了。可以看见,使用HTTPS协议的网页,地址栏前面都会有把小锁,表示这是一个加密安全的网站,你的信息发送到此站点时是保密的。而且Google、Baidu等搜索引擎巨头对于HTTPS网站都会给到更好的搜索排名。本文会先解释HTTP为什么是不安全的,然后讲解HTTPS为了保证Web的安全提供了哪些手段,最后再揭晓谜底,为什么更安全的HTTPS协议在互联网上没有被全面采用。1.不安全的HTTP通过上面那张图我们已经知道,对于使用HTTP的网站,浏览器就会提醒我

  • 特斯拉割了燃油车最后的稻草,国产Model 3降至最低25万元,「无钴」电池或巧立功

    新智元报道编辑:梦佳【新智元导读】等等党的胜利!全国人民欢度双节的第一天,特斯拉官网显示,国产Model3起步价降至24.99万元。此外,更新后的Model3标准续航升级版续航里程还获得了20公里以上的提升。性价比可谓是踩了燃油车的底线。特斯拉又双叒叕降价了。最低不到25万!「无钴」电池或巧立功十一国庆,全国人民欢度双节的第一天,特斯拉官网显示,中国制造Model3标准续航升级版补贴后售价为249900元,中国制造Model3长续航后轮驱动版补贴后售价309900元。据称,特斯拉同时在其标准版续航的Model3中引入了无钴磷酸铁锂电池。在之前曝光的工信部新车信息中,特斯拉Model3将搭载磷酸铁锂电池,该电池由宁德时代提供。磷酸铁锂电池较三元电池,当前能量密度是无法企及,但价格却会低40%左右。CEO马斯克最近提到,他相信磷酸铁锂电池的能量密度已经有了足够的提高,现在可以在低端车型中使用这种更便宜的无钴电池。钴是电动汽车电池中使用的最昂贵的金属之一。特斯拉降价早已经数见不鲜,上一次调价还是今年的5月1日。当时中国制造的Model3标准续航升级版补贴前售价由原来的32.38万元降至29.

  • 谷歌提出多语言BERT模型:可为109种语言生成与语言无关的跨语言句子嵌入

    新智元报道来源:Google编辑:雅新【新智元导读】谷歌研究人员提出了一种LaBSE的多语言BERT嵌入模型。该模型可为109种语言生成与语言无关的跨语言句子嵌入,同时在跨语言文本检索性能优于LASER。近日,谷歌AI研究人员提出了一种称为LaBSE的多语言BERT嵌入模型,该模型可为109种语言生成与语言无关的跨语言句子嵌入。这一论文题目为「Language-agnosticBERTSentenceEmbedding」,目前已在arxiv上发表。论文地址:https://arxiv.org/pdf/2007.01852.pdf研究背景 多语言嵌入模型是一种功能强大的工具,可将不同语言的文本编码到共享的嵌入空间中,从而使其能够应用在一系列下游任务,比如文本分类、文本聚类等,同时它还利用语义信息来理解语言。用于生成此类嵌入的现有方法如LASER或m~USE依赖并行数据,将句子从一种语言直接映射到另一种语言,鼓励句子嵌入之间的一致性。尽管这些现有的多语言嵌入方法可在多种语言中有良好的整体性能,但与专用双语模型相比,它们在高资源语言上通常表现不佳。此外,由于有限的模型容量、低资源语言的训练数

  • tf.config

    目录一、模块和函数二、experimental模块1、tf.config.experimental.set_visible_devices2、tf.config.experimental.list_physical_devices3、tf.config.experimental.set_memory_growth4、tf.config.experimental.list_logical_devices一、模块和函数Modules:experimentalmodule:PublicAPIfortf.config.experimentalnamespace.optimizermodule:PublicAPIfortf.config.optimizernamespace.threadingmodule:PublicAPIfortf.config.threadingnamespace.Functions:experimental_connect_to_cluster(...):Connectstothegivencluster.experimental_connect_to_host(...)

  • Java的面试基础题(一)

    什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”Java虚拟机是一个可执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性JDK和JRE的区别是什么Java运行时环境(JRE)是将要执行Java程序的Java虚拟机。它同时也包含了执行applet需要的浏览器插件。 Java开发工具包(JDK)是完整的Java软件开发包,包含了JRE,编译器和其他的工具(比如:JavaDoc,Java调试器),可以让开发者开发、编译、执行Java应用程序。”static”关键字是什么意思?static表示“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类的特定的实例,被类的所有实例共享。这样这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到他们。因此,s

  • Bash shell中四种算术运算方式的区别与联系简介

    版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢 在bashshell中,整数的运算也会偶尔遇到。为了彻底弄清其中的奥秘,我们通过实例验证的方式来探索一下常见的四种运算中的区别与联系。实例验证之前,我们来定义一组变量,并对其赋值:[root@GeekDevOps~]#a=9;b=13;c=-1;d=+4;e=7.2 [root@GeekDevOps~]#echo$a$b$c$d$e 913-1+47.2复制一、expr[root@GeekDevOps~]#echo`expr$a+$b` 22 [root@GeekDevOps~]#echo`expr$c+$d` expr:非整数参数 [root@GeekDevOps~]#echo`expr$c+$b` 12 [root@GeekDevOps~]#echo`expr$a+$d` expr:非整数参数 [root@GeekDevOps~]#echo`expr$a+$e` expr:非整数参数复制从以上例子中我们可以看出,使用expr来进行数字运算时,不支持浮点类型的数字计算,也不支持带符号

  • 浅谈Linux磁盘修复e2fsck命令

    linux下磁盘检查修复命令e2fsck -a:检查partition,如发现问题会自动修复。-b:设定superblock位置。-Bsize:指定size作为区块大小。-c:检查partition是否有坏轨。-Cfile:将检查结果储存到file。-d:输出e2fsckdebug结果。-f:e2fsck预设只会对错误的档案系统检查,加上-f是强制检查。-F:在检查前将硬盘的buffercache清空,避免发生错误。-llist:记录了坏轨区块加入list中。-d:打印e2fsck的debug结果。-f:强制检查。-n:以(read-only)开启档案系统-p:关闭互动模式,如有问题自动修复,等同-a。-v:显示详细报告。-y:启用使用者互动模式。使用例子检查/dev/mapper/VolGroup00-LogVol02是否有问题,如发现问题便自动修复:e2fsck-a/dev/mapper/VolGroup00-LogVol02执行e2fsck或fsck前请先umountpartition,否则有机会令档案系统毁损。分区忙的情况,需要将所有涉及该分区的进程杀掉,有个快速的方法是执行f

  • WordPress 百度熊掌号自动推送插件安装使用教程

    百度熊掌号是百度推出的新平台,把你网站的原创内容在最短时间内展现到百度搜索结果中,有助于提高百度搜索排名和权重,也就提高了网站流量。按照百度要求,接入百度熊掌号需要对网站进行一番改造,涉及到的代码不是大家能搞定的,所以介绍两款熊掌号插件FanlySubmit和BaiduXZHSubmit,可以有效提交原创内容,省去大把时间,更多精力放在网站上面。一、FanlySubmitFanlySubmit需要添加熊掌号APPID和熊掌号Token,就能够正常连接到熊掌号并自动推送文章了,简单的不得了。看下图填写空格,勾选全部选项即可。不过要注意一点,就是说你要知道自己的文章原创程度有多少,如果确实原创的少就别勾选这个选项,熊掌号会自行判断你文章的原创度,如果总是提交一些不达标的伪原创文章,会被降低提交数量的。下面是文章发布页面的选项,上面默认全部勾选后,下面会自动勾选,也可以自己改动。如何验证提交到熊掌号了呢?去熊掌号后台查看提交数量是否变化了就知道了。FanlySubmit插件在wordpress插件里面是找不到的,只能在作者网站下载到,大家自行搜索网址。二、BaiduXZHSubmit百度熊掌

  • Redis实现类似同步方法调用的功能(二)

    接上一篇,这么干纯粹是为了好玩。上一篇的博客中的例子只能处理一个Server对一个Client的情况,今天修改了一版,可以支持一个Server对多个Client。实现方式就是Server每派发一个动作就扔到一个线程里去,Client也类似每收到一个数据,就起一个线程去做自己的逻辑。这样看起来就有点像socket变成了。importredis importtime importjson importthreading host='localhost' port=6322 queue='myqueue' classServer(threading.Thread): def__init__(self): threading.Thread.__init__(self) defrun(self): pool=redis.BlockingConnectionPool(host=host,port=port,db='0') conn=redis.Redis(connection_pool=pool) idx=0 whileTru

  • Linux账号管理

    Linux的账号管理包括用户与用户组,它们两者是多对多的关系,即一个用户可以属于多个用户组,且一个用户组可以包含多个用户。一个用户组中的用户具有相同的权限。 在Linux中,用户的账户信息和用户组信息都记录在指定的文件中,这些文件构成了Linux用户管理的基础。下面我们就详细介绍Linux用户管理相关的文件。Linux的账号文件1./etc/passwd这个文件虽然叫passwd,但实际存放Linux所有的账户信息,其中每一行表示一个账户信息,每条账户信息由7个字段构成,每个字段用:分隔。 这个文件中除了有root账户、我们自定义的用户账户外,还有很多系统账户,如下所示:root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/s

  • java实现简单的抽奖游戏(数组学习)

    参考文章https://blog.csdn.net/zzq1992126/article/details/44118429 参考书籍《java核心技术·卷一:基础知识》 代码packagecn.itcast.day04.demo02; //程序目标:从给定的奖池中抽取出一系列中奖数字,每个数字只能被选取一次。程序实现如下: importjava.util.Arrays; importjava.util.Scanner; publicclassLottryDrawing{ publicstaticvoidmain(String[]args){ System.out.println("请输入最大抽奖数字,就是奖池最大多少,也就是从哪个范围随机生成中奖数字"); Scannerinput=newScanner(System.in); intn=input.nextInt(); System.out.println("请输入抽奖个数,就是就是生成几个抽奖次数,"+ "Math.random()*n生成的数字就是中奖数字,没有指定的中奖数字

  • Samba日志分析

    Samba日志分析 随着我们文件共享安全级别的提高,越来越多的情况下需要对日志进行记录并审计。Linux平台下的Samba服务的配置文件是smb.conf,有不少图形化配置工具例如Webmin、smbconftool、SWAT及RedHat提供的system-config-samba等工具都可以为您简化配置smb.conf的过程,但这些工具的细致程度却无法满足samba的需求,对于如何分析samba日志的问题,没有现成的工具,我们必须对其日志结构有所了解。 1.Samba默认提供的日志     具体怎么配置samba就不讲了,下面的实例用以跟踪查询客户端通过SMB访问共享资源的情况。命令netstat–na|grepESTABLISHED  代表显示TCP已连接情况,如图1所示。 图 1 Samba日志分析 如图1中倒数第二行的PID10600,代表smbd的进程ID号,用“ps-ef|grep10600”可查看到。与此同时,系统会把samba进程启动日志写到/var/log/messages中。 通常,

  • CF1349F2

    F1-Solution 方便起见给\(k\)减\(1\),考虑答案为: \[\begin{aligned} &Ans_k=\sumf_{i,k}\binom{n}{i}(n-i)! \end{aligned}\]对于\(f_{i,k}\)考虑通过容斥计算,设\(g_{i,k}\)表示长度为\(i\)的序列存在\(k\)处<的方案数。 那么就有: \[g_{i,k}=\sumf_{i,j}\binom{j}{k} \]对于\(g_{i,k}\),考虑其代表了\(i-k\)个连通块,此时相当于将\(i\)个带标号球分配给\(i-k\)个盒子的方案数,又相当于给\(1\simi\)染上\(i-k\)种颜色,且每类颜色非空的方案数,即\(i!\cdot(e^x-1)^{i-k}[x^i]\) 于是: \[f_{i,k}=\sum_j\binom{j}{k}(-1)^{j-k}g_{i,j} \]故: \[Ans_k=\sum_i\binom{n}{i}(n-i)!\sum_j\binom{j}{k}(-1)^{j-k}i!\cdot(e^x-1)^{i-j}[x^i] \]\[A

  • antd4 form表单使用

      antd4.xform使用实例: //AntdForm4.jsclass组件形式 importReact,{Component,useEffect}from"react"; import{Form,Input,Button}from"antd"; constFormItem=Form.Item; constnameRules={required:true,message:"请输入姓名"}; constpasswordRules={required:true,message:"请输入密码"}; exportdefaultclassAntdFormPageextendsComponent{ formRef=React.createRef(); componentDidMount(){ this.formRef.current.setFieldsValue({name:"defaultname"}); } onReset=()=>{ this.formRef.current.resetFields(); }; onFinish=(val)=>{ console.

  • Flink集群搭建

    Flink支持多种安装模式。 local(本地)——单机模式,一般不使用 standalone——独立模式,Flink自带集群,开发测试环境使用 yarn——计算资源统一由HadoopYARN管理,生产环境测试   Standalone模式  步骤 1.解压flink压缩包到指定目录 2.配置flink 3.配置slaves节点 4.分发flink到各个节点 5.启动集群 6.提交WordCount程序测试 7.查看FlinkWebUI 具体操作 1.上传flink压缩包到指定目录 2.解压缩flink到/export/servers目录 tar-xvzfflink-1.6.0-bin-hadoop26-scala_2.11.tgz-C /export/servers 3.使用vi修改conf/flink-conf.yaml  #配置Master的机器名(IP地址) jobmanager.rpc.address:node-1  #配置每个taskmanager生成的临时文件夹 taskmanag

  • C++中文件组织的一般方法

    用C++编写一个稍大程序时,我们需要别写几个类和一些过程函数。为了文档的规整有序和程序的排错,文档比较合理的安排方法: 1、每个类的声明写在一个头文件中,根据编译器的要求你可以加.h后缀名,也可以不加。这个头文件一般以类的名字命名。并且为了防止编译器多次包含同一个头文件,头文件总是以下面的框架组织: #ifndefCLASSNMAE_H_ #defineCLASSNAME_H_ …………将你类的声明写在这里面 #endif复制 说明:CLASSNAME_H_中的CLASSNAME就是你在这个文件中声明的类名。 2、将类的实现放在另一个文件中,取名为classname.cpp(classname为你在类声明文件中声明的类名)。并且在该文件中的第一行包含类声明的头文件,如:#include”classname”(C++新标准不支持带.h的头文件)。然后在此文件中写类的实现代码。一般格式: #include”classname” 3、与类的相似,编写函数时,我们总是把函数的声明和一些常数的声明放在一个头件中;把函数的具体实现放在另一个头文件中。 4、一般地如果你在某个源文件中需要引

  • swift学习

      中文版书籍: http://numbbbbb.gitbooks.io/-the-swift-programming-language-/ http://www.swiftv.cn/

  • 数据挖掘中的十大经典算法

    以下就是从参加评选的18种候选算法中,最终决选出来的十大经典算法: 一、C4.5C4.5,是机器学习算法中的一个分类决策树算法,它是决策树(决策树也就是做决策的节点间的组织方式像一棵树,其实是一个倒树)核心算法,ID3的改进算法,所以基本上了解了一半决策树构造方法就能构造它。决策树构造方法其实就是每次选择一个好的特征以及分裂点作为当前节点的分类条件。 C4.5相比于ID3改进的地方有:1、用信息增益率来选择属性。ID3选择属性用的是子树的信息增益,这里可以用很多方法来定义信息,ID3使用的是熵(entropy,熵是一种不纯度度量准则),也就是熵的变化值. 而C4.5用的是信息增益率。对,区别就在于一个是信息增益,一个是信息增益率。一般来说率就是用来取平衡用的,就像方差起的作用差不多,比如有两个跑步的人,一个起点是10m/s的人、其10s后为20m/s;另一个人起速是1m/s、其1s后为2m/s。如果紧紧算差值那么两个差距就很大了,如果使用速度增加率(加速度,即都是为1m/s^2)来衡量,2个人就是一样的加速度。 因此,C4.5克服了ID3用信息增益选择属性时偏向选择取值多的属性的不足。

  • 0-9的数 每两个换行输出

    1packagetest; 2 3publicclassTest{ 4publicstaticvoidmain(String[]args){ 5System.out.println("当前记录为"); 6for(inti=0;i<10;i++){ 7System.out.print(i+""); 8if(i%2==0){ 9continue;//注意continue的用法继续循环不退出 10} 11System.out.println(); 12 13} 14} 15}复制 念起,便是这个季节最浓的色彩

相关推荐

推荐阅读