Go Signal信号处理
参考: [Go Signal信号处理]((17条消息) Go Signal信号处理_无风的雨-CSDN博客_go signal)
可以先参考官方资料[go.signal](signal package - os/signal - pkg.go.dev)
前言
信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方式。对于Linux系统来说,信号就是软中断,用来通知进程发生了异步事件。
当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。
有时候我们想在Go程序中处理Signal信号,比如收到SIGTERM信号后优雅的关闭程序,以及 goroutine结束通知等。
Go 语言提供了对信号处理的包(os/signal)。
Go 中对信号的处理主要使用os/signal包中的两个方法:一个是notify
方法用来监听收到的信号;一个是 stop
方法用来取消监听。
Go信号通知机制可以通过往一个channel中发送os.Signal实现。
信号类型
各平台的信号定义或许有些不同。下面列出了POSIX中定义的信号。 Linux 使用34-64信号用作实时系统中。命令 man signal 提供了官方的信号介绍。在POSIX.1-1990标准中定义的信号列表
需要特别说明的是,SIGKILL和SIGSTOP这两个信号既不能被应用程序捕获,也不能被操作系统阻塞或忽略。
// main.go
package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
// 创建一个os.Signal channel
sigs := make(chan os.Signal, 1)
//创建一个bool channel
done := make(chan bool, 1)
//注册要接收的信号,syscall.SIGINT:接收ctrl+c ,syscall.SIGTERM:程序退出
//信号没有信号参数表示接收所有的信号
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
//此goroutine为执行阻塞接收信号。一旦有了它,它就会打印出来。
//然后通知程序可以完成。
go func() {
sig := <-sigs
fmt.Println(sig)
done <- true
}()
//程序将在此处等待,直到它预期信号(如Goroutine所示)
//在“done”上发送一个值,然后退出。
fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}
执行 go run main.go 再敲入 ctrl+c,程序会输出
单例模式
简介
单例模式是指在内存中只会创建一次对象的设计模式,在程序中多次使用同一个对象且作用相同的时候,为了防止频繁的创建对象,单例模式可以让程序在内存中创建一个对象,让所有调用者都共享这一单例对象。
几种单例模式的demo
线程安全的单例模式
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
使用
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
# <__main__.Singleton object at 0x7fc6c6a57898> <__main__.Singleton object at 0x7fc6c6a57898>
多线程使用
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
# <__main__.Singleton object at 0x7fc6c6a57898>
片选低电平有效:
主要是为了降低功率。选中信号输出时,地址译码器输出端为低电平,此时译码器不输出功率;选中信号没有输出(不选中)时,译码器输出端为高阻状态,输出消耗功率也为0。因此芯片的CS信号采用低电平有效可以最大程度减小片选控制的功率消耗。
此外,低电平有效也可以最大程度地减小干扰和保证控制的可靠性。低电平有效时,外部的任何干扰都不能进入被控制的芯片,因而保证芯片的可靠工作。这样在干扰信号能够进入芯片时是在芯片不工作时(片选无效),也就是说,干扰信号对芯片的工作没有影响。而若采用高电平有效,在芯片工作时不要说外部干扰信号能够很容易地进入芯片干扰,造成各种误动作,就连电源的任何波动都可能影响芯片的正常工作。
在 Docker 内部以及容器之间管理数据,在容器中管理数据主要有两种方式:
- 数据卷(Data Volumes)
- 挂载主机目录 (Bind mounts)
数据卷
数据卷
是一个可供一个或多个容器使用的特殊目录,它绕过UFS
,可以提供很多有用的特性:
- 数据卷 可以在容器之间共享和重用
- 对 数据卷 的修改会立马生效
- 对 数据卷 的更新,不会影响镜像
- 数据卷 默认会一直存在,即使容器被删除
注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。
选择 -v 还是 -–mount 参数: Docker 新用户应该选择–mount参数,经验丰富的 Docker 使用者对-v或者 –volume已经很熟悉了,但是推荐使用–mount参数。
docker volume create my-vol # 创建一个数据卷\\
docker volume rm my-vol
docker volume ls
local my-vol
查看数据卷的信息
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
启动一个挂载数据卷的容器:在用docker run命令的时候,使用–mount标记来将 数据卷 挂载到容器里。在一次docker run中可以挂载多个 数据卷。下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /webapp 目录。
最简单的使用tar的命令:
# 压缩
tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称
# 查询
tar -jtv -f filename.tar.bz2
# 解压缩
tar -jxv -f filename.tar.bz2 -C 欲解压缩的目录
最简单的使用tar的命令:
# 压缩
tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称
# 查询
tar -jtv -f filename.tar.bz2
# 解压缩
tar -jxv -f filename.tar.bz2 -C 欲解压缩的目录
标准输入重定向
1>: 以覆盖的方法将「正确的数据」输出到指定的文件或装置上;
1>>:以追加的方法将「正确的数据」输出到指定的文件或装置上;
2>: 以覆盖的方法将「错误的数据」输出到指定的文件或装置上;
2>>:以追加的方法将「错误的数据」输出到指定的文件或装置上;
要注意:「1»」以及「2»」中间无空格。只有「>」代表1。
将正确与错误的输出分别定向到不同的文件中
使用范例:
[root@centos ~]# find /home -name .bashrc > list_rigth 2> list_err
此时屏幕不会出现任何信息,因为刚刚执行的结果中,标准正确输出被重定向到了list_right
文件中,而标准错误输出被重定向到了list_err
文件中。
将正确的输出在屏幕上显示,错误输出不显示
利用黑洞 /dev/null
,这个东西可以吃掉任何导向这个设备的数据。
[root@centos ~]# find /home -name .bashrc 2> /dev/null
/home/gc/.bashrc
/home/lex/.bashrc
/home/www/.bashrc
正确与错误的均写入同一个文件
[root@centos ~]# find /home -name .bashrc >list 2>&1
&&与||
指令下达 | 说明 |
---|---|
cmd1&&cmd2 |
1.若cmd1执行完毕且正确执行($?=0 ),则开始执行cmd2. 2. 若cmd1执行完毕且为错误($?!=0 ),则cmd2不执行。 |
cmd1||cmd2 |
1.若cmd1执行完毕且正确执行,则cmd2不执行。2.若cmd1执行完毕且错误,则开始执行cmd2. |
命令格式:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Usage: Run a command in a new container
中文意思为:通过run命令创建一个新的容器(container)
- 常用选项说明
d, --detach=false
, 指定容器运行于前台还是后台,默认为falsei, --interactive=false
, 打开STDIN,用于控制台交互t, --tty=false
, 分配tty设备,该可以支持终端登录,默认为falseu, --user=""
, 指定容器的用户a, --attach=[]
, 登录容器(必须是以docker run -d启动的容器)w, --workdir=""
, 指定容器的工作目录c, --cpu-shares=0
, 设置容器CPU权重,在CPU共享场景使用e, --env=[]
, 指定环境变量,容器中可以使用该环境变量m, --memory=""
, 指定容器的内存上限P, --publish-all=false
, 指定容器暴露的端口p, --publish=[]
, 指定容器暴露的端口h, --hostname=""
, 指定容器的主机名v, --volume=[]
, 给容器挂载存储卷,挂载到容器的某个目录-volumes-from=[]
, 给容器挂载其他容器上的卷,挂载到容器的某个目录-cap-add=[]
, 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities-cap-drop=[]
, 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities-cidfile=""
, 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法-cpuset=""
, 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU-device=[]
, 添加主机设备给容器,相当于设备直通-dns=[]
, 指定容器的dns服务器-dns-search=[]
, 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件-entrypoint=""
, 覆盖image的入口点-env-file=[]
, 指定环境变量文件,文件格式为每行一个环境变量-expose=[]
, 指定容器暴露的端口,即修改镜像的暴露端口-link=[]
, 指定容器间的关联,使用其他容器的IP、env等信息-lxc-conf=[]
, 指定容器的配置文件,只有在指定–exec-driver=lxc时使用-name=""
, 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字-net="bridge"
, 容器网络设置:- bridge 使用docker daemon指定的网桥
- host //容器使用主机的网络
- container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
- none 容器使用自己的网络(类似–net=bridge),但是不进行配置
-privileged=false
, 指定容器是否为特权容器,特权容器拥有所有的capabilities-restart="no"
, 指定容器停止后的重启策略:- no:容器退出时不重启
- on-failure:容器故障退出(返回值非零)时重启
- always:容器退出时总是重启
-rm=false
, 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)-sig-proxy=true
, 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理
示例
- 运行一个在后台执行的容器,同时,还能用控制台管理:
docker run -i -t -d ubuntu:latest
- 运行一个带命令在后台不断执行的容器,不直接展示容器内部信息:
docker run -d ubuntu:latest ping www.docker.com
- 运行一个在后台不断执行的容器,同时带有命令,程序被终止后还能重启继续跑,还能用控制台管理,
docker run -d --restart=always ubuntu:latest ping www.docker.com
- 为容器指定一个名字,
docker run -d --name=ubuntu_server ubuntu:latest
- 容器暴露80端口,并指定宿主机80端口与其通信(: 之前是宿主机端口,之后是容器需暴露的端口),
docker run -d --name=ubuntu_server -p 80:80 ubuntu:latest
- 指定容器内目录与宿主机目录共享(: 之前是宿主机文件夹,之后是容器需共享的文件夹),
docker run -d --name=ubuntu_server -v /etc/www:/var/www ubuntu:latest
主要依靠的一个开源软件runlike
See runlike-github
yum clean all
yum makecache
yum install python-pip python-setuptools -y
pip install runlike
# 若此时正在运行的一个容器为`redis`
# 则
runlike redis > redis_start.sh
# 此时redis_start.sh即为docker启动redis容器的命令
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过-P
或-p
参数来指定端口映射。
当使用-P
标记时,Docker会随机映射一个端口到内部容器开放的网络端口。
使用docker container ls
可以看到,本地主机的32768被映射到了容器的80端口。此时访问本机的32768端口即可访问容器内nginx的默认页面。
$ docker run -d -P nginx:alpine
$ docker container ls -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fae320d08268 nginx:alpine "/docker-entrypoint.…" 24 seconds ago Up 20 seconds 0.0.0.0:32768->80/tcp bold_mcnulty
同样的,可以通过docker logs
命令来查看访问记录。
$ docker logs fa
172.17.0.1 - - [25/Aug/2020:08:34:04 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0" "-"
-p
则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
映射所有接口地址
使用hostPort:containerPort
格式将本地的80端口映射到容器的80端口,可以执行
$ docker run -d -p 80:80 nginx:alpine
映射到指定地址的指定端口
可以使用ip:hostPort:containerPort
格式指定映射使用一个特定地址,比如localhost地址127.0.0.1