分类目录归档:技术笔记

Python 二进制结构化数据处理和封装

当 python 需要调用 C 程序,或是进行文件、网络操作时,需要对二进制结构化字节流进行处理,此时需要使用到 struct 这个模块提供的方法。

详细方法可以查看 官方教程,这里以 perlpack 作为对比,使用 python 实现类似 perl 数据打包的效果。

在 perl 的 pack 方法中,提供了一种 Z* 的写法,可以总是保证最后有一位空填充,在 python 中则可以这样实现:

# 类比 perl 的 pack "VVVVZ*", $max, 0, 0, 0, $user;
fmt = "<4lx" + str(len(user) + 1) + "s"  # 类比 perl Z*,总是保证最后有一位空填充
bindata = struct.pack(fmt, maxn, 0, 0, 0, bytes(user, encoding="utf8"))
# 小端序,4个 long (32位整) 后面跟 填充字节 ,然后再拼 字符长度 +1 个 s(字节串)

最后打印出来的效果是这样:

b'tasklist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00dev\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

如果直接使用 .format 或是字符拼接 + `.ljust(256, '\000') 之类的方法在后面强行补 也是可以实现类似的效果,但是打印出来还是字符对象,不是我希望的字节流对象,而且很繁琐,不专业:

'tasklist                                                                                                                                                                                                                                                        dev                                                                                                                                                                                                                                                             '

大概就是这样,像是中间塞了一堆空格。建议数据打包还是使用 struct.pack 来进行。

基本实现需求。

参考文献

Linux 下的文件锁

本文内容为 Linux 系统通用,各个语言实现可能稍有不同,但原理相同。

当多个进程或多个程序都想要修同一个文件的时候,如果不加控制,多进程或多程序将可能导致文件更新的丢失。

例如进程1和进程2都要写入数据到a.txt中,进程1获取到了文件句柄,进程2也获取到了文件句柄,然后进程1写入一段数据,进程2写入一段数据,进程1关闭文件句柄,会将数据flush到文件中,进程2也关闭文件句柄,也将flush到文件中,于是进程1的数据被进程2保存的数据覆盖了。

所以,多进程修改同一文件的时候,需要协调每个进程:

  • 保证文件在同一时间只能被一个进程修改,只有进程1修改完成之后,进程2才能获得修改权
  • 进程1获得了修改权,就不允许进程2去读取这个文件的数据,因为进程2可能读取出来的数据是进程1修改前的过期数据

这种协调方式可以通过文件锁来实现。

文件锁分类

文件锁分两种,

  • 独占锁(写锁)
  • 共享锁(读锁)

当进程想要修改文件的时候,申请独占锁(写锁),当进程想要读取文件数据的时候,申请共享锁(读锁)。

独占锁和独占锁、独占锁和共享锁都是互斥的。

只要进程1持有了独占锁,进程2想要申请独占锁或共享锁都将失败(阻塞),也就保证了这一时刻只有进程1能修改文件,只有当进程1释放了独占锁,进程2才能继续申请到独占锁或共享锁。

但是共享锁和共享锁是可以共存的,这代表的是两个进程都只是要去读取数据,并不互相冲突。

文件锁:flock 和 lockf

Linux上的文件锁类型主要有两种:flock和lockf。后者是fcntl系统调用的一个封装。它们之间有些区别:

  • flock来自BSD,而fcntl或lockf来自POSIX,所以lockf或fcntl实现的锁也称为POSIX锁
  • flock只能对整个文件加锁,而fcntl或lockf可以对文件中的部分加锁,即粒度更细的记录锁
  • flock的锁是劝告锁,lockf或fcntl可以实现强制锁。所谓劝告锁,是指只有多进程双方都遵纪守法地使用flock锁才有意义,某进程使用flock,但另一进程不使用flock,则flock锁对另一进程完全无限制
  • flock锁是附加在(关联在)文件描述符上的(见下文更深入的描述),而lockf是关联在文件实体上的。本文后面将详细分析flock锁在文件描述符上的现象

参考文献

Perl //= 和 ||= 的区别 | 附实验

结论

  • $var//=2:等价于 defined($var)||2,即 未定义 时才赋值为 2 ,否则不变( 即使是 空字符串
  • $var||=2 :除非定义且为 true 才不会赋值,否则赋值(比如 空字符串 时)为2。

//=

Step-1 空串

$var='';
$var//=2;
print "'$var'\n";
# perl atest4.pl 
''

Step-2 0

$var=0;
$var//=2;
print "'$var'\n";
# perl atest4.pl 
'0'

Step-3 1

$var=1;
$var//=2;
print "'$var'\n";
# perl atest4.pl 
'1'

Step-4 undef

$var=undef;
$var//=2;
print "'$var'\n";
# perl atest4.pl 
'2'

||=

Step-1 空串

$var='';
$var||=2;
print $var;
# perl atest4.pl 
2

Step-2 0

$var=0;
$var||=2;
print $var;
# perl atest4.pl 
2

Step-3 1

$var=1;
$var||=2;
print $var;
# perl atest4.pl 
1

Step-4 undef

$var=undef;
$var||=2;
print $var;
# perl atest4.pl 
2

Perl 常用内置函数 -r -e 等

-r: File is readable by effective uid/gid.
-w: File is writable by effective uid/gid.
-x: File is executable by effective uid/gid.
-o: File is owned by effective uid.

-R: File is readable by real uid/gid.
-W: File is writable by real uid/gid.
-X: File is executable by real uid/gid.
-O: File is owned by real uid.

-e: File exists.
-z: File has zero size (is empty).
-s: File has nonzero size (returns size in bytes).

-f: File is a plain file.
-d: File is a directory.
-l: File is a symbolic link.
-p: File is a named pipe (FIFO), or Filehandle is a pipe.
-S: File is a socket.
-b: File is a block special file.
-c: File is a character special file.
-t: Filehandle is opened to a tty.

-u: File has setuid bit set.
-g: File has setgid bit set.
-k: File has sticky bit set.

-T: File is an ASCII text file (heuristic guess).
-B: File is a "binary" file (opposite of -T).

-M: Script start time minus file modification time, in days.
-A: Same for access time.
-C: Same for inode change time (Unix, may differ for other platforms)

参考文献

什么是 WEB3 以及其发展概要

要问何为 Web3,就先从什么味 Web2 和 Web1 来说起吧。

最近在网上看到 Web3 的概念很火热,比肩区块链、元宇宙,其实貌似一整个未来,特意整理一下认为比较好的材料分享出来。

WEB 的进化

https://imagehost-cdn.frytea.com/images/2021/12/31/image83ab2e1e696f8c3e.png

截止目前,互联网技术大致进行过2次重要的革新,每个时代,都有其显著地时代特征。

WEB1 – 信息投喂

虽然我很不喜欢这个词,但貌似事实如此。

早在上世纪 90 年代互联网刚刚开始普及的时候,用户与网络的互动处于非常早期的阶段,即用户只能被动地浏览文本、图片以及简单的视频内容,网站提供什么,用户查看什么。几乎没有互动可言。

上世纪 90 年代中后期,在谷歌、百度发展之前的一段时间,互联网曾经由 AltaVista 和 Netscape 主宰。当时,互联网的存在只是为了宣传他们的实体公司。这些网站是「只读网站」,意味着你只能搜索和阅读信息。

https://imagehost-cdn.frytea.com/images/2021/12/31/image83876cc0f4e5dc0a.png

而那个时代的电子商务网站实际上跟现在超市发的促销手册一样,只是给你展示内容,你无法在网站上下单。

这就是 Web1.0,缓慢的 Web 1.0,完全受控制的 Web 1.0

WEB2 – 网上互动

当前我们所处的这个时代,大致可理解为 WEB2.0

这一代互联网被称为「Web2.0」或「读写」网络。现在,用户不仅仅是单纯的访问者,他们还可以创建自己的内容,并将其上传到网站上。以著名的视频博主「办公室小野」为例,她仅用了 2 年的时间就从一个默默无闻的视频作者变成了月收入数千万的视频大 V。

https://imagehost-cdn.frytea.com/images/2021/12/31/imagec1f47771a6aaaafa.png

有影响的网红只需要发一个视频就可以让一个餐馆排满长队,也可以用一句话让一家网店差评如潮。互联网人对于互联网的影响力与日俱增,以往网站给用户投喂信息的时代已经过去。

Web2.0 的主要目标是,使互联网更加民主,并尽可能地方便用户访问。

WEB3 – 未来互联网

至于什么是 WEB3 还很难有一个准确的定义,但可以总结这么几个关键词:

  • 统一身份认证
  • 数据确权与授权
  • 隐私保护与抗审查
  • 去中心化运行

在 WEB3.0 时代,身份认证有一个统一的平台,不再需要记录各个平台的账号密码,也不需要担心账号泄露;数据的确权与授权统一规范,不再是一家独大;个人隐私可以受到很好的保护,网站再也不能随意的跟踪用户行为;一切网络服务的运作,都是去中心化的。

至于具体改变了什么,未来将会是什么样子,可以想象。举个例子,某网盘再也不能随意的封禁用户数据,不可能因为新浪博客单方面的停止运营导致我们的博客内容无法访问,音乐资源不再局限于哪一家的版权,因为一切都是去中心化的。

当然改变越大,风险就越大,难度也越大。

怪不得最近总是看到有人说:不学习加密技术是自我毁灭

就像是有些人已经成了比特币首富,有些人连什么是比特币还不清楚。一些人已经在闷声发大财,有些人还在固有的道路上艰难前进。

变化

在将来,人们的使用习惯和生活方式可能会发生一些变化,比如现在常用 Chrome,未来可能使用 Brave。

https://imagehost-cdn.frytea.com/images/2021/12/31/image3dae6010ffd8d459.png

信息就是金钱,随着 Web 3.0的出现,新的平台将以一种不被垄断服务提供商扼杀的健康竞争水平出现。

这个概念是这样的: 目前,分散的应用程序、钱包、平台以及其他构成 Web 3.0的数字资产是分散的。访问这些接口需要单独的种子、登录和身份标识,这与现有的 Web 2.0非常相似。将通过一个种子将这些不同的平台连接在一起。因为这将作为一个加密的钥匙,可以与其拥有者联系起来,艾森蒂亚将提供身份证明,但不会透露任何非必要的个人身份。

总结

也许 WEB3 没有一个清晰地样子,只是不断的畅想未来的模型。如果一切明了,大概就是下一个时代的变革点了。因此抓住它,不要嫌弃它的简陋,要看到它就是未来。

参考文献

国内对象存储/CDN服务价格横评

话不多说,直接上表格。

价格表(国内HTTPS价格)

名称 存储免费额度(GB) 存储费用(元 / GB / 日) 流量免费额度 流量费用(元/GB) CDN流量费用
多吉云 10 0.003 20 0.11 0.11
七牛云 10 0.098-0.145 \ 0.29 0.28
腾讯云 \ 0.099-0.118 \ 0.5 0.21
阿里云 \ 0.12 \ 0.25-0.5 0.24
又拍云 \ 0.0043 \ 0.5 0.29
百度智能云 \ 0.119-0.15 \ 0.25-0.49 0.20

注:价格整理于2021年12月30日;存储指对象存储,流量指对象存储公网流出流量。

分析说明

由于本人使用过国内各种主流云厂商云存储、CDN服务,目前稳定存储2G左右,每日流量1G左右,使用过各家。知道最近发现多吉云也推出了自家云存储服务,虽然是阿里云、腾讯云包了一层,但费用便宜很多,应该是走量优惠比较大。

后来我就把自己网站的对象存储和CDN都迁移到多吉云了,目前使用起来感觉良好。

还记得上次过年期间,使用的七牛云存储出了点问题,提工单半天没解决,非说是我解析有问题。后来自愈了,对七牛云的印象就没有多好了,因此这次果断迁移到多吉云。

经过这次迁移,发现各家云存储费用差异还比较大,顺便整理,方便各位有需要的客官挑选。

参考文献

Ubuntu 安装 GitKraken 并汉化

GitKraken 是一款超好用的 git 可视化(gui)工具,但是官方不提供多语言支持,今天就以 Ubuntu 平台为例介绍一下安装方法。

安装及汉化

首先在 GitKraken 官网下载安装包:

如果是 debian 系操系统可下载其中的 deb 版本,之后安装即可。

至于汉化,使用 Github 上的 k-skye/gitkraken-chinese 这个仓库即可完成。

方法很简单,安装好之后,使用汉化语言包替换原有的语言包即可。

# 首先备份官方语言包
mv /usr/share/gitkraken/resources/app.asar.unpacked/strings.json /usr/share/gitkraken/resources/app.asar.unpacked/strings.json.bk
# 从github拉取资源并替换
wget https://github.com/k-skye/gitkraken-chinese/raw/master/strings_8.1.1.json -O /usr/share/gitkraken/resources/app.asar.unpacked/strings.json

# 如果在国内访问 github 有困难
# 可使用 fastgit 提供的 github 国内镜像拉取
wget https://hub.fastgit.org/k-skye/gitkraken-chinese/raw/master/strings_8.1.1.json -O /usr/share/gitkraken/resources/app.asar.unpacked/strings.json

之后重启 GitKraken 即可。

https://imagehost-cdn.frytea.com/images/2021/12/30/image031b80c4bef3b2af.png

参考文献

深挖 docker 默认网络 | 为什么 docker 默认网络能上外网

为什么默认配置创建出来的 docker 容器可以访问外网,为什么监听对应端口就能对外暴露docker服务,一张图搞清楚。

首先 Docker 有四中网络模式, 分别是 Bridge、Host、Container、None,默认使用 Bridge,今天就来讲讲 Bridge。

https://imagehost-cdn.frytea.com/images/2021/12/28/drawf92673ecda5420ba.jpg

创建的容器默认使用 bridge 的方式联网,因此默认就可以docker间互通,该网桥名叫 bridge0,通过 nat 的方式与物理网卡相连,每创建一个该模式下的容器,就自动创建一对 veth-pair 挂上去。

# brctl show 
bridge name bridge id       STP enabled interfaces
br-1061a9012b5a     8000.0242998bddbb   no      
docker0     8000.0242f9c2fd4f   no      veth4fc1b46
                            veth86c0817
                            vethe510127
vmbr0       8000.b496916544ad   no      enp59s0f1

通过 nat 的方式,docker可以自由的通过宿主机网卡访问外网,如果映射端口,也是通过 nat 的方式将对应流量送入docker:

# docker ps
CONTAINER ID   IMAGE          COMMAND              CREATED        STATUS        PORTS                                       NAMES
398907d00b97   001b9ea10452   "/sbin/init start"   5 hours ago    Up 5 hours    192.168.226.140:8206->8006/tcp              hci-nos3
475b07e8faa4   001b9ea10452   "/sbin/init start"   5 hours ago    Up 5 hours    192.168.226.139:8206->8006/tcp              hci-nos2
e9955db2132c   001b9ea10452   "/sbin/init"         29 hours ago   Up 29 hours   0.0.0.0:8106->8006/tcp, :::8106->8006/tcp   hci-nos
# iptables -n -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.18.0.0/16        0.0.0.0/0           
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:8006
MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:8006
MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:8006

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8106 to:172.17.0.2:8006
DNAT       tcp  --  0.0.0.0/0            192.168.226.139      tcp dpt:8206 to:172.17.0.3:8006
DNAT       tcp  --  0.0.0.0/0            192.168.226.140      tcp dpt:8206 to:172.17.0.4:8006

以上面几个docker为例,分别直接监听和指定ip,会发现创建了对应的规则在 iptables 中。

至此,为什么docker访问外网及访问docker的原理讲解完毕,有问题欢迎留言。

参考文献

理解网桥、集线器、交换机、路由器的本质

一直弄不清楚这几者的区别,加之现代的三层交换机,就更搞不清楚几者的关系了。

后来发现试图以现代产品的思维理解这几个概念是极其容易混淆的,必须回归到几者最本质的样子,再去拓展才行。

后来在知乎上找到一段描述,感觉说的很好,在这里记录一下:

  • 集线器
    • 工作在物理层(L1)。一个口收到的信号,原封不动的发送给所有其他的口,由其他的口上的设备自己决定是否接收信号。
  • 网桥
    • 工作在数据链路层(L2)。以太网中,数据链路层地址就是mac地址,网桥与hub的区别在于,网桥会过滤mac,只有目的mac地址匹配的数据才会发送到出口。一个bridge指的是一个输入到一个输出的桥接。
  • 交换机
    • 工作在数据链路层(L2)。早期的switch,其实可以看成多个bridge的集成设备,因此也工作在数据链路层。一个交换机口的输入到另一个交换机口的输出,可以认为是一个bridging。交换机中的MAC table,实际是为了bridge能工作而存在。
  • 路由器
    • 工作在网络层(L3),基于IP地址做转发。

回归本质,有助于更好地理解。至此我终于理解了什么是网桥。

参考文献