感悟
文件权限
- 所有对linux文件的操作,像替换jar包,复制文件,新增文件,处理完之后都要看一下文件的权限对不对。
- 在升级jar包的时候,有一个很重要的问题,回滚之后的jar也要注意权限。。。。。这是个坑呀。
- linux替换文件,拥有的是原文件的权限。
有一次替换jar包后,调用15822接口的Rest服务,报找不到服务器。然后开始排查,启动日志是正常启动了的,usf的日志是正常的,run和debug的日志也是正常啊的,netstat和lsof看接口15822接口也是在监听的。
后来发现是替换jar包后,这个jar包没有其他用户的读权限。。。。坑啊。但是为什么没有日志报错呢,猜测是因为usf框架接到请求后,去调用jar包然后每找到,然后线程就挂了,但是usf框架可能打的是debug日志,所以日志里面看不到。。。。。
crontab
crontab虽然可以设置系统级的运行(相当于ROOT用户在执行),也可以挂在某个用户下面进行运行。
这些都不影响crontab的任务的执行,只是说执行用户挂的是谁而已。因为crontab只要程序被唤起,就会去执行。
执行文件
在linux中,有的时候我们用./,然后用tab去联想我们当前目录下面的脚本的时候,发现并没有联想文件,那是因为当前目录下的可执行文件我们都没有执行权限,所以不会给我们联想的提示。
- ./xxx是执行当前目录下的文件。
- xxx这种什么都不加是去PATH路径下找文件执行。
当我们输入一个ls时,这个指令是怎么去执行的
- 以相对/绝对路径执行指令,例如“ /bin/ls ”或“ ./ls ”;
- 由 alias 找到该指令来执行;
- 由 bash 内置的 (builtin) 指令来执行;
- 通过 $PATH 这个变量的顺序搜寻到的第一个指令来执行。
可以通过type -a xxx来查看xxx指令的搜寻顺序
sh,./,source的区别
- source(或者小数点+一个空格)是在当前的bash程序中继续执行
- sh a.sh和./a.sh需要的权限不一样(但是两者都是子程序内执行)
- sh会使用bash来执行a.sh这个文件,只需要使用r的权限就能够执行
- ./a.sh需要有rx权限才能执行这个文件。(因为它需要自己先去执行,才能根据sh里面的内容去找bash)
静态库和动态库
linux程序经常需要用到库文件,静态库就是编译时已经复制到程序里面的,动态库是运行时再去外部找文件去进行引用的。
动态库的位置查找顺序是:
- 首先在程序编译的时候去程序编译时由gcc的-R或者-rpath查找
- 从LD_LIBRARY_PATH的环境变量查找
- 系统的默认路径/usr/ 和/usr/local下面遍历查找
- 从/etc/ld.so.conf里面记录的绝对路径查找(有的需要读缓存/etc/ld.so.cache,那么需要重新执行/sbin/ldconfig才能把更新的/etc/ld.so.conf刷到缓存/etc/ld.so.cache里面)
所以我们很多程序在编译的时候不指定lib的位置(这些lib本身是在源程序的文件夹lib里面,但是你不指定的话,它也不会遍历自己的文件夹),那么直接把整个程序的文件夹都复制到/usr/local/下面就好了。
后台运行
nohup xxxx & 可以把程序在后台运行,$防止ctrl+c,nohup防止关闭终端,这样怎么都不会终止进程,但是也有一个坑,就是如果你执行程序用的相对路径的话,可能你一关闭终端,程序就挂了,没找到具体的原因。
网络问题
- 问题:用ping能ping通,但是用telnet显示no route to host。(另外用nc连不上,用traceroute能发现路由)。
解决办法:清除防火墙:iptables -F
。清除完之后就好了。 - es启动后,需要把elasticsearch.yml的network.host改成你自己的ip,不然默认绑定的是127.0.0.1,外网访问不了。
- netstat的localAddress如果显示的是127.0.0.1:port的话,这个port是只能本机连接的,不能通过外网访问
网络判断
查看网口
- 先
Ping Ip
- 再
telnet ip port
(这个方法如果对方机器没装telnet好像不能用) - 用
nc ip port
用来查看端口是否打开。 - 用
traceroute -n -T -p port ip
可以发一个TPC的数据包过去。 - 查看各种TCP的连接状态的个数
netstat -n |awk '/^tcp/ {++S[$NF]} END {for (a in S) print a, S[a]}'
本机端口和线程
Netstat -ntupl
看端口占用。Lsof -I
看端口占用Ps -ef
看线程
其他命令
- 查看本机系统版本:
cat /etc/issue
cat /proc/version
- 安装rpm包:
rpm -ivh
- 查看rpm包安装状态:
rpm -qa
- 解压tar.gz:
tar -zxvf
(使用前也要看看包的权限,不是什么权限都能解的,如果不是gzip格式的话可以不用z参数) - 上传到远程:
scp [本机文件、-r 本机目录] user@ip:路径
(下载远程文件到本地的话把顺序互换一下)
执行追踪
linux执行命令碰到问题的时候,
有的时候往上没有教程,自己又不知道什么原因,可以追踪一下命令的执行过程。
用strace,lstrace,truss。
比如:strace kadmin.local > /tmp/2.log 2>&1
加解密
在unix环境使用OPENSSL进行AES的加解密。
首先操作系统支持openssl库的都可以进行加解密的活动,简单的话,推荐使用AES128-ECB的加密方式,如下:
echo -n "明文" | openssl enc -aes-128-ecb -a -e -pass pass:"密钥" -nosalt;
输出的就是密文啦,可进行交互;
解密方式是:
echo "密文" | openssl enc -aes-128-ecb -a -d -pass pass:"密钥" -nosalt
分析CPU占用高问题
-
查看CPU占用高的进程中,哪个线程占用CPU高
可通过以下三种方式查看:
top
中按SHIFT+H查找哪个线程占用高(之后再按c可以显示command的参数详情,也可以配合ps -ef |grep PID
来查看这个进程的具体参数)。top -H -p PID
命令查看哪个线程占用高(这里的线程也是PID。。。)。ps -mp PID -o THREAD,tid,time
-
将线程id转换为16进制
printf "%X\n" thread_id
-
导出进程的所有线程信息
使用jstack PID可以导出进程下的所有线程信息,并用上步获得的16进制线程ID去查找问题线程的状态信息。
也可以采用如下方式查找问题线程信息:
jstack PID | grep 线程的16进制ID(0x开头,字母小写)
好像输出到文件更好用,
jstack PID >> /home/cmp/logs/pf1.txt
(这个文件可以多生成几份,看看哪些进程一直在的)jstack只能在运行该JAVA程序的用户下执行才能成功,所以要切换用户。
其实jastack所看的文件是/tmp/hsperfdata_xxx目录下的,看这个xxx就是用户,文件夹里面的就是线程文件。
-
接下来如果发现占用CPU高的线程是GC线程。我们就要来分析一下是什么原因导致的。
可以用
jmap -dump:format=b,live,file=/home/cmp/dump1.hprof PID
来把dump打印出来。然后用Eclipese Memory Analynizer来分析。直接打出来的dominator tree可以很容易看出各个线程占用的内存量。找到了占用内存多的线程还可以看其调用关系。
-
可以在java的执行参数中(比如JAVA_OPTS里面)增加内存溢出导出dump
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/cmp/tmp/
,如果想打印GC详情,还可以增加-XX:+PrintGCDetails
查看文件大小
-
仅仅查看文件大小
ll -h
-
查看文件夹的大小
du -h --max-depth=1
-
查看各磁盘大小
df -h
-
搜索某文件夹下大于一定容量的文件
find / -size +100M -exec ls -lh {} \;
查看主机
vi /etc/hosts
或者hostname
su - cmp
su - cmp
相比于su cmp
,不仅仅是切换了目录,而且还刷新了环境,也就是说,如果你用su cmp
,切换过去之后你用的环境变量还是之前的环境变量。
shell脚本
- shell里面很多莫名其妙的错误,竟然是因为多打了空格。。。。。
- [[]]和[]里面的每个单元间都需要用空格隔开。
- shell如果在if里面要使用正则表达式,需要用[[]]括起来,模糊匹配符号是=~,且含有正则表达式的部分不能使用单、双引号括起来。正确事例如下:
if [[ "$toberename" =~ .*\.bak ]]
- 同时输出到桌面和文件,用
xxxx 2>&1 | tee -a xxx.txt
只有使用了2>&1才能把正确的和错误的信息都输出出来。 - shell不支持变量嵌套像KaTeX parse error: Expected '}', got 'EOF' at end of input: {{i}},需要使用
eval echo '$'"${i}"
- shell的命令执行是否成功,不需要去说看看抓取的命令是否是一个空的或者空字符串,因为shell自带有返回码,可以用 ? ( ?( ?(?的本质是获取脚本的返回值)获取。成功的情况一般是返回0,不成功是非0.这个可以用if进行判断。
- if可以测试条件返回码的返回情况([]跟test是一个意思),就是[]的返回值(跟脚本执行完的$?输出是一个东西),返回0的时候相当于true,返回其他的时候相当于false。
- (())里面可以用C语言的所有数学表达式,只能进行数学表达式运算,所以变量也就不需要加$。
- [[]]是高级的字符串处理功能,相当于在[]的基础上增加了正则表达式的功能。
- 在调用所属者不是你的脚本的时候,会出现一个问题,就是一些命令的别称你没有。。所以要是所属者在脚本里写了一些别称像pf,会导致你用不了他的脚本。。。。所以在脚本里最好用su来写那些只有所属者有的脚本
- shell是大小写敏感的,所以在使用搜索的时候,最好使用
find -iname
,使得大小写都能搜出来。 - 脚本里指定的shell和sh不兼容,则不能以sh去执行脚本,会报syntax error。
"X${has_kafka}" != "X"
。在shell里面,判断某个变量是不是为空,一定要构造一个字符串,不然单独判空会报语法错误。一般的做法是借助一个字符串辅助构造一个,比如随便选个字母X,构造X$’{}。- 写脚本,在使用grep的时候一定要注意,因为直接在文件外grep文件里面的内容时,根据命令的不同,最后结果的参数是不一样的。比如下图就多了一段文件名。所以最好的使用方法是先用cat读入文件,然后再用管道符|在文件里面进行grep。像
cat xxx | grep bbb
grep quota ./*
的最后输出也会是以./文件夹名:文件内容
,所以不能直接抓取到日志中。if [ -d $xxx ]
这个写法,当 x x x 为 空 时 , 会 被 当 做 t r u e , 是 个 坑 ! ! ! 一 定 不 能 这 么 写 , 要 写 成 ‘ i f [ − d " xxx为空时,会被当做true,是个坑!!!一定不能这么写,要写成`if [ -d " xxx为空时,会被当做true,是个坑!!!一定不能这么写,要写成‘if[−d"xxx" ]`,这个坑是因为在[]中的变量没有加引号,此时因为解析$xxx之后再判断的条件式里面有几个元素,所以结果就相当于if [ -d ],这个的结果就是true的。要注意,[]做条件判断时,所有的元素要加双引号或者单引号,元素之间一定要用空格隔开。- 脚本中的一些特殊表达式的用法
- echo *.jpg;这里啥都没加,*会作为通配符,匹配所有字符。
- echo “*.jpg”里面会先解析变量,再作为字符串元素,总结就是”“允许变量扩展,但是不允许通配符扩展;
- echo ‘*.jpg’是直接将所有特殊符号都转义,总结就是防止任何扩展;
- ${}是可以放变量;
- ``或者$()是执行代码;
- $(())是执行计算;
- []是相当于test,要注意的是为了和很多特殊意思区分,[]的每一个元素之间都要用空格隔开,且为了防止各种一个变量先被解析了之后被当成了两个元素,所有的元素都要用“”引起来。