Linux定时器
https://www.computerhope.com/unix/ucrontab.htm#command-entries
使用systemd-run 替换
# systemd-run --on-active=30 /bin/touch /tmp/foo
# systemd-run --on-active="12h 30m" --unit some-unit.service
https://www.computerhope.com/unix/ucrontab.htm#command-entries
# systemd-run --on-active=30 /bin/touch /tmp/foo
# systemd-run --on-active="12h 30m" --unit some-unit.service
docker 有3种方式使用系统存储:
-v dir1:dir2
。将宿主机的目录dir1挂载到容器的dir2目录,实际执行的mount -o bind
类似命令--mount type=tmpfs,destination=/dir
, tmpfs是退出即释放的一种文件系统docker使用的文件系统经过很多变化,而且在各发行版下可能不同,但目前主流的是overlay2,执行docker info
查看当前使用的是overlay2
sudo docker info | grep Storage
Storage Driver: overlay2
除了overlay2,还有aufs(ubuntu),devicemapper(centos),btrfs和zfs。他们的实现都不同,都能支持分层和支持写时复制(Cow/copy-on-write),而他们实现的方式有区别,所以效率也有区别
而容器就是在镜像顶层压栈
了一个可写
层,而且是临时的,当容器销毁时,这层的文件也会删除
写时复制 copy-up 会导致第一次写时造成延迟,特别是大文件,拷贝起来费时。 但第二次就不会延时, 而且overlay2 有caching, 相比其它文件系统,更减少延时
touch /var/lib/rpm/*
当运行docker容器时查看挂载
overlay on /var/lib/docker/overlay2/04ea1faa8074e5862f40eecdba968bd9b7f222cb30e5bf6a0b9a9c48be0940f2/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/B74PWZCBMRCWXFH5UL2ZXB5WEU:/var/lib/docker/overlay2/l/WNHICVPVSDNUGSCZW435TPSMOK,upperdir=/var/lib/docker/overlay2/04ea1faa8074e5862f40eecdba968bd9b7f222cb30e5bf6a0b9a9c48be0940f2/diff,workdir=/var/lib/docker/overlay2/04ea1faa8074e5862f40eecdba968bd9b7f222cb30e5bf6a0b9a9c48be0940f2/work)
在保存容器后(docker commit),会多一层,里面包含了修改的文件,以及删除后生成的without文件,然后生成镜像
但对于以下特殊目录文件不会提交, 因为这些文件是运行时docker 要根据用户配置进行修改的。
例如docker 的link选项,会在容器的hosts 文件里定义对应的容器名->容器ip
.
├── A
│ ├── aa
│ └── a.txt
├── B
│ ├── a.txt
│ └── b.txt
├── C
│ └── c.txt
└── worker
└── work [error opening dir]
sudo mount -t overlay overlay -o lowerdir=A:B,upperdir=C,workdir=worker /tmp/test/
/tmp/test/
├── aa
├── a.txt
├── b.txt
└── c.txt
mount | grep 'overlay'
overlay on /tmp/test type overlay (rw,relatime,lowerdir=A:B,upperdir=C,workdir=worker)
https://docs.docker.com/storage/storagedriver/
遇到过一个面试题, 以下程序打印几个'A'
#include <stdio.h>
#include <unistd.h>
int main() {
printf("A");
fork();
printf("A");
}
#include <stdio.h>
#include <unistd.h>
int main() {
printf("A\n");
fork();
printf("A");
}
#./a.out > r.log
#cat r.log
分为无缓冲、行缓冲和全缓冲
发现有些程序可以输出到屏幕,重定向到文件后,执行ctl+c
退出后却没有任何内容,例如以下
#include <stdio.h>
#include <unistd.h>
int main(void) {
while (1) {
printf("Hello World\n");
sleep(1);
}
}
#./a.out > r.log
while read line ;do
#or while read -r line ;do
echo $line
done < $1
cat $1 | while read line ;do
echo $line
done
使用for时,结果略有不同, for以空格为一行
for line in $(cat $1) ;do
echo $line
done
https://bash.cyberciti.biz/guide/Reads_from_the_file_descriptor_(fd)
在debian上,之前一直用curlfs工具,将远程目录mount到本地目录。系统升级之后发现这个包没有了, 原来因为ftp不安全,所以现在推荐sshfs。
https://www.linuxtechi.com/configure-sftp-chroot-debian10/
# accept tupe and list
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n
print(sum)
calc([1,2])
calc((1,2,3))
# accept varidict argument
def calc2(*numbers):
sum = 0
for n in numbers:
sum = sum + n
return sum
calc2(0)
calc2(1,2,3,4)
numbers = [1,2,3]
# transform list to varidcit argument
print(calc(*numbers))
# varidict named arguments will be transformed to dict
# only accept named varidict argument
def person2(**kw):
if 'hello' in kw:
passs
print(kw)
# accept compose argument in sequence: normal argument & named varidict argument
def person(name, age, **kw):
print('name', name, 'age', age, 'other', kw)
person('mike',12)
person('mike',12, city='sh')
person('mike',12, city='sh', birth=1990)
person2(name='mike',age = 12, city='sh', birth=1990)
# compose normal type argument, varidict type argument, and varidict named type argument
def f2(a, b, c=0, *d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
cs=[3,4]
f2(1,2,*cs,hello='hello')
# use * to sepcify named type argument
def f3(a,b,c,*,d,e):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'e =', e)
# don't accept other named argument
f3(1,2,3,d=4,e=5)
# use tupe and dict can call all kind of function
# so *arg and **kw are always used to forward all argument to another function of any type of argument
args = (1,2,3)
kw = {'c':1,'d':2}
f2(*arg, **kw)
# at last, use base type for default value
Linux的命令行是非常强大的生产工具,现实中的问题好多可以不写复杂的编程代码而通过命令来解决, 而且不过几行代码而已
当执行which的时候,比如which echo
会输出'shell built-in command'而不是返回路径,就说明这个命令是内建命令
shell在执行内建命令时是直接执行,而非内建命令则fork一个子进程来执行
xargs可以将输入多进程并发处理,配合find
非常厉害。 配合wget就是一个并发爬虫了
EXAMPLES
find /tmp -name core -type f -print | xargs /bin/rm -f
Find files named core in or below the directory /tmp and delete them. Note that this will work incorrectly if there are any filenames containing newlines or spaces.
find /tmp -name core -type f -print0 | xargs -0 /bin/rm -f
Find files named core in or below the directory /tmp and delete them, processing filenames in such a way that file or directory names containing spaces or newlines are correctly
handled.
find /tmp -depth -name core -type f -delete
Find files named core in or below the directory /tmp and delete them, but more efficiently than in the previous example (because we avoid the need to use fork(2) and exec(2) to
launch rm and we don't need the extra xargs process).
cut -d: -f1 < /etc/passwd | sort | xargs echo
Generates a compact listing of all the users on the system.
find . -exec grep chrome {} \;
find . -exec grep chrome {} +
ls 本来是行分隔的 awk将ls的换行符变为|分割
ls | awk '{ ORS="|"; print; }'
echo $(ls)
则把ls的换行符变为空格
declare可以指定变量的类型'-i'为整形,'-r'制度(等同shell的readonly),'-g'全局变量
shell默认是字符串,使用'-i'后,数学运算不需要let
#retry=0
declare -i retry=0
while [ $retry -lt 30 ] ; do
ps aux --cols=1024 | grep xxx
if [ $? -eq 0 ] ; then
exit 0
fi
sleep 1
#let retry=$retry+1
retry=$retry+1
done
因为很多函数需要处理error, 当存在全局变量时,可能会覆盖全局变量
var global_var int = 1
func foo(){
// 此时覆盖了全局变量
global_var, err := os.OpenFile("file")
}
var global_var int = 1
func foo(){
var err error
global_var, error = os.OpenFile("file")
}
数组和slice使用上如同c++里的std::array和std::vector, 数组的长度固定,slice长度可以2倍扩容
但有传参上有大区别: golang中数组是值传递,相当于将整个数组复制一遍,而slice是引用传递,所以slice用得多,map同理
定义数组和slice:
// 数组
var a [3] int
arr := [5] int {1,2,3,4,5}
var array2 = [...]int {6,7,8}
q := [...] int {1,2,3} // 省略长度
q2 := [...] int {99, -1} // 前99项为0,第100个元素是-1
// slice
s1 := []int{1, 2, 3}
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} //a是数组
s2 := a[2:8]
s3 := make([]int, 10, 20)
两者很多时候可以相互替换,但是在不同类型上,有区别
对于基本类型string, int, boolean, 数组,var声明
即初始化
var str string // 自动初始化为空字符串
var inter int // 自动初始化为0
var bo bool // 自动初始化为false
// 可以直接使用
fmt.Printf("%s %d %t", str, inter, bo)
而对于slice, map, chann类型而言, 使用var m map[int]string
只是声明,需要再用make
来获得内存和初始化
var m map[int] string // 此时不能使用m
m = make(map[int] string){1:"a",2:"b"}
fmt.Println(m[1])
m := make(map[int] string){1:"a", 2:"b"}
m := map[int] string{1:"a", 2:"b"}
存储周期类型: 有关变量的创建和销毁 链接类型: 有关变量函数的内存位置 作用域: 有关变量函数的可见范围
本文讨论的标识符,包括变量和函数
存储说明符
控制变量何时分配和释放,有以下几种
说明 - automatic: 最常见的局部变量,且没有声明为static或者thread_local,位于栈上, 随着代码块的执行和结束而自动分配和销毁 - static: 静态变量, 在程序启动和结束时创建和销毁,但初始化是在第一次执行初始化代码时执行 - thread: 在线程开始和结束时分配和销毁 - dynamic: 最常见的堆上的变量, 需要执行new和delete,
auto 在c++11中不是声明存储周期,而是类型推导符, 但这种存储周期类型的依然存在(局部变量)
标识符(变量&函数)用一块内存里的值或者函数体来表示的, 而linkage决定其他相同的标识符是否指向同一块内存。c/c++有3种linkage, no-linkage, internal linkage和external linkage
static
声明internal linkage,所以允许在不同文件声明两个名称&类型相同的internal linkage 标识符,他们指向不同的内存单元。 static
没有影响extern
声明这个变量,就能使用指向同一内存的变量extern
声明这个函数(可省),就能使用指向同一内存的函数可见static
和extern
即表示存储周期,又表示linkage, static相对简单,extern则比较复杂,如以下情况
int g_x = 1; // 定义有初始化的全局变量(可加可不加extern)
int g_x; // 定义没有初始化的全局变量(不可加extern),可选初始化
extern int g_x; // 前置声明一个全局变量,不可初始化
extern const int g_y { 1 }; // 定义全局常量,const必须初始化
extern const int g_y; // 前置声明全局常量,不可初始化
所以若是定义未初始化的全局变量,不能加extern,不然就成了前置声明了。
虽然通过给constexpr添加extern
修饰符来让其具备external属性,但不能在其他文件前置声明。因为constexpr是在编译期替换的,编译器(compile)的可见域限定在文件内,所以编译期无法知道constexpr的值,所以在编译期无法获取到其内存单元的值, 也就无法在其他文件进行声明,只能定义。
局部变量的scope、no-linkage以及duration相同,从{
开始到}
结束。 理论上global scope涵盖了file scope。而linkage来规定其是否能在其他文件里使用。
local class 不允许有static data member
https://en.cppreference.com/w/cpp/language/storage_duration
系统原厂商是不喜欢讨论系统调优的,一方面说起来没完没了,二来比较复杂,而且私以为调优即说明系统默认不够好?
而且SUSE的原厂规定:
原理机制的介绍及系统调优并不在我们的技术支持范畴
这里是一点相关介绍
buffer是用于存放将要输出到disk(块设备)的数据,而cache是存放从disk上读出的数据。二者都是为提高IO性能而设计的。
- buffer:缓冲将数据缓冲下来,解决速度慢和快的交接问题;速度快的需要通过缓冲区将数据一点一点传给速度慢的区域。
例如:从内存中将数据往硬盘中写入,并不是直接写入,而是缓冲到一定大小之后刷入硬盘中。
A buffer is something that has yet to be "written" to disk.
总之buff和cache都是内存和硬盘之间的过渡,前者是写入磁盘方向,而后者是写入内存方向
drop_caches回收一下。
#sync;sync;sync
#echo 3 > /proc/sys/vm/drop_caches
Swap意思是交换分区,是硬盘中的一个分区。内核将内存Page移出内存到swap分区(swap out)
swap通过 vm.swappiness 这个内核参数控制,默认值是60。cat /proc/sys/vm/swappiness
可以查看当前值
这个参数控制内核使用swap的优先级。该参数从0到100。
设置该参数为0,表示只要有可能就尽力避免交换进程移出物理内存;
设置该参数为100,这告诉内核疯狂的将swapout物理内存移到swap分区。
注意:设置该参数为0,并不代表禁用swap分区,只是告诉内核,能少用到swap分区就尽量少用到,设置vm.swappiness=100的话,则表示尽量使用swap分区。
这里面涉及到当然还涉swappiness及到复杂的算法。如果以为所有物理内在用完之后,再使用swap, 实事并不是这样。以前曾经遇到过,物理内存只剩下10M了,但是依然没有使用Swap交换空间,另外一台服务器,物理内存还剩下15G,居然用了一点点Swap交换空间。 其实少量使用Swap交换空间是不会影响性能,只有当内存资源出现瓶颈或者内存泄露,进程异常时导致频繁、大量使用交换分区才会导致严重性能问题。
这个问题如上面说的,比较难说,理论上是当物理内存不够用的时候,又需要读入内存时,会将一些长时间不用的程序的内存Page 交换出去。
但是很多时候会发现,内核即使在内存充足的情况下也是使用到swap
可以看下面的测试
swapoff 之后执行sudo sysctl vm.swappiness=0
临时让内核不用swapout
并把swap的数据加载内存,并重启swap
#swapoff -a
#swapon -a
5.10.0-8-amd64
total used free shared buff/cache available
Mem: 12162380 4911564 5605744 459364 1645072 6466572
Swap: 1000444 763040 237404
重启swap后
total used free shared buff/cache available
Mem: 12162380 5605800 4843176 524984 1713404 5707112
Swap: 1000444 0 1000444
可见,停用swap后,swap的used大部分到了mem的used
,小部分到了Mem的shared
perf + flame火焰图: 查看运行耗时,可以查看函数调用耗时,如果是自己的程序,可以知道哪些函数需要优化
vmstat 查看磁盘io情况,使用vmstat -t 3
命令,如果b状态的数字一直很大,那么说明磁盘阻塞严重,可能是磁盘坏了,可能是程序设计不合理
还有top,iperf等等