Linux shell脚本单例模式实现

一、说明

关于单例模式,最开始的是一些小工具,运行起来后再点击运行时会提示已经运行了一个实例,觉得挺有意思但也没有很在意。

前段时间看了前领导的一段代码不太懂是做什么用的,同事查了下资料说是为了实现单例模式,讨论之下才知道单例模是是设计模式中的一种,具体表现也即上边说的只能运行一个实例。

上周被反馈说写的shell脚本在系统是运行了好多个进程,排查之下发现是yum命令一直等不到锁导致整个脚本卡住所致,脚本每次运行都会拉起一个卡死的进程。由此感觉shell脚本也应当考虑一下单例模式。

二、如果之前已有进程则结束当前进程的单例模式

#!/bin/bash
main(){
    # $0是当前文件的文件名
    # 如果运行是bash test.sh则$0就是test.sh
    # 如果运行是bash /tmp/test.sh则$0就是/tmp/test.sh
    # 如果不管怎么运行都只想要文件名,可以basename $0也可以file_name=${0##*/}
    file_name=$0
    # 如果匹配当前文件名的进程数量大于1,即文件实例不只当前进程,则退出当前进程
    # 从认知上来说应该是大于1,这里要大于2的原因,后边说
    if [ `pgrep -f ${file_name} | wc -l` -gt 2 ]
    then
        echo "${file_name} process existed, will be exit."
        exit 1
    fi
    # sleep 60
}
main

三、如果之前已有进程则结束之前的进程继续当前进程的单例模式

#!/bin/bash
main(){
    # $0是当前文件名
    file_name=$0
    # 文件除当前进程外的的所有其他进程
    pid_list=$(pgrep -f ${file_name} | grep -v ^$$\$)
    # 逐个进程杀除
    for tmp_pid in ${pid_list}
    do
      # 先杀除其所有子进程
      pkill -P ${tmp_pid}
      # 如子进程处于卡死状态,无法接收默认的15) SIGTERM状态进而直行退出;直接发送9) SIGKILL从内核将其杀死
      # pkill -9 -P ${tmp_pid}
      # 再杀除其自身
      # pkill -P 只会杀除子进程不会杀除其自身
      # 但有可能阻塞的子进程杀除后,自身进程后续步骤快速,导致kill去杀除时已没有该进程
      kill -9 ${tmp_pid}
    done
}
main

四、上边两个模式的注意事项说明

4.1 第二大节中的if语句为什么是大于2而不是大于1

从一般认知上说,我们运行了此脚本就启动了一个进程,如果此脚本对应的进程数大于1那就说明存在其他进程。但在上边代码中我们是要求大于2。

这是因为从观察来看,运行脚本启动了一个进程。然后在运行脚本中具体每一条命令时都会新建一个子进程去执行执行完后就结束该子进程;对于pgrep等普通的命令子进程名仍与父进程名一样。所以统计出来的进程数会是2,所以第二大节的代码要完成大于2。

另外这里强调pgrep等“普通命令”,sleep等命令的进程名则是自己。至于哪些算普通哪些算特殊暂时还没搞得很清楚。

4.2 这对第三大节中的代码有没有影响

既然每执行一条命令都会建一个子进程来执行,且子进程名与父进程名相一致,而第三大节的代码又相当于把当前父进程之外的所有进程都关闭。那会不会出现把当前父进程的子进程也杀掉导致当前父进程也出现问题的状况?答案是并不会。

扫描二维码关注公众号,回复: 8264815 查看本文章

pgrep传递给grep的是父进程pid、pgrep子进程pid及其他可能存在的先前运行脚本的pid;grep之后传给kill的是pgrep子进程pid及其他可能存在的先前运行脚本的pid。

我们前边也说过,在执行命令前启动子进程在命令执行完后退出子进程,所以等不到kill命令把pgrep子进程kill掉,在传递给grep时它就已经自己退出了。

参考:

https://www.mylinuxplace.com/bash-singleton-process/

https://stackoverflow.com/questions/15740481/prevent-process-from-killing-itself-using-pkill

猜你喜欢

转载自www.cnblogs.com/lsdb/p/12010128.html