分类:未分类


Nginx记录用户请求Header到access log


参考  https://www.cnblogs.com/Serverlessops/p/13410262.html

log_format里增加字段: “$http_mycheck“‘

curl -H “mycheck: justtestlog” localhost/whatever.html

 

nginx 作为正向代理


nginx还是蛮强大的,不仅可以作为7层的正向代理,还可以作为4层的正向代理。

参考文档  https://zhuanlan.zhihu.com/p/70522194

 

7层是需要加三分模块 ngx_http_proxy_connect_module,github地址:https://github.com/chobits/ngx_http_proxy_connect_module 注意,不同的Nginx版本,还需要下载不同的patch包

具体配置   参考 https://github.com/aminglinux/nginx/blob/master/proxy/z_proxy.md

配置文件

server {
     listen                         3128;


     # dns resolver used by forward proxying
     resolver                       119.29.29.29;


     # forward proxy for CONNECT request
     proxy_connect;
     proxy_connect_allow            80 443 8080;
     proxy_connect_connect_timeout  10s;
     proxy_connect_read_timeout     10s;
     proxy_connect_send_timeout     10s;


     # forward proxy for non-CONNECT request
     location / {
         proxy_pass http://$host;
         proxy_set_header Host $host;
     }
}

 

4层的话,需要额外增加编译参数

--with-stream  --with-stream_ssl_preread_module  --with-stream_ssl_module

配置文件

stream {
    resolver 114.114.114.114;
    server {
        listen 443;
        ssl_preread on;
        proxy_connect_timeout 5s;
        proxy_pass $ssl_preread_server_name:$server_port;
    }
}

Nginx tcp反向代理记录日志


对于Nginx访问日志,大家都很熟悉,但是对于tcp代理来说,到底能不能记录访问日志呢? 想了一下,貌似不能啊,因为毕竟访问日志里面记录的元素都是http协议的。

但是当我去搜索时,的确搜到了相关的文档,索性先试试看,测试后发现确实可以。

修改nginx配置文档/usr/local/nginx/conf/nginx.conf 设置日志格式

stream {
 log_format proxy '$remote_addr [$time_local] '
 '$protocol $status $bytes_sent $bytes_received '
 '$session_time "$upstream_addr" '
 '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
 include ./conf.d/*.tcpstream;
}

修改具体的tcp配置文档

server{
 error_log /opt/log/device5001_error.log;
 access_log /opt/log/device5001_access.log proxy;
 listen 5001;
 proxy_pass device5001;
}

 

日志如下:

 

但是,需要提醒你的是:

nginx会等待session结束才会记录到日志文件;
session日志只是tcp层面的记录,包括session时间,发送接收字节数等等;
session内部发送日志(比如一个socket连接建立起来以后,多次发送心跳数据)需要在应用层面才能记录;

nginx代理过程中修改user_agent


关于User_agent是啥,就不多介绍了,简单说就是客户端的标识,比如chrome浏览器有chrome浏览器的标识,手机客户端也有手机客户端的标识。

有些特殊场景需要指定一个固定的user_agent,来模拟某个客户端。这时候,就可以借助Nginx的反向代理来实现。Nginx配置文件如下:

server {
    listen 8000;
    server_name localhost;
    access_log  log/access-8000.log;
    error_log  log/error-8000.log;

    location / {
        set $ua "lalallalalla x33xx33";
        proxy_pass   http://127.0.0.1:8081/;
        proxy_set_header User-Agent $ua;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}

再来看看访问日志:

127.0.0.1 - - [19/Nov/2020:11:56:55 +0800] "GET /build/framework/js/7eaf6.chunk.js HTTP/1.0" 200 42870 "http://192.168.0.62:8000/" "lalallalalla x33xx33"
127.0.0.1 - - [19/Nov/2020:11:56:55 +0800] "GET /uusafe/gloab/rest/saas/all HTTP/1.0" 200 536 "http://192.168.0.62:8000/" "lalallalalla x33xx33"

已经将user_agent修改为了自定义的了。


centos使用yum update升级到指定小版本


参考: https://www.cnblogs.com/configure/p/10918189.html

 

提供一个搜狐镜像的7.6的yum源:

[C7.6.1810-base]
name=CentOS-7.6.1810 - Base
baseurl=http://mirrors.sohu.com/centos/7.6.1810/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.6.1810-updates]
name=CentOS-7.6.1810 - Updates
baseurl=http://mirrors.sohu.com/centos/7.6.1810/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.6.1810-extras]
name=CentOS-7.6.1810 - Extras
baseurl=http://mirrors.sohu.com/centos/7.6.1810/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.6.1810-centosplus]
name=CentOS-7.6.1810 - CentOSPlus
baseurl=http://mirrors.sohu.com/centos/7.6.1810/centosplus/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.6.1810-fasttrack]
name=CentOS-7.6.1810 - Fasttrack
baseurl=http://mirrors.sohu.com/centos/7.6.1810/fasttrack/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

nginx反向代理时保持长连接


【场景描述】

HTTP1.1之后,HTTP协议支持持久连接,也就是长连接,优点在于在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接消耗和延迟

如果我们使用了nginx去作为反向代理或者负载均衡,从客户端过来的长连接请求就会被转换成短连接发送给服务器端。

为了支持长连接,我们需要在nginx服务器上做一些配置。

 

·【要求】

使用nginx时,想要做到长连接,我们必须做到以下两点:

  1. 从client到nginx是长连接
  2. 从nginx到server是长连接

 

对于客户端而言,nginx其实扮演着server的角色,反之,之于server,nginx就是一个client

 

·【保持和 Client 的长连接】

我们要想做到Client与Nginx之间保持长连接,需要:

  1. Client发送过来的请求携带“keep-alive”header
  2. Nginx设置支持keep-alive

 

【HTTP配置】

默认情况下,nginx已经开启了对client连接的 keepalive 支持。对于特殊场景,可以调整相关参数。

http {

keepalive_timeout 120s;        #客户端链接超时时间。为0的时候禁用长连接。

keepalive_requests 10000;    #在一个长连接上可以服务的最大请求数目

#当达到最大请求数目且所有已有请求结束后,连接被关闭。

#默认值为100

}

 

大多数情况下,keepalive_requests = 100也够用,但是对于 QPS 较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。
QPS=10000 时,客户端每秒发送 10000 个请求 (通常建立有多个长连接),每个连接只能最多跑 100 次请求,意味着平均每秒钟就会有 100 个长连接因此被 nginx 关闭。

同样意味着为了保持 QPS,客户端不得不每秒中重新新建 100 个连接。

 

因此,如果用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket连接 (即使此时keep alive已经在 Client 和 NGINX 之间生效)。

 

·【保持和Server的长连接】

想让Nginx和Server之间维持长连接,最朴素的设置如下:

http {

upstream backend {

server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;

server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;

  keepalive 300; // 这个很重要!

}

server {

listen 8080 default_server;

server_name “”;

 

location / {

proxy_pass http://backend;

proxy_http_version 1.1;                         # 设置http版本为1.1

proxy_set_header Connection “”;      # 设置Connection为长连接(默认为no)}

}

}

}

 

【upstream配置】

upstream中,有一个参数特别的重要,就是keepalive

这个参数和之前http里面的 keepalive_timeout 不一样

这个参数的含义是,连接池里面最大的空闲连接数量

 

不理解?没关系,我们来举个例子:

场景:

有一个HTTP服务,作为upstream服务器接收请求,响应时间为100毫秒。

要求性能达到10000 QPS,我们需要在nginx与upstream服务器之间建立大概1000条HTTP请求。(1000/0.1s=10000)

 

最优情况:

假设请求非常的均匀平稳,每一个请求都是100ms,请求结束会被马上放入连接池并置为idle(空闲)状态

我们以0.1s为单位:

1. 我们现在keepalive的值设置为10,每0.1s钟有1000个连接

2. 第0.1s的时候,我们一共有1000个请求收到并释放

3. 第0.2s的时候,我们又来了1000个请求,在0.2s结束的时候释放

 

请求和应答都比较均匀,0.1s释放的连接正好够用,不需要建立新连接,且连接池中没有idle状态的连接。

 

第一种情况:

应答非常平稳,但是请求不平稳的时候

4. 第0.3s的时候,我们只有500个请求收到,有500个请求因为网络延迟等原因没有进来

这个时候,Nginx检测到连接池中有500个idle状态的连接,就直接关闭了(500-10)个连接

5. 第0.4s的时候,我们收到了1500个请求,但是现在池里面只有(500+10)个连接,所以Nginx不得不重新建立了(1500-510)个连接。

如果在第4步的时候,没有关闭那490个连接的话,只需要重新建立500个连接。

 

第二种情况:

请求非常平稳,但是应答不平稳的时候

4. 第0.3s的时候,我们一共有1500个请求收到

但是池里面只有1000个连接,这个时候,Nginx又创建了500个连接,一共1500个连接

5. 第0.3s的时候,第0.3s的连接全部被释放,我们收到了500个请求

Nginx检测到池里面有1000个idle状态的连接,所以不得不释放了(1000-10)个连接

 

造成连接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲连接数

上面的两种情况说的都是 keepalive 设置的不合理导致Nginx有多次释放与创建连接的过程,造成资源浪费。

 

keepalive 这个参数设置一定要小心,尤其是对于 QPS 要求比较高或者网络环境不稳定的场景,一般根据 QPS 值和 平均响应时间能大致推算出需要的长连接数量。

然后将keepalive设置为长连接数量的10%到30%。

 

【location配置】

http {

server {

location / {

proxy_pass http://backend;

proxy_http_version 1.1;                         # 设置http版本为1.1

proxy_set_header Connection “”;      # 设置Connection为长连接(默认为no)

}

}

}

HTTP 协议中对长连接的支持是从 1.1 版本之后才有的,因此最好通过 proxy_http_version 指令设置为 1.1。

HTTP1.0不支持keepalive特性,当没有使用HTTP1.1的时候,后端服务会返回101错误,然后断开连接

 

而 “Connection” header 可以选择被清理,这样即便是 Client 和 Nginx 之间是短连接,Nginx 和 upstream 之间也是可以开启长连接的。

 

【另外一种高级方式】

http {

map $http_upgrade $connection_upgrade {

default upgrade;

” close;

}

upstream backend {

server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;

server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;

keepalive 300;

}

server {

listen 8080 default_server;

server_name “”;

location / {

proxy_pass http://backend;

 

proxy_connect_timeout 15;       #与upstream server的连接超时时间(没有单位,最大不可以超过75s)

proxy_read_timeout 60s;           #nginx会等待多长时间来获得请求的响应

proxy_send_timeout 12s;           #发送请求给upstream服务器的超时时间

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection $connection_upgrade;

}

}

}

 

http里面的map的作用是:

让转发到代理服务器的 “Connection” 头字段的值,取决于客户端请求头的 “Upgrade” 字段值。

如果 $http_upgrade没有匹配,那 “Connection” 头字段的值会是upgrade

如果 $http_upgrade为空字符串的话,那 “Connection” 头字段的值会是 close

 

【补充】

NGINX支持WebSocket。

对于NGINX将升级请求从客户端发送到后台服务器,必须明确设置Upgrade和Connection标题。

这也算是上面情况所非常常用的场景。

HTTP的Upgrade协议头机制用于将连接从HTTP连接升级到WebSocket连接,Upgrade机制使用了Upgrade协议头和Connection协议头。

为了让Nginx可以将来自客户端的Upgrade请求发送到后端服务器Upgrade和Connection的头信息必须被显式的设置

 

【注意】

在nginx的配置文件中,如果当前模块中没有proxy_set_header的设置,则会从上级别继承配置

继承顺序为:http, server, location

 

如果在下一层使用proxy_set_header修改了header的值,则所有的header值都可能会发生变化,之前继承的所有配置将会被丢弃

所以,尽量在同一个地方进行proxy_set_header,否则可能会有别的问题。

 

·【参考】

Nginx中文官方文档: http://www.nginx.cn/doc/

测试参考文档: https://www.lijiaocn.com/问题/2019/05/08/nginx-ingress-keep-alive-not-work.html

keep-alive参考文档: https://wglee.org/2018/12/02/nginx-keepalive/

mysql命令行不再提醒警告


在Linux命令行里输入mysql 用户密码就会提示:

Warning: Using a password on the command line interface can be insecure.

即使我们用2>想给它重定向也无济于事。

有一个方法可以隐藏掉这个警告。

 mysql_config_editor set --user=root --socket=/tmp/mysql.sock --password

输入密码即可,这样就可以把密码保存到一个配置文件里 ~/.mylogin.cnf, 这个文件不能直接cat查看

以后再次使用mysql时,直接敲mysql命令即可完成登录。

搭建一个局域网http的yum源


1. 先创建一个目录,作为存在rpm包的目录
mkdir /data/yumdata
2. 拷贝ISO镜像文件中的rpm包到/data/yumdata/
mount /dev/cdrom /mnt/
cp /mnt/Packages/*rpm /data/yumdata
【小常识】 可以在/data/yumdata/下面创建子目录,然后把rpm包放到子目录下面,也可以被识别到
3. 创建repository
createrepo /data/yumdata/
如果rpm包有增加,需要执行
createrepo --update /data/yumdata/
4. 安装nginx,提供http服务
yum install epel-release
yum install nginx
当然,如果无法使用yum,需要下载nginx源码包,并编译安装
配置nginx.conf,使其可以通过IP地址访问到/data/yumdata
参考配置文件:
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /data/yumdata;
        autoindex on;

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
编辑完配置文件,启动nginx服务
systemctl start nginx
5. 客户端上配置repo文件
cd /etc/yum.repos.d
mkdir bak
mv *.repo  bak/  //把系统自带的repo挪走
vim my.repo //内容如下
[aming]
name=myserver
baseurl=http://192.168.133.140
gpgcheck=0
enabled=1
6.  做一个镜像yum源
比如可以把系统默认的源给镜像到局域网来,思路是用rsync工具把远程的rpm包同步到这台局域网的对应目录下即可。
rsync -av rsync://mirrors.ustc.edu.cn/centos/7/os/x86_64/Packages/  /data/yumdata/
7. 当局域网的rpm库有更新时,除了服务端执行“createrepo  –update /data/yumdata/”外,客户端上也需要执行
yum  clean all   //删除缓存

Nginx代理tomcat https跳转到http


问题

网站采用了 Nginx 反向代理 Tomcat 的方式来负载均衡。

Nginx使用https,默认端口443。Tomcat使用http,端口8080

结果今天后台操作停留时间过长session超时后,跳转到登录页面时出现无法访问错误。如图:

http_error

分析

可以看到,出错的原因应该是跳转的时候加上了web默认80端口,而https默认的端口并不是80,所以导致无法访问。

后台无session时跳转代码:

  1. requestURL = HttpUtils.encodeUrl(requestURL);
  2. String path = request.getContextPath();
  3. String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path
  4. + "/";
  5. return basePath + "user/login?goUrl=" + requestURL;

代码跳转的是绝对路径,包括schema和port,问题应该就在这里。

经过调试发现获取到的request.getScheme()总是http而非实际的https,相应的request.getServerPort()也就成了80。

端口是80而非Tomcat本身的8080说明获取到的确实是nginx转发过来的请求,那schema为什么得不到呢?

解决

通过查找资料,发现nginx请求转发时必须设置proxy_set_header才能让被代理的tomcat获取到正确的客户端ip等信息。

但是之前已经设置过nginx的proxy_set_header,而获取ip等也毫无问题:

  1. proxy_set_header X-Real-IP $remote_addr;
  2. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  3. proxy_set_header Host $http_host;
  4. proxy_set_header X-Forwarded-Proto $scheme;
  5. proxy_redirect http:// $scheme://;
  6. proxy_pass http://localhost:8080;#转向tomcat处理

上面有一行设置了proxy_redirect,所以浏览器地址栏的schema仍然是https.

接着查找资料,发现要传递schema,在Tomcat端也需要相应配置。

  1. <Engine>
  2. <Valve className="org.apache.catalina.valves.RemoteIpValve"
  3. remoteIpHeader="X-Forwarded-For"
  4. protocolHeader="X-Forwarded-Proto"
  5. protocolHeaderHttpsValue="https"/>
  6. </Engine >

加上后,重启Tomcat,一切正常了!

你不知道的vim小秘密


大家先了解一些背景知识:

1) 给文件增加了i权限,那文件不能被更改,不能删除,也不能修改名字以及权限。

2) 给文件增加a权限,文件可以追加内容,不能删除,不能修改内容,不能修改名字以及权限。

3) vim一个文件,如果不正常退出,再次编辑时是会提示一些信息的,并且有一个隐藏的文件.xxx.swp

了解以上知识后,再来看下面的现象:

1) 如果给一个文件增加a权限,用vim编辑文件,增加内容(注意是在文件末尾增加内容,不要修改其他内容),并不会成功。

2) 如果给一个目录增加i权限或者a权限,在该目录下面vim一个文件,更改文件内容可以正常保存。

既然a权限可以追加内容,那为何vim一个文件在末尾增加内容不能成功?既然i权限不能修改,那为何在目录里面变更文件内容却可以成功?

关于这两点,你有没有疑惑?下面我们来分析原因。

先不管i或者a权限,在一个没有i或者a权限的目录下,编辑一个没有i或者a权限的文件,用strace来查看其执行过程。

mkdir /tmp/test

strace vim /tmp/test/aminglinux.txt 2>/tmp/vim.log

写入一个数字1,然后保存退出。再来查看vim.log的内容。

less /tmp/vim.log

大部分内容你不用关心,只需要看这几行:

stat(“/tmp/test/aminglinux.txt”, 0x7fff072ecb10) = -1 ENOENT (No such file or directory)

access(“/tmp/test/aminglinux.txt”, W_OK) = -1 ENOENT (No such file or directory)

open(“/tmp/test/aminglinux.txt”, O_RDONLY) = -1 ENOENT (No such file or directory)

readlink(“/tmp/test/aminglinux.txt”, 0x7fff072eb360, 4095) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDONLY) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL, 0600) = 3

open(“/tmp/test/.aminglinux.txt.swx”, O_RDONLY) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swx”, O_RDWR|O_CREAT|O_EXCL, 0600) = 4

unlink(“/tmp/test/.aminglinux.txt.swx”) = 0

unlink(“/tmp/test/.aminglinux.txt.swp”) = 0

stat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff072ec310) = -1 ENOENT (No such file or directory)

lstat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff072ec3e0) = -1 ENOENT (No such file or directory)

lstat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff072ec8a0) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0600) = 3

stat(“/tmp/test/aminglinux.txt”, 0x7fff072eac40) = -1 ENOENT (No such file or directory)

stat(“/tmp/test/aminglinux.txt”, 0x7fff072ebe20) = -1 ENOENT (No such file or directory)

stat(“/tmp/test/aminglinux.txt”, 0x7fff072eadf0) = -1 ENOENT (No such file or directory)

write(1, “\”/tmp/test/aminglinux.txt\””, 26) = 26

stat(“/tmp/test/aminglinux.txt”, 0x7fff072ec050) = -1 ENOENT (No such file or directory)

open(“/tmp/test/aminglinux.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=2, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=2, …}) = 0

unlink(“/tmp/test/.aminglinux.txt.swp”) = 0

看起来乱乱的,其实大概的过程就是vim  /tmp/test/aminglinux.txt时,先看有没有.aminglinux.txt.swp以及.aminglinux.txt.swx,因为这两个文件就是vim产生的临时文件,swp先产生,如果swp存在就产生第二个swx。写入的内容先存到swp里,当保存退出vim时,再把swp的内容存到aminglinux.txt里,最后删除掉swp文件。

有了这个认识之后,我们再来分析上面提到的现象1。如果文件给了a权限,那么在编辑该文件时,会产生swp文件,当保存退出时,swp文件内容会写入该文件,这相当于更改该文件,很线上a权限是不允许的。

再来分析现象2,按照我们的推测,如果目录给了a权限,增加文件没问题,也就是说产生swp或者swx文件没有问题,当然把swp或者swx内容写入到文件里时也不会有问题,但swp或者swx文件却不会被删除了,所以再次编辑文件时就会提示临时文件已经存在了。但这并不会影响修改文件内容。

如果给目录设置了i权限的话,vim编辑文件,要产生swp或swx肯定会出错啊,但为何依然能正常编辑文件? 下面继续用strace来分析一下。

chattr +i  /tmp/test

strace vim /tmp/test/aminglinux.txt 2> /tmp/vim.log

看vim.log里面和aminglinux.txt相关的信息

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

access(“/tmp/test/aminglinux.txt”, W_OK) = 0

open(“/tmp/test/aminglinux.txt”, O_RDONLY) = 3

readlink(“/tmp/test/aminglinux.txt”, 0x7fff49efc6f0, 4095) = -1 EINVAL (Invalid argument)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDONLY) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied)

stat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff49efd6a0) = -1 ENOENT (No such file or directory)

lstat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff49efd770) = -1 ENOENT (No such file or directory)

lstat(“/tmp/test/.aminglinux.txt.swp”, 0x7fff49efdc30) = -1 ENOENT (No such file or directory)

open(“/tmp/test/.aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0600) = -1 EACCES (Permission denied)

readlink(“/tmp/test/aminglinux.txt”, 0x7fff49efc6f0, 4095) = -1 EINVAL (Invalid argument)

open(“/root/tmp/aminglinux.txt.swp”, O_RDONLY) = -1 ENOTDIR (Not a directory)

open(“/root/tmp/aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL, 0600) = -1 ENOTDIR (Not a directory)

stat(“/root/tmp/aminglinux.txt.swp”, 0x7fff49efd6a0) = -1 ENOTDIR (Not a directory)

lstat(“/root/tmp/aminglinux.txt.swp”, 0x7fff49efd770) = -1 ENOTDIR (Not a directory)

lstat(“/root/tmp/aminglinux.txt.swp”, 0x7fff49efdc30) = -1 ENOTDIR (Not a directory)

open(“/root/tmp/aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0600) = -1 ENOTDIR (Not a directory)

readlink(“/tmp/test/aminglinux.txt”, 0x7fff49efc6f0, 4095) = -1 EINVAL (Invalid argument)

open(“/var/tmp/aminglinux.txt.swp”, O_RDONLY) = -1 ENOENT (No such file or directory)

open(“/var/tmp/aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL, 0600) = 4

open(“/var/tmp/aminglinux.txt.swx”, O_RDONLY) = -1 ENOENT (No such file or directory)

open(“/var/tmp/aminglinux.txt.swx”, O_RDWR|O_CREAT|O_EXCL, 0600) = 5

unlink(“/var/tmp/aminglinux.txt.swx”)   = 0

unlink(“/var/tmp/aminglinux.txt.swp”)   = 0

stat(“/var/tmp/aminglinux.txt.swp”, 0x7fff49efd6a0) = -1 ENOENT (No such file or directory)

lstat(“/var/tmp/aminglinux.txt.swp”, 0x7fff49efd770) = -1 ENOENT (No such file or directory)

lstat(“/var/tmp/aminglinux.txt.swp”, 0x7fff49efdc30) = -1 ENOENT (No such file or directory)

open(“/var/tmp/aminglinux.txt.swp”, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, 0600) = 4

chmod(“/var/tmp/aminglinux.txt.swp”, 0644) = 0

open(“/tmp/test/aminglinux.txt”, O_RDONLY) = 3

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

access(“/tmp/test/aminglinux.txt”, W_OK) = 0

write(1, “\”aminglinux.txt\””, 16)      = 16

stat(“aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

access(“aminglinux.txt”, W_OK)          = 0

getxattr(“aminglinux.txt”, “system.posix_acl_access”, 0x7fff49efd050, 132) = -1 ENODATA (No data available)

stat(“aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=4, …}) = 0

open(“aminglinux.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0644) = 3

chmod(“aminglinux.txt”, 0100644)        = 0

setxattr(“aminglinux.txt”, “system.posix_acl_access”, “\x02\x00\x00\x00\x01\x00\x06\x00\xff\xff\xff\xff\x04\x00\x04\x00\xff\xff\xff\xff \x00\x04\x00\xff\xff\xff\xff”, 28, 0) = 0

stat(“/tmp/test/aminglinux.txt”, {st_mode=S_IFREG|0644, st_size=6, …}) = 0

unlink(“/var/tmp/aminglinux.txt.swp”)   = 0

我相信你可以看到Permission denied的提示,这是因为当前目录有i权限,不能增加文件,也就不能在当前目录下生成临时文件。当然,vim如果遇到这样的问题,它还是会“曲线救国”的,于是先找/root/tmp/,但是并没有该目录,只好继续找/var/tmp/,这个目录存在,所以就在这个目录里生成了临时文件(并不是隐藏的)。后面的操作就不用多说了。

既然vim可以在/var/tmp/下生成临时文件,自然也可以在已经设置了i权限的目录里编辑文件的,这样现象2也解释通了。上面罗嗦了这么多,其实我就想表达如下观点:

vim编辑文件时,会在该文件所在目录生成临时隐藏文件.swp和.swx,如果那目录不可写就会到/root/tmp/下或者/var/tmp/下生成临时文件(非隐藏),当编辑的文件保存后,临时文件删除。