Docker的底层原理+基础命令

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Docker的底层原理+基础命令

传统虚拟机与docker的区别

传统虚拟机:完全型解耦

   需要携带操作系统,在系统之上运行应用程序

   调用资源繁琐:

例如内存调用:利用hypervisor虚拟化内存,整个过程需要 虚拟内存--虚拟物理内存--真正物理内存三步


docker:半解耦

   不携带操作系统,轻巧

   docker引擎分配资源 虚拟内存--真正物理内存


对比        传统虚拟机        docker

磁盘        几个或几十G             几十到几百M

CPU内存        虚拟系统占用过多        docker引擎几乎不占资源

启动速度        几分钟            几秒

安装管理        专门的管理运维        安装管理方便

应用部署        每次部署耗时        第二次开始极其方便

耦合性        多个服务互相干扰        一个服务一个容器,隔离

系统依赖        无            相同相似内核,推荐类Unix



Namespace(名称空间):


名字空间是Linux内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样。名字空间保证了容器之间彼此互不影响。


**IPC:**                共享内存、消息列队

**MNT:**                 挂载点、文件系统

**NET:**                  网络栈

**PID:**                   进程编号

**USER:**                用户、组

**UTS:**                  主机名、域名


pid 名字空间



不同用户的进程就是通过 pid 名字空间隔离开的,且不同名字空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为Docker进程,每个 LXC 进程具有不同的名字空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。


net 名字空间



有了 pid 名字空间, 每个名字空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 名字空间实现的, 每个 net 名字空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。


ipc 名字空间



容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication – IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 名字空间中的进程间交互,因此需要在 IPC 资源申请时加入名字空间信息,每个 IPC 资源有一个唯一的 32 位 id。


mnt 名字空间



类似 chroot,将一个进程放到一个特定的目录执行。mnt 名字空间允许不同名字空间的进程看到的文件结构不同,这样每个名字空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个名字空间中的容器在 /proc/mounts 的信息只包含所在名字空间的 mount point。


uts 名字空间



UTS(“UNIX Time-sharing System”) 名字空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。


user 名字空间



每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。


namespace六项隔离,实现了容器与宿主机、容器与容器之间的隔离。




Cgroup(控制组):


控制组(cgroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。


控制组技术最早是由 Google 的程序员 2006 年起提出,Linux 内核自 2.6.24 开始支持。


控制组可以提供对容器的内存、CPU、磁盘 IO 等资源的限制和审计管理。。

mount -t cgroup 
cd /sys/fs/cgroup


四大功能:


   1)资源限制:cgroup可以对进程组使用的资源总额进行限制。


   2) 优先级分配:通过分配的cpu时间片数量以及硬盘IO带宽大小,实际上相当于控制了进程运行的优先级别。


   3)资源统计:cgroup可以统计系统资源使用量,比如cpu使用时间,内存使用量等,用于按量计费。同时,还支持挂起动能,也就是说通过cgroup把所有资源限制起来,对资源都不能使用,注意着并不是说我们的程序不能使用了,只是不能使用资源,处于等待状态。


   4)进程控制:可以对进程组执行挂起、恢复等操作。




内存限额:


容器内存包括两个部分:物理内存和swap


可以通过参数控制容器内存的使用量:

   -m 或者--memory:  设置内存的使用限额

   --memory-swap: 设置内存+ swap的使用限额


例如:运行一个容器,并且限制该容器最多使用200M内存和100M的swap。    

docker run -itd --name container1 -m 200MB --memory-swap 300MB centos:7 
cd /sys/fs/cgroup/memory/docker/545dc2376efedb0d48e174fd8a9a614baf64f99fc09
cat memory.limit_in_bytes 
209715200
cat memory.memsw.limit_in_bytes
314572800

CPU使用


通过-c或者--cpu-shares设置容器使用cpu的权重。如果不设置默认为1024.


例如:

docker run -itd --name container2 -c 512 centos:7 
cd /sys/fs/cgroup/cpu/docker/76178cf366cea1d7a60729f21877d135c25141281ae4b6
cat cpu.shares 
512

对比一个没有做CPU权重限制的容器。

docker run -itd --name container3 centos:7 
cd /sys/fs/cgroup/cpu/docker/4d0858de8e825d362305c05de2293a619d4329ba82dbf8
cat cpu.shares
1024

容器的Block IO


磁盘的读写。


docker中可以通过设置权重,限制bps和iops的方式控制容器读写磁盘的IO.

bps:  每秒读写的数据量      byte per second

iops:  每秒IO的次数         io per second。


默认情况下,所有容器都能够平等的读写磁盘,也可以通过--blkio-weight参数改变容器的blockIO 的优先级。


--device-read-bps: 显示读取某个设备的bps。

--device-write-bps: 显示写入某个设备的bps。

--device-read-iops: 显示读取某个设备的iops。

--device-write-iops: 显示写入某个设备的iops。


例如:限制testA这个容器,写入/dev/sda这块磁盘的bps为30MB.

docker run -it  --name testA --device-write-bps /dev/sda:30MB centos:7

从/dev/zero输入,然后输出到test.out文件中,每次大小为1M,总共800次,oflag=direct 用来指定directIO方式写文件,这样才会使--device-write-bps生效。

time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct
800+0 records in
800+0 records out
838860800 bytes (839 MB) copied, 26.6228 s, 31.5 MB/s
real    0m26.629s
user    0m0.005s
sys    0m1.902s



再运行一个不做限制testB容器,对比一下速率

docker run -itd --name testb centos:7 
docker exec -it testb /bin/bash
time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct
800+0 records in
800+0 records out
838860800 bytes (839 MB) copied, 2.32559 s, 361 MB/s
real    0m2.328s
user    0m0.000s
sys    0m2.107s


查看镜像

docker images

查看所有容器

docker ps -a

查看运行的容器

docker ps


保存镜像

docker save -o nginx.tar nginx:latest

载入镜像

docker load -i nginx.tar

停止容器

docker stop 容器名或id号


删除容器

docker rm 容器名或id号

删除镜像

docker rmi nginx:latest

进入容器

docker exec -it testA /bin/bash

Docker的常用命令

1、帮助命令

docker version         #显示docker的版本信息。

docker info         #显示docker的系统信息,包括镜像和容器的数量

docker 命令 --help     #帮助命令

帮助文档的地址:https://docs.docker.com/engine/reference/commandline/docker/


2、镜像命令


docker images         #查看所有本地主机上的镜像 可以使用docker image ls代替

docker search         #搜索镜像

docker pull         #下载镜像 docker image pull

docker rmi             #删除镜像 docker image rm


docker images

REPOSITORY    TAG       IMAGE ID       CREATED        SIZE

hello-world   latest    d1165f221234   4 months ago   13.3kB

# 解释

REPOSITORY   镜像的仓库源

TAG          镜像的标签

IMAGE ID     镜像的ID

CREATED      镜像的创建时间

SIZE         镜像的大小

# 可选项

 -a, --all             # 列出所有的镜像

 -q, --quiet           # 只显示镜像的id


docker images -aq     #显示所有镜像的id



docker search 搜索镜像


docker search mysql

NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED

mysql                             MySQL is a widely used, open-source relation…   11138     [OK]      

mariadb                           MariaDB Server is a high performing open sou…   4221      [OK]      

mysql/mysql-server                Optimized MySQL Server Docker images. Create…   829       [OK]

percona                           Percona Server is a fork of the MySQL relati…   547       [OK]      

phpmyadmin                        phpMyAdmin - A web interface for MySQL and M…   274       [OK]      

centos/mysql-57-centos7           MySQL 5.7 SQL database server                    89                  

mysql/mysql-cluster               Experimental MySQL Cluster Docker images. Cr…   88  


# 可选项

--filter=STARS=3000   # 搜索出来的镜像就死starts 大于3000的


docker search mysql --filter=STARS=3000

NAME      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED

mysql     MySQL is a widely used, open-source relation…   11138     [OK]      

mariadb   MariaDB Server is a high performing open sou…   4221      [OK]



docker pull 下载镜像


# 下载镜像 docker pull 镜像名[:tag]

docker pull mysql

Using default tag: latest

latest: Pulling from library/mysql

b4d181a07f80: Pull complete

a462b60610f5: Pull complete

578fafb77ab8: Pull complete

524046006037: Pull complete

d0cbe54c8855: Pull complete

aa18e05cc46d: Pull complete

32ca814c833f: Pull complete

9ecc8abdb7f5: Pull complete

ad042b682e0f: Pull complete

71d327c6bb78: Pull complete

165d1d10a3fa: Pull complete

2f40c47d0626: Pull complete

Digest: sha256:52b8406e4c32b8cf0557f1b74517e14c5393aff5cf0384eff62d9e81f4985d4b

Status: Downloaded newer image for mysql:latest

docker.io/library/mysql:latest

# 等价于

docker pull mysql

docker pull docker.io/library/mysql:latest


# 指定版本下载

docker pull mysql:5.7

5.7: Pulling from library/mysql

b4d181a07f80: Already exists

a462b60610f5: Already exists

578fafb77ab8: Already exists

524046006037: Already exists

d0cbe54c8855: Already exists

aa18e05cc46d: Already exists

32ca814c833f: Already exists

52645b4af634: Pull complete

bca6a5b14385: Pull complete

309f36297c75: Pull complete

7d75cacde0f8: Pull complete

Digest: sha256:1a2f9cd257e75cc80e9118b303d1648366bc2049101449bf2c8d82b022ea86b7

Status: Downloaded newer image for mysql:5.7

docker.io/library/mysql:5.7



docker rmi 删除镜像


docker rmi -f 镜像id                     # 删除指定的镜像

docker rmi -f 镜像id 镜像id 镜像id 镜像id       # 删除多个镜像

docker rmi -f $(docker images -aq)             # 删除全部的镜像



3、容器命令


docker run 镜像id         #新建容器并启动

docker ps                 #列出所有运行的容器 docker container list

docker rm 容器id         #删除指定容器

docker start 容器id     #启动容器

docker restart容器id     #重启容器

docker stop 容器id         #停止当前正在运行的容器

docker kill 容器id         #强制停止当前容器

docker pause 容器id     #挂起容器

docker unpause 容器id         #恢复挂起

docker commit 容器id 镜像名     #把容器制作成镜像


说明:有镜像才可以创建容器,linux,下载一个centos镜像来测试学习

docker pull centos:7


新建容器并启动

docker run [可选参数] image

# 参数说明

--name="Name"     容器名字   tomcat01   tomcat02,  用来区分容器

-d                后台方式运行

-it               使用交互方式运行,进入容器查看内容

-p                指定容器的端口 -p  8080:8080

   -p   ip:主机端口:容器端口

   -p   主机端口:容器端口(常用)

   -p   容器端口(随机映射到外部端口,一般32000+)


-P       随机映射端口


案例:

docker run -d --name web1 -p 8080:80 nginx    #指定端口映射

docker run -d --name web2 -p 8081:80 nginx    

docker run -d --name web3 -p 80 nginx        #随机映射端口

docker run -d --name web4 -P nginx            #随机映射端口



# 测试,启动并进入容器

docker run -it centos:7 /bin/bash

[root@90262d2cbb32 /]# ls

bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var


# 从容器中退回主机,容器关闭(想要容器持续运行,添加参数-d)

[root@90262d2cbb32 /]# exit

exit



列出所有的运行的容器


#docker ps命令 #列出当前正在运行的容器

   # 列出当前正在运行的容器

-a  # 列出当前正在运行的容器+带出历史运行过的容器

-q  # 只显示容器的编号


docker ps

CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES

a199a44bb07c   centos    "/bin/bash"   23 minutes ago   Up 23 minutes             compassionate_feynman


docker ps -a

CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS                     PORTS     NAMES

90262d2cbb32   centos         "/bin/bash"   4 minutes ago    Exited (0) 3 minutes ago             zen_cerf

a199a44bb07c   centos         "/bin/bash"   23 minutes ago   Up 23 minutes                        compassionate_feynman

2a70eb3c087c   d1165f221234   "/hello"      25 hours ago     Exited (0) 25 hours ago              bold_darwin


退出容器

exit    


删除容器

docker rm 容器id                 # 删除指定容器,不能删除正在运行的容器,如果要强制删除  

docker rm -f $(docker ps -aq)     # 删除所有的容器

docker ps -a -q | xargs docker rm -f      # 删除所有的容器


启动和停止容器的操作

docker start 容器id     #启动容器

docker restart 容器id  #重启容器

docker stop 容器id      #停止当前正在运行的容器

docker kill 容器id      #强制停止当前容器


4、常用其他命令

后台启动容器

# 命令 docker  run -d 镜像名!


docker run -d centos:7

# 问题docker  ps,发现centos停止了

# 常见的坑,docker 容器使用后台运行,就必须要有一个前台进程, docker 发现没有应用,就会自动停止

# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了


查看日志

docker logs --help

Usage:  docker logs [OPTIONS] CONTAINER

Fetch the logs of a container

Options:      

   --details                显示更多的信息  

   -f, --follow             跟踪日志输出,最后一行为当前时间戳的日志      

   --since string           显示具体某个时间或时间段的日志

   -n, --tail string        从末尾显示多少行,默认all

   -t, --timestamps         显示时间戳      

 


#命令测试看日志

docker logs -f -t --details  容器id


# 自己编写一段shell脚本

docker run -d centos:7 /bin/sh -c "while true;do echo 6666;sleep 1;done" #模拟日志


docker ps

CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES596b80f933ac   centos    "/bin/sh -c 'while t…"   9 seconds ago   Up 8 seconds             priceless_meninsky


# 显示日志

-tf                #显示日志

--tail=number         #要显示日志条数

docker logs -tf --tail 10 596b80f933ac

docker logs -tf --tail=10 596b80f933ac



查看容器中进程信息 ps


# 命令 docker top 容器id

docker top 596b80f933ac

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD

root                10528               10491               0                   17:17               ?                   00:00:00            /bin/sh -c while true;do echo 6666;sleep 1;done

root                11362               10528               0                   17:23               ?                   00:00:00            /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1



查看镜像的元数据


# 命令

docker inspect 容器id

#测试

docker inspect 596b80f933ac



进入当前正在运行的容器  *****


# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置

# 命令

docker exec -it 容器id bashShell  


# 测试

docker exec -it 596b80f933ac /bin/bash

ls

bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var


ps -ef

UID        PID  PPID  C STIME TTY          TIME CMD

root         1     0  0 Jul19 ?        00:00:17 /bin/sh -c while true;do echo 6666;sleep 1;done

root     23455     0  0 00:51 pts/0    00:00:00 /bin/bash

root     23704     1  0 00:55 ?        00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1

root     23705 23455  0 00:55 pts/0    00:00:00 ps -ef


# 方式二

docker attach 容器id

# 测试

docker attach 596b80f933ac

正在执行当前的代码。。。。

# docker exec    #进入容器后开启一个新的终端,可以在里面操作(常用)

# docker attach  #进入容器正在执行的终端,不会启动新的进程!


从容器内拷贝到主机上

docker cp 容器id:容器内路径   目的的主机路径


docker attach 0b308ba7528d

cd /home

ls

# 在容器内新建一个文件

touch test.java

exit

# 将这个文件拷贝出来到主机上

docker cp 0b308ba7528d:/home/test.java /home


# 拷贝是一个手动过程,使用 -v 卷的技术,可以更容易实现


==============================

总结:

attach    Attach to a running container                                  #当前shell下attach连接指定运行镜像

build     Build an image from a Dockerfile                               #通过Dockerfile定制镜像

commit    Create a new image from a containers changes                   #提交当前容器为新的镜像

cp          Copy files/folders from a container to a HOSTDIR or to STDOUT  #从容器中拷贝指定文件或者目录到宿主机中

create    Create a new container                                         #创建一个新的容器,同run 但不启动容器

diff      Inspect changes on a containers filesystem                     #查看docker容器变化

events    Get real time events from the server                           #从docker服务获取容器实时事件

exec      Run a command in a running container                           #在已存在的容器上运行命令

export    Export a containers filesystem as a tar archive                #导出容器的内容流作为一个tar归档文件(对应import)

history   Show the history of an image                                   #展示一个镜像形成历史

images    List images                                                    #列出系统当前镜像

import    Import the contents from a tarball to create a filesystem image  #从tar包中的内容创建一个新的文件系统映像(对应export)

info      Display system-wide information                                #显示系统相关信息

inspect   Return low-level information on a container or image           #查看容器详细信息

kill      Kill a running container                                       #kill指定docker容器

load      Load an image from a tar archive or STDIN                      #从一个tar包中加载一个镜像(对应save)

login     Register or log in to a Docker registry                        #注册或者登陆一个docker源服务器

logout    Log out from a Docker registry                                 #从当前Docker registry退出

logs      Fetch the logs of a container                                  #输出当前容器日志信息

pause     Pause all processes within a container                         #暂停容器

port      List port mappings or a specific mapping for the CONTAINER     #查看映射端口对应的容器内部源端口

ps        List containers                                                #列出容器列表

pull      Pull an image or a repository from a registry                  #从docker镜像源服务器拉取指定镜像或者库镜像

push      Push an image or a repository to a registry                    #推送指定镜像或者库镜像至docker源服务器

rename    Rename a container                                             #重命名容器

restart   Restart a running container                                    #重启运行的容器

rm        Remove one or more containers                                  #移除一个或者多个容器

rmi       Remove one or more images                                      #移除一个或多个镜像(无容器使用该镜像才可以删除,否则需要删除相关容器才可以继续或者-f强制删除)

run       Run a command in a new container                               #创建一个新的容器并运行一个命令

save      Save an image(s) to a tar archive                              #保存一个镜像为一个tar包(对应load)

search    Search the Docker Hub for images                               #在docker hub中搜索镜像

start     Start one or more stopped containers                           #启动容器

stats     Display a live stream of container(s) resource usage statistics  #统计容器使用资源

stop      Stop a running container                                       #停止容器

tag       Tag an image into a repository                                 #给源中镜像打标签

top       Display the running processes of a container                   #查看容器中运行的进程信息

unpause   Unpause all processes within a container                       #取消暂停容器

version   Show the Docker version information                            #查看容器版本号

wait      Block until a container stops, then print its exit code        #截取容器停止时的退出状态值


===================================================

练习

centos安装nginx


#1. 搜索镜像 search 建议大家去docker hub搜索,可以看到帮助文档

docker search centos


#2. 拉取镜像 pull

docker pull centos


#3. 运行测试

# -d 后台运行

# --name 给容器命名

# -p 宿主机端口:容器内部端口


docker images

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE

nginx        latest    4cdc5dd7eaad   13 days ago    133MB

centos       latest    300e315adb2f   7 months ago   209MB


docker run -itd --name nginx01 -p 80:80 centos

8661ad7244a0dc33bed197e3e0147843b4e5e9d9e8646e442ffb102e1d0d4ac3


docker ps

CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                  NAMES

8661ad7244a0   centos     "/docker-entrypoint.…"   9 seconds ago   Up 8 seconds   0.0.0.0:80->80/tcp   nginx01


curl localhost


# 进入容器

docker exec -it nginx01 /bin/bash


#安装vim和nginx

yum -y install epel-release

yum -y install vim net-tools nginx


#把容器制作成镜像

docker commit nginx01 mynginx


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
安全 Docker 容器
|
2月前
|
网络安全 数据安全/隐私保护 Docker
|
2月前
|
存储 缓存 Linux
docker的底层原理六: 联合文件系统(UnionFS)
本文介绍了Docker使用的联合文件系统(UnionFS),它通过分层存储和写时复制(CoW)机制,实现了容器的轻量级、高性能存储,支持了镜像继承、数据持久化和隔离性。
73 0
|
2月前
|
存储 监控 安全
|
2月前
|
存储 关系型数据库 MySQL
|
2月前
|
网络协议 Docker 容器
docker pull命令拉取镜像失败的解决方案
docker pull命令拉取镜像失败的解决方案
1204 1
|
2月前
|
安全 网络安全 开发者
Docker学习笔记(一):Docker命令总结
本文介绍了Docker的基本概念、优点以及常用命令。Docker是一个开源的应用容器引擎,它通过容器打包应用程序及其依赖项,实现快速部署和迁移。主要优点包括轻量级、可移植性、易于管理、安全性和开源性。文章还区分了镜像和容器的概念,并提供了构建镜像、查看容器、运行容器、停止和删除容器等常用Docker命令的示例。
145 0
|
2月前
|
安全 Linux 调度
docker的底层原理三: 内核共享
本文阐述了Docker容器通过共享宿主机的内核来实现轻量级运行,同时利用命名空间、控制组、文件系统和网络隔离等技术确保容器的安全性和资源控制。
77 6
|
2月前
|
Linux 网络安全 Docker
安装后无法使用 Docker 命令
【10月更文挑战第3天】
273 2
|
2月前
|
存储 监控 Shell
docker的底层原理二:容器运行时环境
本文深入探讨了Docker容器运行时环境的关键技术,包括命名空间、控制组、联合文件系统、容器运行时以及分离的进程树,这些技术共同确保了容器的隔离性、资源控制和可移植性。
45 5