IT技术精华 http://it.taocms.org/ 聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长 2020-07-09 IT技术精华 32866 [原]cBPF/eBPF如何处理reuseport的吐槽和示例 作者:dog250 发表于 2020/07/08 23:23:24 原文链接 [sourcelinkurl]
阅读:18

立即购买:淘宝网]]>
IT技术精华 2020-07-09 00:07:05
32861 Amazon RDS的同步复制是如何实现的 一直以来实现数据库的零数据丢失都是非常有挑战,尤其是跨可用区的场景下。很多核心系统为了实现这一点都投入了大量的智慧和金钱。Amazon RDS在文档都明确的写到,数据库在多AZ之间的数据是保持同步的(注:同步是指数据写入两边要同时写成功,即使一边不可用,已经提交的事务在另一边一定是成功的)。一直以来,我也很好奇Amazon RDS在哪个层面实现的同步复制。

这个问题原本也是没有太大疑问的,可以推测应该是通过EBS层面的块复制来下。依据有两方面,有一些公开的Amazon RDS一些架构图中可以看到有EBS复制的箭头说明。另外,还有一点,只有通过EBS的复制实现跨可用区数据一致性,才可能在RDS支持的多种数据库,如MySQL、SQL Server、Oracle等,上保持架构上一致。否则,不同数据库类型的高可用和复制架构可能相差很大。

但是,之前很长时间我还是有一个疑问,Amazon RDS复制到底是在数据库逻辑层实现的还是在EBS物理层实现的。

既然有上面的猜测,那为什么产生了这个疑问呢?是因为,在Aurora很多的对外介绍材料(包括论文、架构介绍的slide)中,会放一个MySQL架构来突出Aurora的架构优势。这个图一直让我误以为Amazon RDS使用了数据库的binlog的复制。在了解Aurora的时候大家经常会看到如下架构图作为反面案例(参考):

Snip20200327_2

因为这幅图的误导,让我一度以为Amazon RDS MySQL用了这种复制方式。但是仔细一想,应该是不可能的,使用EBS实现多可用区复制是一种更加一致、通用的方案。为了找到这一点的实锤证据,就稍微做了一些搜索。最终,在Youtube上AWS Re:Invent大会上的演讲找到了AWS同学详细关于这个点的阐述,具体的描述如下(参考: AWS re:Invent 2017: Deep Dive on Amazon Relational Database Service (RDS) (DAT302)):

...They're replicating synchronously now that replication is at the block level.It's not a logical replication. It's actually physically replication the storage blocks...

另外,还有明确表示EBS复制关系的胶片如下:

Snip20200327_4

好了,这个问题的疑问就到此为止了,各种证据已经实锤。

那么,Aurora论文中的架构图是怎么回事呢?事实上,当用户在EC2/ECS自建数据库的时候,架构应该就是上面Figure 2。


立即购买:淘宝网]]>
IT技术精华 2020-07-09 00:07:26
32809 SSH 证书登录教程 SSH 是服务器登录工具,提供密码登录和密钥登录。

但是,SSH 还有第三种登录方法,那就是证书登录。很多情况下,它是更合理、更安全的登录方法,本文就介绍这种登录方法。

一、非证书登录的缺点

密码登录和密钥登录,都有各自的缺点。

密码登录需要输入服务器密码,这非常麻烦,也不安全,存在被暴力破解的风险。

密钥登录需要服务器保存用户的公钥,也需要用户保存服务器公钥的指纹。这对于多用户、多服务器的大型机构很不方便,如果有员工离职,需要将他的公钥从每台服务器删除。

二、证书登录是什么?

证书登录就是为了解决上面的缺点而设计的。它引入了一个证书颁发机构(Certificate1 authority,简称 CA),对信任的服务器颁发服务器证书,对信任的用户颁发用户证书。

登录时,用户和服务器不需要提前知道彼此的公钥,只需要交换各自的证书,验证是否可信即可。

证书登录的主要优点有两个:(1)用户和服务器不用交换公钥,这更容易管理,也具有更好的可扩展性。(2)证书可以设置到期时间,而公钥没有到期时间。针对不同的情况,可以设置有效期很短的证书,进一步提高安全性。

三、证书登录的流程

SSH 证书登录之前,如果还没有证书,需要生成证书。具体方法是:(1)用户和服务器都将自己的公钥,发给 CA;(2)CA 使用服务器公钥,生成服务器证书,发给服务器;(3)CA 使用用户的公钥,生成用户证书,发给用户。

有了证书以后,用户就可以登录服务器了。整个过程都是 SSH 自动处理,用户无感知。

第一步,用户登录服务器时,SSH 自动将用户证书发给服务器。

第二步,服务器检查用户证书是否有效,以及是否由可信的 CA 颁发。

第三步,SSH 自动将服务器证书发给用户。

第四步,用户检查服务器证书是否有效,以及是否由信任的 CA 颁发。

第五步,双方建立连接,服务器允许用户登录。

四、生成 CA 的密钥

证书登录的前提是,必须有一个 CA,而 CA 本质上就是一对密钥,跟其他密钥没有不同,CA 就用这对密钥去签发证书。

虽然 CA 可以用同一对密码签发用户证书和服务器证书,但是出于安全性和灵活性,最好用不同的密钥分别签发。所以,CA 至少需要两对密钥,一对是签发用户证书的密钥,假设叫做user_ca,另一对是签发服务器证书的密钥,假设叫做host_ca

使用下面的命令,生成user_ca


# 生成 CA 签发用户证书的密钥
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/user_ca -C user_ca

上面的命令会在~/.ssh目录生成一对密钥:user_ca(私钥)和user_ca.pub(公钥)。

这个命令的各个参数含义如下。

  • -t rsa:指定密钥算法 RSA。
  • -b 4096:指定密钥的位数是4096位。安全性要求不高的场合,这个值可以小一点,但是不应小于1024。
  • -f ~/.ssh/user_ca:指定生成密钥的位置和文件名。
  • -C user_ca:指定密钥的识别字符串,相当于注释,可以随意设置。

使用下面的命令,生成host_ca


# 生成 CA 签发服务器证书的密钥
$ ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca

上面的命令会在~/.ssh目录生成一对密钥:host_ca(私钥)和host_ca.pub(公钥)。

现在,~/.ssh目录应该至少有四把密钥。

  • ~/.ssh/user_ca
  • ~/.ssh/user_ca.pub
  • ~/.ssh/host_ca
  • ~/.ssh/host_ca.pub

五、CA 签发服务器证书

有了 CA 以后,就可以签发服务器证书了。

签发证书,除了 CA 的密钥以外,还需要服务器的公钥。一般来说,SSH 服务器(通常是sshd)安装时,已经生成密钥/etc/ssh/ssh_host_rsa_key了。如果没有的话,可以用下面的命令生成。


$ sudo ssh-keygen -f /etc/ssh/ssh_host_rsa_key -b 4096 -t rsa

上面命令会在/etc/ssh目录,生成ssh_host_rsa_key(私钥)和ssh_host_rsa_key.pub(公钥)。然后,需要把服务器公钥ssh_host_rsa_key.pub,复制或上传到 CA 所在的服务器。

上传以后,CA 就可以使用密钥host_ca为服务器的公钥ssh_host_rsa_key.pub签发服务器证书。


$ ssh-keygen -s host_ca -I host.example.com -h -n host.example.com -V +52w ssh_host_rsa_key.pub

上面的命令会生成服务器证书ssh_host_rsa_key-cert.pub(服务器公钥名字加后缀-cert)。这个命令各个参数的含义如下。

  • -s:指定 CA 签发证书的密钥。
  • -I:身份字符串,可以随便设置,相当于注释,方便区分证书,将来可以使用这个字符串撤销证书。
  • -h:指定该证书是服务器证书,而不是用户证书。
  • -n host.example.com:指定服务器的域名,表示证书仅对该域名有效。如果有多个域名,则使用逗号分隔。用户登录该域名服务器时,SSH 通过证书的这个值,分辨应该使用哪张证书发给用户,用来证明服务器的可信性。
  • -V +52w:指定证书的有效期,这里为52周(一年)。默认情况下,证书是永远有效的。建议使用该参数指定有效期,并且有效期最好短一点,最长不超过52周。
  • ssh_host_rsa_key.pub:服务器公钥。

生成证书以后,可以使用下面的命令,查看证书的细节。


$ ssh-keygen -L -f ssh_host_rsa_key-cert.pub

最后,为证书设置权限。


$ chmod 600 ssh_host_rsa_key-cert.pub

六、CA 签发用户证书

下面,再用 CA 签发用户证书。这时需要用户的公钥,如果没有的话,客户端可以用下面的命令生成一对密钥。


$ ssh-keygen -f ~/.ssh/user_key -b 4096 -t rsa

上面命令会在~/.ssh目录,生成user_key(私钥)和user_key.pub(公钥)。

然后,将用户公钥user_key.pub,上传或复制到 CA 服务器。接下来,就可以使用 CA 的密钥user_ca为用户公钥user_key.pub签发用户证书。


$ ssh-keygen -s user_ca -I user@example.com -n user -V +1d user_key.pub

上面的命令会生成用户证书user_key-cert.pub(用户公钥名字加后缀-cert)。这个命令各个参数的含义如下。

  • -s:指定 CA 签发证书的密钥
  • -I:身份字符串,可以随便设置,相当于注释,方便区分证书,将来可以使用这个字符串撤销证书。
  • -n user:指定用户名,表示证书仅对该用户名有效。如果有多个用户名,使用逗号分隔。用户以该用户名登录服务器时,SSH 通过这个值,分辨应该使用哪张证书,证明自己的身份,发给服务器。
  • -V +1d:指定证书的有效期,这里为1天,强制用户每天都申请一次证书,提高安全性。默认情况下,证书是永远有效的。
  • user_key.pub:用户公钥。

生成证书以后,可以使用下面的命令,查看证书的细节。


$ ssh-keygen -L -f user_key-cert.pub

最后,为证书设置权限。


$ chmod 600 user_key-cert.pub

七、服务器安装证书

CA 生成服务器证书ssh_host_rsa_key-cert.pub以后,需要将该证书发回服务器,可以使用下面的scp命令,将证书拷贝过去。


$ scp ~/.ssh/ssh_host_rsa_key-cert.pub root@host.example.com:/etc/ssh/

然后,将下面一行添加到服务器配置文件/etc/ssh/sshd_config


HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub

上面的代码告诉 sshd,服务器证书是哪一个文件。

重新启动 sshd。


$ sudo systemctl restart sshd
# 或者
$ sudo service sshd restart

八、服务器安装 CA 公钥

为了让服务器信任用户证书,必须将 CA 签发用户证书的公钥user_ca.pub,拷贝到服务器。


$ scp ~/.ssh/user_ca.pub root@host.example.com:/etc/ssh/

上面的命令,将 CA 签发用户证书的公钥user_ca.pub,拷贝到 SSH 服务器的/etc/ssh目录。

然后,将下面一行添加到服务器配置文件/etc/ssh/sshd_config


TrustedUserCAKeys /etc/ssh/user_ca.pub

上面的做法是将user_ca.pub加到/etc/ssh/sshd_config,这会产生全局效果,即服务器的所有账户都会信任user_ca签发的所有用户证书。

另一种做法是将user_ca.pub加到服务器某个账户的~/.ssh/authorized_keys文件,只让该账户信任user_ca签发的用户证书。具体方法是打开~/.ssh/authorized_keys,追加一行,开头是@cert-authority principals="...",然后后面加上user_ca.pub的内容,大概是下面这个样子。


@cert-authority principals="user" ssh-rsa AAAAB3Nz...XNRM1EX2gQ==

上面代码中,principals="user"指定用户登录的服务器账户名,一般就是authorized_keys文件所在的账户。

重新启动 sshd。


$ sudo systemctl restart sshd
# 或者
$ sudo service sshd restart

至此,SSH 服务器已配置为信任user_ca签发的证书。

九、客户端安装证书

客户端安装用户证书很简单,就是从 CA 将用户证书user_key-cert.pub复制到客户端,与用户的密钥user_key保存在同一个目录即可。

十、客户端安装 CA 公钥

为了让客户端信任服务器证书,必须将 CA 签发服务器证书的公钥host_ca.pub,加到客户端的/etc/ssh/ssh_known_hosts文件(全局级别)或者~/.ssh/known_hosts文件(用户级别)。

具体做法是打开ssh_known_hostsknown_hosts文件,追加一行,开头为@cert-authority *.example.com,然后将host_ca.pub文件的内容(即公钥)粘贴在后面,大概是下面这个样子。


@cert-authority *.example.com ssh-rsa AAAAB3Nz...XNRM1EX2gQ==

上面代码中,*.example.com是域名的模式匹配,表示只要服务器符合该模式的域名,且签发服务器证书的 CA 匹配后面给出的公钥,就都可以信任。如果没有域名限制,这里可以写成*。如果有多个域名模式,可以使用逗号分隔;如果服务器没有域名,可以用主机名(比如host1,host2,host3)或者 IP 地址(比如11.12.13.14,21.22.23.24)。

然后,就可以使用证书,登录远程服务器了。


$ ssh -i ~/.ssh/user_key user@host.example.com

上面命令的-i参数用来指定用户的密钥。如果证书与密钥在同一个目录,则连接服务器时将自动使用该证书。

十一、废除证书

废除证书的操作,分成用户证书的废除和服务器证书的废除两种。

服务器证书的废除,用户需要在known_hosts文件里面,修改或删除对应的@cert-authority命令的那一行。

用户证书的废除,需要在服务器新建一个/etc/ssh/revoked_keys文件,然后在配置文件sshd_config添加一行,内容如下。


RevokedKeys /etc/ssh/revoked_keys

revoked_keys文件保存不再信任的用户公钥,由下面的命令生成。


$ ssh-keygen -kf /etc/ssh/revoked_keys -z 1 ~/.ssh/user1_key.pub

上面命令中,-z参数用来指定用户公钥保存在revoked_keys文件的哪一行,这个例子是保存在第1行。

如果以后需要废除其他的用户公钥,可以用下面的命令保存在第2行。


$ ssh-keygen -ukf /etc/ssh/revoked_keys -z 2 ~/.ssh/user2_key.pub

十二、参考链接

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期: 2020年7月 8日

立即购买:淘宝网]]>
IT技术精华 2020-07-08 15:07:07
32790 使用 Golang 实现一个 JSON 命令行工具

首先先提一个问题,"abc"123 或者 [1, 2, 3] 是不是一个合法的 json ?

之前一直有在使用一个 json 的命令行工具 jq,这个工具是基于 flex 和 bison 来实现的(去了解这些是基于当年学习 php 的经历)。后来有段时间我又发现一个不错的词法和语法分析工具 antlr,它支持多种语言的生成,并且本身也提供了多种语言的基本语法文件。所以我就想能不用基于它实现一个 go 语言版的 json 命令行工具。

下面就开始一步一步行动吧(如果想直接看代码可以直接拉到底部),我将这个项目命名为 jtlr

提供的功能

根据我自己常使用的场景,我要实现以下几个功能:

基本用法:

jtlr ‘{“a”: 1}’

交互模式,可以多次输入,并且最好能支持上下切换:

jtlr -a

从标准输入中读取内容,可以格式化实时输出的日志:

tail -f xxx.log | jtlr -s

从文件中读取:

jtlr -f xxx.log

什么是 json

在动手之前,先要对 json 有一个全面的认识。先来大致看一下官网提供的 json 的 BNF 范式的起始部分:

json
element

value
object
array
string
number
"true"
"false"
"null"

...

element
ws value ws

ws 是 whitespace 的缩写,即空白字符,忽略这个之后,即可简单清晰的看到 json 的内的有效数值。虽然我们常用的 json 内容都是 object 起的,但并不是一定要从 object 开始,所以对于文章开头那个问题,你有答案了吗?

在实现时我并没有去复制官网提供的 BNF,而是采用了 antlr4 提供的语法,关于它的实现,这里有一篇文章说明:https://andreabergia.com/a-grammar-for-json-with-antlr-v4/

简单来说,json 有七种的数据,其中 arrayobject 是可以再包含 value,剩下五种就是基本的数值数据。

此外,还有一类比较特殊的情况,就是对 string 的用法:

member
ws string ws ':' element

string 既可以是一个基本类型的 value,也可以一个对象成员的键值。这会导致我们在对 string 做上色等处理时需要考虑着两种情况。

antlr4 提供的接口

使用下面的命令即可生成基于 go 语言的 lexer 和 parser:

antlr -Dlanguage=Go -o parser/ JSON.g4

接下来就是功能实现的工作了。

antlr4 生成的接口比较完备,包含每个分支逻辑进入、退出和错误节点访问的接口。并且有较好的错误纠正和提示机制。

但对于 json 本身这个 case,需要注意的是对 valuestring。上面也有提到,所有七类数值都是 value,所以都会触发 EnterValueExitValue 事件,string 同理。

对于 objectarray 来说,比较棘手的在于嵌套的数据,例如:

{“a”: [134, {“a”: 1}, true, [1, 2, 3], false]}

在使用 antlr4 提供的接口时,需要标注进入和退出的顺序。

交互模式下的问题

最开始我做了个非常简单的交互模式的实现:

reader := bufio.NewReader(os.Stdin)
for {
fmt.Print(">>> ")
text, err := reader.ReadString('n')
if err == io.EOF {
break
}
if text == "n" || text == "rn" {
continue
}
fn(text)
}

但是在这种实现逻辑下,上下左右等按键会直接打印在屏幕上而无法正确处理,因为终端处于 cooked mode 下。go 本身也没有提供 tty 的封装。所以要进入 raw mode,一种是通过直接 call 起命令行的方式:

func raw(start bool) error {
r := "raw"
if !start {
r = "-raw"
}

rawMode := exec.Command("stty", r)
rawMode.Stdin = os.Stdin
err := rawMode.Run()
if err != nil {
return err
}

return rawMode.Wait()
}

另外一种是操作 stdin 的文件句柄,这样实现起来就相当复杂了。

出于兼容性和可维护性的考虑,我使用了 golang/crypto 提供的 terminal 的封装,这也是项目中除了 antlr 以外唯一一个引入的第三方包(如果算是第三方的话)。

但是这个包有一个问题是必须使用 rn 进行回车(官方的 issue 解释是一些历史原因吧啦吧啦),不然光标不会回到行首,但是 go 标准的 fmt 包中使用的 n 换行,而 antlr 使用了 fmt 进行错误输出,所以需要对错误输出进行重载。

未完成

从开始构思到实现到当前阶段,大概耗时两个周末了。

由于前期偷懒,格式化输出全部使用的是 fmt,这里后续需要优化一下。

现在的实现对于 antlr 来说有点像用牛刀杀鸡,jq 本身支持的节点选取,这是后续实现的一个方向。

另外,go 官方虽然提供了官方的 json 序列化和反序列化工具,但是市面上也有一些第三方的实现被使用,我也想探讨一下实现方式。

另外,windows 下还没做完全的兼容测试。

最后,贴上项目地址:https://github.com/XiaoLer/jtlr-go


立即购买:淘宝网]]>
IT技术精华 2020-07-08 12:07:08
32731 [原]【原创】PHP/Go程序员的发展与规划
立即购买:淘宝网]]>
IT技术精华 2020-07-08 02:07:05
32447 Linux下删点日志也能搞死人 点击在新窗口中浏览此图片

想了十天十夜不知道写些什么,那就写写面试题吧。

1

在面试应聘者的时候,我常常会问:

在 Linux 下,如何删除一个目录下的所有 log 文件?

不知道是不是我人畜无害的围笑给了应聘者我很好应付的错觉 

点击在新窗口中浏览此图片

以至于应聘者全都回答:`rm *.log`

追问:该目录下可能有很多子目录,如何把子目录里的 log 文件也删掉呢?

答:`rm -r *.log` 

点击在新窗口中浏览此图片

2

令我很意外的是,真的只有很少的应聘者能想到 find 命令。

而且想到的人也很少有记得具体用法的。

点击在新窗口中浏览此图片

目前为止只有一个应聘者能够给出完整的命令:

find -name \*.log -exec rm -f {} \;


注:这里的两个斜杠都不是笔误。

我觉得 find 应该不算一个很罕见的命令?

我们有一台共享开发机,因为大家都懒得删 log ,经常磁盘爆满,后来我们给它加了个 crontab:

0 4 * * * find /home/ -type f -name "*.log*" -size +100M -exec bash -c "echo -n > '{}'" \;


注:".log\*" 后面的 \* 是考虑了 log rotate。

注意,这个命令没有用 rm ,而是使用重定向来清空文件,原因后面会讲。

3

基于清理磁盘空间这个场景,我还会继续问: 

你有遇到过删了 log 文件,但是磁盘空间却没有释放的情况吗? 

有些候选人可能心里在想着:文件删了不就删了吗,还有什么磁盘空间没释放?

点击在新窗口中浏览此图片

所以有时候我需要解释一下,是 df 命令看到的磁盘空间没有减少。

还有个候选人努力想了想,和我确认,是不是正好这个目录挂载的是其他磁盘,所以看起来当前磁盘空间没减少。(当然不是)

思路稍微开阔一点的候选人会想到:你个憨批莫不是删了个软链接吧? 

点击在新窗口中浏览此图片

当然候选人的语气会比较友好。

然后我会和候选人继续沟通: 

你提到了软链接,那它和硬链接的区别是什么呢? 

有时候我怀疑这几个连续的问题问到候选人开始怀疑人生,因为有的候选人有点犹豫,觉得自己想说的其实是硬链接。

点击在新窗口中浏览此图片

不过还是有几个候选人知道,软链接是一种文件类型,其内容是目标文件的路径;硬链接是 inode 的别名,同一个 inode 可以有多个链接,在 inode 里记录了硬链接的数量(引用计数)。 

比如这样:

创建一个空文件,看下inode和链接数:

$ touch a.txt #创建一个空文件
$ stat -c 'inode %i, links %h' a.txt
inode 12058942, links 1


创建一个软链接,再看看文件大小: 

$ ln -s a.txt b.txt #软链接
$ stat -c 'inode %i, links %h' b.txt
inode 12058978, links 1

$ ls -l b.txt #大小5字节
lrwxrwxrwx ... 5 ... b.txt -> a.txt

$ readlink b.txt #文件内容
a.txt


创建一个硬链接,看下inode和链接数

$ ln a.txt c.txt #硬链接,inode不变,链接数变成2
$ stat -c 'inode %i, links %h' c.txt
inode 12058942, links 2

$ ls -l c.txt #大小0字节,和a一样
lrwxrwxrwx ... 0 ... c.txt


4

但实际生产上,遇到 “删了log文件、但空间不释放” 通常和软/硬链接没有什么关系。

点击在新窗口中浏览此图片

实战经验比较丰富的候选人会知道,这往往是因为 log 文件正被另一个进程打开。

比如在终端 1 打开 a.txt:

$ python
>>> f = open("a.txt")


然后在终端 2 可以看到该文件被 Python 打开:

$ lsof a.txt
COMMAND  PID ...    NODE NAME
python  2390 ... 12058942 a.txt


删掉 a.txt,再查看 python 打开的文件列表:

$ rm a.txt
$ ls -l /proc/2390/fd
lrwx------ 1 user ... 00:04 0 -> /dev/pts/5
lrwx------ 1 user ... 00:04 1 -> /dev/pts/5
lrwx------ 1 user ... 00:04 2 -> /dev/pts/5
lr-x------ 1 user ... 00:04 3 -> /tmp/a.txt (deleted)


注:0、1、2、3 是内核的 fd 编号。0=stdin, 1=stdout, 2=stder。

可以看到,a.txt  被标记为已删除,但因为进程还开着它,可能会访问文件的内容,所以内核会等到进程关闭该文件(或进程退出后)才在磁盘上移除这个文件。

5

在面试中通常没有机会再问下去了,但实践中往往问题还没解决。

比如前述共享开发机,就曾遇到了磁盘空间共800G,但用 du 命令查看,所有文件只占用了 500G的情况。

那么: 

1. 如何才能知道现在系统中有哪些文件已删除、但是仍被占用呢?

$ sudo lsof | grep deleted
COMMAND  PID  …  NAME
main  893246  …  /../nohup.out (deleted)
...


发现是有大量已经被删除、但仍被某些进程打开的 nohup.out 。 

2. 坑是找到了,该怎么填呢?

由于这是开发机,很简单,把进程杀掉就好了,进程退出时,内核会负责关闭文件,然后清理占用的空间。

但如果是线上服务呢?

Linux下有一个 package 叫 logrotate,像 nginx 这些服务就是使用它来做日志切割/轮转的。

但 nginx 是在后台持续运行的,不能为了切个日志就停止服务,所以它们是这样约定的:

a. logrotate 执行 rename 系统调用(相当于 mv 命令)重命名日志文件;
b. 由于 inode 不变,这时 nginx 会继续写入重命名后的日志文件;
c. logrotate 给 nginx 发送一个 SIGHUP 信号;
d. nginx 在收到信号后会关闭当前文件,并重新打开日志文件(即创建了新的日志文件)。

注:为什么是用 SIGHUP 而不是其他信号,以后可能会另开一篇讲讲。

这样 logrotate 出来的日志,就可以放心删除了。 

对于不支持类似逻辑的服务怎么办呢?

重启大法。

如果不怕背 P0 的话,还可以这么作死:

$ sudo gdb
(gdb) attach $PID
(gdb) call ftruncate(3, 0) #按需修改fd
$1 = 0


注:看起来文件是清空了,但可能存在其他坑,后果自负。ftruncate只是清空文件,如果想关闭文件,可以结合 dup、dup2、open和close来搞事,不细说了。 

点击在新窗口中浏览此图片

6

看到这里你应该明白了为什么前面那个 find 命令不直接用 rm 了吧?

照例总结下:

1. 可以用 find 查找文件
2. 软链接存的是路径,硬链接共享inode
3. 删除被进程打开的文件,磁盘空间不会释放
4. lsof 很好用(不只是看文件的占用)

还想知道其他有意思的面试题吗?

不如投个简历来亲身体验下: 

**~ 投递链接 ~**

投放研发工程师(上海)

https://job.toutiao.com/s/J8DRDyG

高级广告研发工程师(北京)

https://job.toutiao.com/s/J8DNwJY

立即购买:淘宝网]]>
IT技术精华 2020-07-06 01:07:10