您的位置:首页 > 运维架构 > Linux

Linux下shell执行程序

2018-12-27 15:32 513 查看

原理

Linux操作系统下shell脚本是开发项目中是比较常用的。shell脚本是如何被Linux系统所调用执行的呢,首先我们以ps命令为例,来解释一下shell脚本是如何被系统执行的。

命令:ps -o pid,ppid,pgid,sid,comm

其中:pid:自身进程id,ppid:父进程id,pgid:所属进程组id,sid:所属会话id,comm:命令。

在Linux系统下执行程序结果如下:

由上图可以看到,ps的父进程为bash,这正是我们所希望的。bash和ps命令两者位于同一会话和前台进程(1982)。不同的终端连接系统时bash所属会话和进程组都不一样,因此每个终端有自身的所属会话和进程组。

Shell调用本质其实是由shell(该系统的shell类型为bash)调用fork()函数创建一个进程,由该创建的子进程去调用一种exec函数去执行另一个程序。当子进程调用一种exec函数时,该子进程的执行的程序完全替换为新进程,因为调用exec函数并不创建新进程,所有前后进程的进程ID并不改变。exec程序只是用一个全新程序替换了当前进程的正文,数据、堆和栈。举个比较形象的例子,就好像一栋大楼里面刚开始是腾讯公司在里面办公,后来倒闭了,里面搬进了阿里巴巴公司,尽管大楼内的部署跟执行的任务已经改变,但这栋大楼还是之前的大楼。

在Linux系统下shell如果在一个管道中执行三个进程我们可以检验一下bash使用的进程控制方式。如下:

由上实验我们可知,bash使用的进程控制方式是:在执行每个命令调用时,都会由bash自身创建一个进程,然后由创建的子进程去调用exec函数去执行调用命令,而且bash创建的所有进程与bash同属一个会话(session id : 3983);而所有的子进程同属一个进程组(gpid:7151),该进程组ID为进程ps命令所属进程ID。如下: 

实例

笔者在项目开发中遇到过这种情况,需要在后台跑一个shell脚本程序,名称为test_lijd.sh。该脚本的任务是定时获取系统信息,当然这并不重要。在该进程执行前需检测有没有同名的进程(名称为test_lijd.sh)已经在运行,如果有,则立刻退出,没有则起该进程去完成任务。

代码 lijd_test.sh 如下:

[code]#!/bin/bash

########################
#filename : lijd_test.sh
#auth     : lijd
#data     : 2018-12-27
########################

main()
{
process_num=`ps aux | grep lijd_test.sh | grep -v grep |wc -l`

if [ $process_num -gt 0 ]
then
printf 'process_num : '$process_num'\n'
printf 'lijd_test.sh process has exist!\n'
exit -1
fi

while [ 1 ]
do
printf 'this is test!\n'
sleep 30
done
}

main

运行结果如下:

运行该脚本前执行运行进程数为0。在脚本中执行,可以看到该运行进程数为:2。

运行结果为什么是2?其中有一个为当前自身正在运行的进程,另一个则是在执行ps命令时由bash创建的子进程去调用,该进程名中也包含关键字lijd_test.sh。

那么如何实现这种功能呢?给计算除的结果减2看等不等于0?这种方法简单,但经过在不同的Linux系统做实验,有的Linux系统用这种方法依旧存在问题,由于不同Linux系统下使用的shell类型不同。

笔者提供一种方法,代码重新修改后如下:

[code]#!/bin/bash

#######################
#filename: lijd_test.sh
#auth    : lijd
#data    : 2018-12-27
#######################

function cheak_myself_process ()
{
ps -ef 1>/tmp/process.log

while read line
do
flag=`awk 'BEGIN {if (match(ARGV[ARGC-1], "lijd_test\\.sh$") != 0) print "true"; else print "false"}' $line 2>/dev/null`
if [ "${flag}" == "true" ]
then
auto_bypass_num+=1
fi
done </tmp/process.log

rm -rf /tmp/process.log
}

main()
{
declare -i process_num=0

cheak_myself_process

if [ $process_num -gt 0 ]
then
printf 'process_num : '$process_num'\n'
printf 'lijd_test.sh process has exist!\n'
exit -1
fi

while [ 1 ]
do
printf 'this is test!\n'
sleep 30
done
}

main

 运行结果如下:

 对于此篇博客存在的问题,欢迎各位大佬指正留言。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: