Linux总结

1. 当前进程和子进程

tt() {
    echo 'in tt'
    exit 2
}

#tt         # ①输出: in tt
t=`tt`      # ②输出: main
echo 'main'

①中函数直接在当前主进程中被调用,函数tt退出整个进程就退出了
②中函数被调用的结果被赋值给变量t,函数是在子进程中被调用的,子进程的退出不会影响主进程的退出,所以最终执行了echo 'main'

实例,函数的返回值和返回状态码:

num=$1

tt() {
    echo 'it is tt'
    if [ $num -eq 1 ];then
        exit 2
    else
        exit 3
    fi

}

t=`tt`                    #在子进程中调用,拿到函数的结果和返回状态码
return_tag=$?     #状态码

echo "t: ${t}, return_tag: ${return_tag}"

如上,tt函数有返回值,并且状态码为2或者3(根据用户的参数)

执行函数得到如下输出结果:

[jinbo2@192 test]$ ./t2.sh 2
t: it is tt, return_tag: 3
[jinbo2@192 test]$ ./t2.sh 3
t: it is tt, return_tag: 3
[jinbo2@192 test]$ ./t2.sh 1
t: it is tt, return_tag: 2

通过t=tt & $?,拿到了函数tt的返回结果和状态码


2. 文件锁

当多个进程同时对同一个文件进行写入修改的时候,修改不成功或者产生脏数据的问题,有没有一种机制,让许多并行的任务能串行进行一系列操作

2.1 引子:让程序保持单实例运行

有的脚本我们希望同一时间只有一个实例在运行,可以这样:

lock_file=/var/run/rsync_files.pid

if [ -e $lock_file ];then                
   echo "Other instance is running!" 
   exit 0          
else
   touch $lock_file
   chmod 600 $lock_file
fi

trap "rm -f ${lock_file}; exit" 0 1 2 3 9 15

....

通过判断文件是否存在来使脚本继续走下去
trap "rm -f ${lock_file}; exit" 0 1 2 3 9 15的意义:因为脚本是通过判断文件而运行的,我们必须保证当服务器上没有该实例时这个文件不能存在,所以**当程序不是自己退出的时候,trap能够捕获这些异常信号并删除这个文件

2.2 文件夹锁(不推荐!实际生产并没有达到要求)

有这样一个问题:我有一个定时任务脚本,脚本步骤的最后都是把检查的结果最后修改写入到一个文件里,这个脚本有很多定时任务并且很多任务是同一个时间运行的,我们发现那些同时运行的任务的结果并没有都写入到那个文件里面去,而部分又是正常的
有没有一种机制,能让许多并行的脚本能串行进行一系列操作。比如多个脚本对文件进行依次读写,以免产生脏数据。无疑,如果脚本如果shell中有一种“锁”的机制,就可以解决这个问题。原来只使用过API级别的锁,脚本中的锁还真没用过。其实,如果要在shell脚本实现锁,需要满足两个条件:

这句话出处: https://blog.goquxiao.com/posts/2013/08/31/filelock_in_script/

  1. 一个全局可见的状态
  2. 一种“检测 + 加锁”的原子操作

上面的2.1小结,通过检查文件是否存在并不能满足我们的需求,因为检测文件和新建文件无法做到原子性。我们手动对脚本进行启动的时候不存在那个问题,但是当同时启动多个进程的时候(比如定时任务或多进程),其并不能满足

检测文件和新建文件无法做到原子性,但是mkdir操作,却能做到原子地检测文件夹和创建文件夹:

........

## ------------------------
while :
do
    # 加锁
    mkdir ${LOCK_DIR}
    if [ $? -eq 0 ];then
        break   
    fi  
done
trap "rm -rf ${LOCK_DIR}; exit" 0 1 2 3 4 9 15 

## ----------------------保证这块区域的操作是顺序执行的----------
tag=`check`  
return_tag=$?  # return_tag=0或非0
t=`date +%F' '%T`
if [ $return_tag -eq 0 ];then
    warning="${business_warning},checktime: ${t}"
        to_beita "${tag}" "${warning}"
elif [ $return_tag -eq 2 ];then
    warning="配置文件错误,checktime: ${t}"
        to_beita 'configError' "${warning}"
elif [ $return_tag -eq 3 ];then
    warning="sql执行失败,checktime: ${t}"
        to_beita 'sqlError' "${warning}"
fi
# ----------------------------------------------------------------------------------------

# 解锁
rm -rf ${LOCK_DIR}

当脚本想对于竞争数据进行操作,就mkdir某个文件夹,根据返回码得知申请所是否成功,申请成功、完成操作之后再rm -rf就可以实现了。
同样使用trap捕获异常退出
更简单的理解‘原子性’: 多个进程mkdir同时只会有一个能成功,而多个进程判断文件是否存在或者touch文件是能够同时成功的

2.3 flock命令,更高级的用法(强烈推荐)

flock - Manage locks from shell scripts

    flock [-sxon] [-w timeout] lockfile [-c] command...
    flock [-sxon] [-w timeout] lockdir [-c] command...
    flock [-sxun] [-w timeout] fd

-s 为共享锁,在定向为某文件的FD上设置共享锁而未释放锁的时间内,其他进程试图在定向为此文件的FD上设置独占锁的请求失败,而其他进程试图在定向为此文件的FD上设置共享锁的请求会成功。
-e 为独占或排他锁,在定向为某文件的FD上设置独占锁而未释放锁的时间内,其他进程试图在定向为此文件的FD上设置共享锁或独占锁都会失败。只要未设置-s参数,此参数默认被设置。
-u 手动解锁,一般情况不必须,当FD关闭时,系统会自动解锁,此参数用于脚本命令一部分需要异步执行,一部分可以同步执行的情况。
-n 为非阻塞模式,当试图设置锁失败,采用非阻塞模式,直接返回1,并继续执行下面语句。
-w 设置阻塞超时,当超过设置的秒数,就跳出阻塞,返回1,并继续执行下面语句。
-o 必须是使用第一种格式时才可用,表示当执行command前关闭设置锁的FD,以使command的子进程不保持锁。
-c 执行其后的comand。

使脚本同时只运行一个实例:

LOCKFILE=lockfile
exec 9 >${LOCKFILE}
flock  -n -e  9    # 设置锁失败时,返回1;设置锁成功时,返回0
if [ $? -eq 1 ];then
    echo "There  is another script running, exit"
    exit
fi

让许多并行的任务能串行进行一系列操作:

#!/bin/bash

basepath=$(cd `dirname $0`;pwd)
filename=${basepath}/file_be_writed
LOCK_FILE=${basepath}/lockfile
#LOCK_DIR

#while :
#do
#    mkdir ${LOCK_DIR}
#    if [ $? -eq 0 ];then
#        break  
#    fi 
#done
#trap "rm -rf ${LOCK_DIR}; exit" 0 1 2 3 4 9 15

exec 9>${LOCK_FILE}
flock  -e  -w  15  9      #等待LOCK_FILE释放锁,阻塞等待15s

sed -i "$1c $2" $filename
#rm -rf ${LOCK_DIR}

这个用法明显比通过创建目录来判断(死循环)更加高效和简洁
直接对脚本要修改的文件加锁不好,即不要直接exec 9>${filename},不解释

通过在脚本里面对文件加锁来保持部分操作的串行是很棒的,我们还可以通过在脚本执行的时候使用flock命令,比如: flock -e -w 15 {锁文件} -c "{脚本路径}",但是这种明显不够好,因为它阻塞了整个脚本的运行,而我们这种是能对指定的一些步骤加锁的。


3. 子进程和管道

3.1 组命令和子进程

参考文档: https://www.cnblogs.com/mianbaoshu/p/12069777.html

组命令:
子进程:

Shell 脚本是从上至下、从左至右依次执行的,即执行完一个命令之后再执行下一个。如果在 Shell 脚本中遇到子脚本(即脚本嵌套,但是必须以新进程的方式运行)或者外部命令,就会向系统内核申请创建一个新的进程,以便在该进程中执行子脚本或者外部命令,这个新的进程就是子进程。子进程执行完毕后才能回到父进程,才能继续执行父脚本中后续的命令及语句。

子进程总结:

子 Shell 虽然能使用父 Shell 的的一切,但是如果子 Shell 对数据做了修改,比如修改了全局变量,这种修改也只能停留在子 Shell,无法传递给父 Shell。不管是子进程还是子 Shell,都是“传子不传父”
子 Shell 才是真正继承了父进程的一切,这才像“一个模子刻出来的”;普通子进程和父进程是完全不同的两个程序,只是维持着父子关系而已。


3.2 管道

包含管道的shell命令,则管道前后的命令分别由不同的进程执行,然后通过管道把两个进程的标准输入输出连接起来,就实现了管道。

例如:

grep "error"  minicom.log | awk '{print $1}'

这句话的作用非常简单
通过grep命令在minicom.log中检索含有error关键字的行
通过awk命令打印grep的输出结果中每一行的第一个字段

在shell中要实现这样的效果,有4个步骤:

  1. 创建pipe
  2. fork两个子进程执行grep和awk命令
  3. 把grep子进程的标准输出、标准错误重定向到管道数据入口
  4. 把awk子进程的标准输入重定向到管道数据入口

这样就实现了shell管道:grep把结果输出到管道,awk从管道获取数据

管道是如何执行的?
先看下面的测试:

[yw@manager2 ~]$ ps aux | grep sshd
root       933  0.0  0.0 106052   676 ?        Ss   Jan25   0:00 /usr/sbin/sshd -D
root     52152  0.0  0.0 148372  1680 ?        Ss   15:04   0:03 sshd: root@pts/0,pts/1
root     56181  0.2  0.1 148372  5396 ?        Ss   17:23   0:00 sshd: root@pts/2
yw       56270  0.0  0.0 112812   968 pts/2    R+   17:24   0:00 grep --color=auto sshd
[yw@manager2 ~]$ 
[yw@manager2 ~]$ ps aux | (sleep 2;grep sshd)
root       933  0.0  0.0 106052   680 ?        Ss   Jan25   0:00 /usr/sbin/sshd -D
root     52152  0.0  0.0 148372  1680 ?        Ss   15:04   0:03 sshd: root@pts/0,pts/1
root     56181  0.2  0.1 148372  5396 ?        Ss   17:23   0:00 sshd: root@pts/2
[yw@manager2 ~]$

一个显示 grep sshd 的进程,一个不显示

参考一个文档: http://m.blog.chinaunix.net/uid-7692530-id-2567612.html
该文档中有这样一段话:Pipelined processes do not execute one after another, or one before another. They are all executed at once. So, “ps aux | grep mysqld | wc -l” immediately spawns ps, grep, wc, then sets the standard output of one to the standard input of the other

也就是说:管道子进程不会一个接一个地执行,它们全部立即执行。因此,“ ps aux | grep mysqld | wc -l”会立即生成ps,grep,wc,然后将一个的标准输出设置为另一个的标准输入


3.3 用命名管道代替普通管道
cd ${src}                            
/usr/local/inotify-tools/bin/inotifywait -mrq --format  '%e %w%f' -e create,attrib,close_write,move ./ | while read file
do
    ......
done

从上面的讨论中我们知道,上面的脚本将产生两个子进程(普通管道),有没有办法改变脚本进程的结构呢?

先看一个简单的示例,基于生产消费者模型,使用命名管道:

#! /bin/bash

tmp_file=/tmp/$$.fifo

mkfifo $tmp_file
exec 8<>${tmp_file}
rm -f ${tmp_file}

echo '23' >&8
echo '11' >&8
echo hello

while read line
do
    echo $line
done <&8

执行脚本依次打印hello 23 11,并等待(管道没有结束)

是的,我们使用命名管道代替普通管道,这样就不用同时创建两个子进程了:

# 使用命名管道代替管道
tmp_file=/tmp/$$.fifo

mkfifo $tmp_file
exec 8<>${tmp_file}
rm -f ${tmp_file}

cd ${src}    
{                        
/usr/local/inotify-tools/bin/inotifywait -mrq --format  '%e %w%f' -e create,attrib,close_write,move ./  >&8
}&

while read file
do
    ......
done  <&8

inotify将监听到的事件写入到命名管道(生产者,放入后台)
while循环从管道中一直读取处理数据(消费者)
通过pstree查看当前脚本运行后的进程树:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271