从零自制docker-8-【构建实现run命令的容器】

文章目录

  • log "github.com/sirupsen/logrus"
  • `args...`
  • go module
  • import第三方包失败
  • package和 go import的导入
  • go build . 和go run
  • cli库
  • `log.SetFormatter(&log.JSONFormatter{})`
  • error和nil的关系
  • cmd.Wait()和cmd.Start()
  • arg……
  • context.Args().Get(0)
  • syscall.Exec和os/exec.Command
  • `syscall.Exec`
  • mountflags
  • syscall.Mount
  • 请注意,只有包中首字母大写的函数(public函数)才能被其他包调用。首字母小写的函数(private函数)只能在包内部使用
  • 代码
  • 最终效果
  • 问题

log “github.com/sirupsen/logrus”

log “github.com/sirupsen/logrus”: 这是引入了 github.com/sirupsen/logrus 包,并将其重命名为 log

args...

假设我们有一个切片 myArgs := []string{"arg1", "arg2", "arg3"},我们想将这个切片作为参数传递给 exec.Command() 函数。

如果直接传递 myArgs 作为参数,那么 exec.Command() 函数会将整个切片作为一个单独的参数:

cmd := exec.Command("/path/to/executable", myArgs)

这相当于执行命令 /path/to/executable ["arg1" "arg2" "arg3"]

但是如果我们使用 args... 语法,就可以将切片中的每个元素都作为独立的参数传递给 exec.Command() 函数:

cmd := exec.Command("/path/to/executable", myArgs...)

这相当于执行命令 /path/to/executable "arg1" "arg2" "arg3"

args... 语法会将切片 myArgs 中的每个元素都作为独立的参数传递给 exec.Command() 函数。这种方式更加灵活,可以方便地将任意长度的参数列表传递给外部命令。

另一个例子是:

args := []string{"-flag1", "value1", "-flag2", "value2"}
cmd := exec.Command("/path/to/executable", args...)

这会将 args 切片中的 6 个元素全部作为独立的参数传递给 /path/to/executable 命令。

总之,args... 语法可以将一个切片展开为多个独立的参数,传递给需要接受多个参数的函数,在处理命令行参数时非常有用。

go module

三种包管理方式参考连接

go get下载的第三方包的位置

在GOMODULE模式下,go get命令会将依赖包下载到$GOPATH/pkg/mod目录下

go module包管理方式(推荐使用)

GO111MODULE 是一个用来控制 Go Modules 行为的环境变量。在 Go 1.11 版本之后,Go 引入了 Go Modules 来管理依赖关系和版本控制。GO111MODULE 环境变量可以设置为以下几个值:

  1. GO111MODULE=on:这是启用 Go Modules 的模式。当设置为 on 时,Go 将会使用 Go Modules 来管理依赖关系,无需再依赖 GOPATH。

  2. GO111MODULE=off:这是禁用 Go Modules 的模式。当设置为 off 时,Go 将使用传统的 GOPATH 模式来管理依赖关系。

  3. GO111MODULE=auto:这是自动模式。在自动模式下,Go 将根据当前目录是否包含 go.mod 文件来决定是否启用 Go Modules。如果在项目根目录下存在 go.mod 文件,则会启用 Go Modules,否则使用 GOPATH 模式。

因此,当设置 GO111MODULE=''(即空字符串)时,表示未显式设置 GO111MODULE 环境变量,Go 将根据自动模式来决定是否启用 Go Modules,具体行为取决于当前项目的目录结构和是否存在 go.mod 文件。

go mod tidy:检测该文件夹目录下所有引入的依赖,写入 go.mod 文件。删除错误或者不使用的modules,下载没download的package(下载到$GOPATH/pkg/mod)

import第三方包失败

go env -w GOPROXY=https://goproxy.cn,direct

再尝试

package和 go import的导入

go import导入包详解

在这里插入图片描述

golang使用同目录下的文件,golang中的package使用简介

在golang 里面一个目录为一个package, 一个package级别的func, type, 变量, 常量, 这个package下的所有文件里的代码都可以随意访问, 也不需要首字母大写。
引用当前目录得其它子目录中的文件可以import这个文件所在的文件夹相对位置,也可以import 当前目录/子目录中的package

Go语言中的包Package详解

包分为两种,一种是main函数(可执行文件)一种是库函数的包
可执行程序的包,编译完成后会生成一个可执行文件、静态库的包编译之后会生成一个.a为后缀的文件(在$GOPATH/pkg/里),自己不能执行只能被可执行包引用。

可执行程序的包必须以main作为包名,静态库的包名没有

main包和其他类库通过静态链接,最终形成的可执行文件是没有任何外部依赖的。

go build . 和go run

go build .将当前目录所有的文件都会编译并生成一个可执行程序
go run 编译单个程序并运行,此时如果多个package为main的程序有相互依赖,那么go run 其中一个会报错,因为用了其他package为main的程序

cli库

Go 每日一库之 cli

log.SetFormatter(&log.JSONFormatter{})

假设我们有一个简单的 Go 程序,用于记录一些基本的日志信息:

package main

import (
    "log"
)

func main() {
    // 设置日志输出格式为 JSON 格式
    log.SetFormatter(&log.JSONFormatter{})

    // 记录一些日志信息
    log.Println("This is an informational message")
    log.Printf("User %s logged in", "John Doe")
    log.Fatalf("Failed to connect to database: %s", "connection refused")
}

在这个例子中,我们首先使用 log.SetFormatter(&log.JSONFormatter{}) 将日志输出格式设置为 JSON 格式。

然后,我们分别使用 log.Println()log.Printf()log.Fatalf() 记录了三条不同级别的日志信息。

当我们运行这个程序时,日志输出将会是 JSON 格式,类似于以下内容:

{"level":"info","msg":"This is an informational message","time":"2024-04-07T12:34:56Z"}
{"level":"info","msg":"User John Doe logged in","time":"2024-04-07T12:34:56Z"}
{"level":"error","msg":"Failed to connect to database: connection refused","time":"2024-04-07T12:34:56Z"}

与默认的文本格式相比,JSON 格式的日志输出包含了更多的元信息,如日志级别(level)、消息内容(msg)和时间戳(time)。这种结构化的日志格式更加方便后续的处理和分析,特别是在需要机器解析日志的场景下。

总之,log.SetFormatter(&log.JSONFormatter{}) 是一个非常实用的方法,可以帮助我们更好地管理和处理 Go 程序的日志信息。

error和nil的关系

error类型和nil的关系是这样的:

  • error类型是Go语言中一个接口,用来表示一个错误情况。一个实现了Error()方法的值,即使没有实现任何具体的行为,也被称为一个error值。
  • nil是Go语言中的一个特殊值,它表示的是空值或不存在的值,对于error类型的值来说,nil表示没有错误发生。

在函数返回值中,当函数执行正确,没有遇到错误时,我们通常会用nil来表示。例如:

func someFunction() error {
    // 函数执行成功,没有错误
    return nil
}

当函数执行遇到错误时,我们会返回一个非nilerror值,例如:

func someFunction() error {
    // 函数执行失败,返回一个错误
    return errors.New("Some error occurred.")
}

所以,error类型和nil的关系是:nil表示error类型的值为非错误状态,非nilerror表示存在错误。在函数返回时,如果error值为nil,则表示函数执行成功,否则表示有错误发生。

cmd.Wait()和cmd.Start()

err变量在调用cmd.Wait()方法后得到返回值。当调用cmd.Start()时,该方法会启动命令并在后台执行。如果在启动命令时发生了错误(例如命令不存在、权限不足等),cmd.Start()会立即返回一个非nil的错误值。

而cmd.Wait()方法的作用是等待命令执行完毕。当命令执行结束后,此方法会返回。返回值有两个含义:

若命令执行成功结束,err将为nil。
若在命令执行过程中出现任何错误(比如命令被信号终止、超时退出等),err将包含相应的错误信息。
因此,在你的代码片段中,err = cmd.Wait()这一行会阻塞直到外部命令执行完毕,然后返回一个表示命令执行结果(成功与否及退出状态)的错误值。

arg……

在Go语言中,os/exec包提供了执行外部命令的功能。这里的代码片段是用于创建一个可执行命令的实例。

args := []string{"init", cmd} 是定义了一个字符串切片(slice),它存储了要执行的命令及其参数。在这个例子中:

  • "init":是一个命令参数,表示要执行的子命令为 “init”。
  • cmd:是一个变量,通常代表另一个命令参数。具体的值取决于程序上下文中的赋值情况。

然后,command := exec.Command("/proc/self/exe", args...) 这一行是用来创建一个新的 *Cmd 结构体实例,这个实例代表了一个待执行的命令。

"/proc/self/exe" 是一个特殊路径,在Linux系统中指向当前正在运行的可执行文件。也就是说,这里实际上是打算执行与当前进程相同的一个新进程,并且传入参数 args

args... 是Go语言中的变长参数语法,它会将字符串切片 args 扩展为多个单独的参数传递给 exec.Command 函数。

所以整个语句的作用是,根据给定的参数列表 args,执行与当前进程相同的新的命令行程序,并且第一个参数是 “init”,第二个参数是变量 cmd 的值。

context.Args().Get(0)

在Go语言中,这段代码片段是基于第三方命令行工具库(如urfave/clicobra)来定义一个命令行命令init的结构体。在这些库中,cli.Context对象包含了从命令行输入解析出来的所有信息。

context.Args().Get(0) 这一行代码在用户调用命令 init 并带有一个或多个额外参数时,用于获取命令行中紧随命令名 init 之后的第一个参数。

举个例子,假设用户在命令行中执行了如下命令:

./myapp init mycontainer

在上述代码段中,context.Args().Get(0) 将返回字符串 "mycontainer"。这是因为context.Args() 返回的是一个包含所有非命令名称参数的字符串切片,索引0位置的元素就是第一个参数。

然而,根据这段代码的具体上下文,看起来开发者并未真正使用到 cmd 变量(它保存了第一个参数的值),而是直接调用了 contain.init() 方法初始化容器。如果预期是利用第一个参数来定制初始化行为,那么应该会在 contain.init() 调用中使用到 cmd 变量。

syscall.Exec和os/exec.Command

在Go语言中,syscall.Execos/exec.Command 都是用来执行外部程序的,但它们在用途和机制上有显著的不同:

  1. syscall.Exec

    • syscall.Exec 是一个低级别的系统调用,直接在当前进程中替换当前程序的映像和数据,执行新的程序。
    • syscall.Exec 成功执行后,原始程序的内存空间、文件描述符等资源将被新程序接管,原始程序不会恢复执行,因为它已经被完全替换掉了。
    • 通常用在那些希望彻底转换当前进程上下文而不返回的情况。
  2. os/exec.Command

    • os/exec.Command 提供了一种高级别的、更易于使用的API来执行外部程序,并且它不会替换当前进程的内容。
    • exec.Command 创建并返回一个 *Cmd 类型的结构体,调用其 Run() 方法来执行外部程序,执行完成后,当前Go程序会继续执行后续代码。
    • 你可以通过 Output()CombinedOutput() 等方法获取子进程的输出,并且可以控制子进程的标准输入、输出和错误流。
    • 如果需要在同一进程中连续执行多个外部命令,或者需要处理子进程的输出结果,那么 os/exec 库是非常适合的。

简单来说,syscall.Exec 更像是进程自身的“变身”,而 os/exec.Command 则是在当前进程中启动一个新的子进程来进行任务,并且可以对子进程进行更多的控制和通信。

syscall.Exec

在Go语言中,当程序执行到 syscall.Exec 函数时,会发生以下情况:

  1. 当前进程替换
    syscall.Exec 函数负责执行一个新的程序,同时替换当前进程的映像和内存空间。这意味着当前执行的Go程序的所有代码和数据都会被新的程序所取代,不再有任何机会回到原来的Go程序执行流中。

  2. 资源继承
    新程序将继承当前进程的PID(进程ID)、打开的文件描述符以及其他一些进程相关的资源。这意味着新程序可以从原先的进程中接过控制权,例如继续监听同一套网络端口或读写已打开的文件。

  3. 程序执行
    syscall.Exec 函数接受一系列参数,包括要执行的程序路径、命令行参数和环境变量。新程序将以指定的方式执行,就像它是由shell或其他启动器直接启动的一样。

  4. 永不返回
    一旦 syscall.Exec 成功执行了新程序,原Go程序的代码将不再被执行,并且 syscall.Exec 函数本身也不会返回——因为它已经不再是那个进程的一部分了。如果 syscall.Exec 失败(例如由于找不到要执行的程序或权限问题),它会返回一个错误,但这种情况下的“返回”实际上已经是当前进程内的紧急错误处理逻辑了,而不是原Go程序的逻辑继续执行。

  5. 用途
    syscall.Exec 通常用于服务程序升级、执行子进程并替换自己以进入不同的执行环境等情况,或者是不需要原程序继续运行的场景。相比之下,如果你想在当前进程中启动一个子进程并与其交互或等待子进程结束后继续执行,应该使用 os/exec 包中的 CommandRun 等方法。

  • syscall.Exec(command, argv, os.Environ()) 的作用是:

    • command:这是要执行的新程序的路径或者可执行文件名。
    • argv:这是一个字符串切片,包含传递给新程序的命令行参数。在这个例子中,只有 command 一个参数,但实际上可以有多个,比如 argv := []string{command, arg1, arg2, ...}
    • os.Environ():这个函数返回当前进程的环境变量列表,格式为 key=value 的字符串切片。在 syscall.Exec 中,这个参数用于传递给新程序,使其继承当前进程的环境变量。

mountflags

  1. syscall.MS_NOEXEC
    设置此标志后,挂载的文件系统上的所有文件都无法被执行,即使是可执行文件。这是一项重要的安全措施,可以阻止恶意或误操作尝试执行存储在特定文件系统分区中的程序。例如,在只用于数据存储的分区上启用此选项可以增强安全性,确保即便有潜在恶意脚本或程序也不得运行。

  2. syscall.MS_NOSUID
    当挂载文件系统时带有此标志,系统会忽略文件的Set-User-ID (SUID) 和 Set-Group-ID (SGID) 位的效果。在Linux中,SUID和SGID权限通常允许用户在执行某个文件时暂时获取文件所有者的权限或执行该文件时的组权限。禁用SUID和SGID意味着,无论文件本身有何种特殊权限设定,进程在执行文件时不会继承文件所有者的用户ID或组ID,从而限制了可能因滥用特权而导致的安全风险。

  3. syscall.MS_NODEV
    使用此标志挂载文件系统时,系统将不允许任何设备特殊文件在该文件系统上工作。设备文件是用来访问硬件设备(如硬盘、终端、打印机等)的接口。禁止设备文件意味着无法在该挂载点创建或访问设备节点,这对于防止未经授权的设备访问以及增强隔离环境的安全性非常有用,比如在沙箱环境中或对安全性要求较高的文件系统分区。

syscall.Mount

syscall.Mount 是 Go 语言标准库 syscall 包中提供的一个函数,用于直接调用操作系统的系统调用来完成文件系统的挂载操作。在Linux环境下,这个函数对应的就是Linux内核提供的mount(2)系统调用接口。

函数签名一般如下所示(可能会根据不同Go版本有所变化):

func Mount(source string, target string, fstype string, flags int, data string) error

参数含义:

  • source:待挂载的文件系统源,它可以是设备名(如 /dev/sda1)、网络共享路径或者其他形式,对于虚拟文件系统如 proc,则是一个标识符字符串。

  • target:挂载点,即目标挂载路径,通常是一个已经存在的目录。

  • fstype:文件系统的类型名称,如 ext4ntfs 或者像上面提到的 proc 这样的特殊文件系统类型。

  • flags:挂载标志,是一个整数值,由多个标志位组合而成,例如 syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV,这些标志位用于控制挂载行为的不同属性。

  • data:可选的额外挂载选项,通常是一个字符串,格式取决于所使用的文件系统类型,对于一些文件系统,可能包含挂载选项的列表或其他特定信息,对于不需要额外数据的文件系统,可以传入空字符串 ""

函数执行的结果是成功或失败,若失败则返回一个错误对象。

在给出的例子中:

syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")

这是在挂载Linux的虚拟文件系统 /proc/proc 目录下,并且使用了 defaultMountFlags 中定义的一系列安全相关的挂载标志。

请注意,只有包中首字母大写的函数(public函数)才能被其他包调用。首字母小写的函数(private函数)只能在包内部使用

代码

https://github.com/FULLK/llkdocker/tree/main/run_docker

最终效果

在这里插入图片描述
在这里插入图片描述

问题

发现只能run一次,下次就出现在这里插入图片描述
有待解决

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/526965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

X86汇编速成

平时用的电脑都是X86的,但是现在大家都在搞RISC-V,计组也都开始以RISC-V作为示例,所以专门回头来补一下X86的汇编,方便平时使用。 寄存器register X86_64中一共有16个64位的通用寄存器,分别为: RAX, RBX,…

分享一款嵌入式开源按键框架代码工程MultiButton

目录 1 工程简介 2 工程代码分析 3 工程代码应用 4 思考 1 工程简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块。 Github地址:https://github.com/0x1abin/MultiButton 这个项目非常精简,只有两个文件: (1&am…

前端layui自定义图标的简单使用

iconfont-阿里巴巴矢量图标库 2. 3. 4.追加新图标 5.文件复制追加新图标

OSPF实验

需求: 1、R1-R3为区域0,R3到R4为区域1;其中R3的环回也在区域0,P1-R3分别有一个环回接口 2、R1-R3 R3为DR设备,没有BDR 3、R4环回地址已固定,其他所有网段使用192.168.1.0/24进行合理分配 4、R4环回不能…

鸿蒙ArkUI声明式学习:【UI资源管理】

OpenHarmony 应用的资源分类和资源的访问以及应用开发使用的像素单位以及各单位之间相互转换的方法。 资源分类 移动端应用开发常用到的资源比如图片,音视频,字符串等都有固定的存放目录,OpenHarmony 把这些应用的资源文件统一放在 resourc…

Golang中的上下文-context包的简介及使用

文章目录 简介context.Background()上下文取消函数上下文值传递建议Reference 简介 Go语言中的context包定义了一个名为Context的类型,它定义并传递截止日期、取消信号和其他请求范围的值,形成一个链式模型。如果我们查看官方文档,它是这样说…

6.10物联网RK3399项目开发实录-驱动开发之SPI接口的使用(wulianjishu666)

嵌入式实战开发例程,珍贵资料,开发必备: 链接:https://pan.baidu.com/s/1149x7q_Yg6Zb3HN6gBBAVA?pwdhs8b SPI 使用 SPI 简介 SPI 是一种高速的,全双工,同步串行通信接口,用于连接微控制器、…

拥有自己的云环境-域名及备案

序 唠叨两句 之前的文章,讲了如何购买一台云服务器,然后购买之后,如何操作云服务器。当买完云服务器之后,我们就可以使用云服务器提供的公网ip,访问到我们的服务器上。但是,这样怎么能体现我们一个老程序…

第十四届蓝桥杯岛屿个数

题目描述: 小蓝得到了一副大小为 MN 的格子地图,可以将其视作一个只包含字符 0(代表海水)和 1(代表陆地)的二维数组,地图之外可以视作全部是海水,每个岛屿由在上/下/左/右四个方向上…

使用 AI 生成正则表达式,告别正则烦恼

如果你有处理正则表达式的需求,那么这个网站(autoregex.xyz)一定要收藏好。 可以根据文字描述生成正则表达式。 默认是从文字到正则,不用选择。 输入框中输入描述,点击 ”GO“ 按钮。 等待一会儿,即可生…

测试开发面经(Flask,轻量级Web框架)

1. Flask的核心特点 a. 轻量级:核心简洁,只提供了基本的功能,其他高级功能可以通过插件或扩展来添加。 b. 灵活性:允许开发者选择适合自己项目的组件和工具,没有强制的项目结构和设计模式。 c. 易于扩展:提…

搭建python编译环境

目录 1.安装依赖包 2.安装失败进行换源 3. 更新系统 通过C 语言调用 Python 代码,需要先安装 libpython3 的 dev 依赖库(不同的 ubuntu 版本下, python 版本 可能会有差异, 比如ubuntu 22.04 里是 libpython3.10-dev &#xff09…

javaScript手写专题——实现instanceof/call/apply/bind/new的过程/继承方式

目录 原型链相关 手写instanceof 实现一个_instance方法,判断对象obj是否是target的实例 测试 手写new的过程 实现一个myNew方法,接收一个构造函数以及构造函数的参数,返回构造函数创建的实例对象 测试myNew方法 手写类的继承 ES6&…

人民大学:揭示大语言模型事实召回的关键机制

引言:大语言模型事实召回机制探索 该论文深入研究了基于Transformer的语言模型在零射击和少射击场景下的事实记忆任务机制。模型通过任务特定的注意力头部从语境中提取主题实体,并通过多层感知机回忆所需答案。作者提出了一种新的分析方法,可…

dll文件丢失怎么恢复,教你5种简单有效的方法

在计算机系统的运行过程中,动态链接库(DLL)文件扮演着至关重要的角色。它们作为共享函数库,封装了大量的可重用代码,使得多个应用程序能够高效调用并执行特定功能,极大地节省了系统资源,提升了软…

Arduino开发 esp32cam+opencv人脸识别距离+语音提醒

效果 低于20厘米语音提醒字体变红 QQ录屏20240406131651 Arduino代码 可直接复制使用&#xff08;修改自己的WIFI) #include <esp32cam.h> #include <WebServer.h> #include <WiFi.h> // 设置要连接的WiFi名称和密码 const char* WIFI_SSID "gumou&q…

指针的深入理解(六)

指针的深入理解&#xff08;六&#xff09; 个人主页&#xff1a;大白的编程日记 感谢遇见&#xff0c;我们一起学习进步&#xff01; 文章目录 指针的深入理解&#xff08;六&#xff09;前言一. sizeof和strlen1.1sizeof1.2strlen1.3sizeof和strlen对比 二.数组名和指针加减…

动态代理

动态代理 动态代理和静态代理角色一致。 代理类是动态生成的,不是我们直接写好的。 动态代理分为俩大类:基于接口的动态代理、基于类的动态代理 基于接口:JDK动态代理(以下示例就是这个) 基于类:cglib java字节码实现:javasist JDK动态代理 InvocationHandler Proxy …

C语言从入门到实战————编译和链接

目录 前言 1. 翻译环境和运行环境 2. 翻译环境 2.1 预处理&#xff08;预编译&#xff09; 2.2 编译 2.2.1 词法分析&#xff1a; 2.2.2 语法分析 2.2.3 语义分析 2.3 汇编 2.4 链接 3. 运行环境 前言 编译和链接是将C语言源代码转换成可执行文件的必经过程&a…

分公司=-部门--组合模式

1.1 分公司不就是一部门吗&#xff1f; "我们公司最近接了一个项目&#xff0c;是为一家在全国许多城市都有分销机构的大公司做办公管理系统&#xff0c;总部有人力资源、财务、运营等部门。" "这是很常见的OA系统&#xff0c;需求分析好的话&#xff0…