Shell 实现多任务并发的示例代码

 更新时间:2023年06月20日 09:08:58   作者:普通网友  
本文主要介绍了Shell 实现多任务并发的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

实现思路

实现一个shell进程库,通过类似于initrunwait几个简单的命令,就可以迅速实现多进程并发,伪码如下:

process_init # 创建进程
for city in ${cities[*]}
do
    cmd="handler $city"
    process_run $cmd 
done
process_wait # 等待进程

原理解析

在实现C++线程库的时候,通常会有一个任务队列,线程从队列中取任务并运行。在实现shell进程库的时候,采用了类似原理,通过一个有名管道充当任务队列。严格来说,并不是一个任务队列,而是一个令牌桶。进程从桶中取得令牌后才可以运行,运行结束后将令牌放回桶中。没有取得令牌的进程不能运行。令牌的数目即允许并发的最大进程数。

管道

主要思路:通过mkfifo创建一个有名管道,将管道与一个文件描述符绑定,通过往管道中写数据的方式,控制进程数量。

function _create_pipe()
{
    _PROCESS_PIPE_NAME=$(_get_uid)
    mkfifo ${_PROCESS_PIPE_NAME}
    eval exec "${_PROCESS_PIPE_ID}""<>${_PROCESS_PIPE_NAME}"
    for ((i=0; i < $_PROCESS_NUM; i++))
    do
        echo -ne "\n" 1>&${_PROCESS_PIPE_ID}
    done
}

exec

exec fd <  file  #以读方式打开文件,并关联文件描述符fd
exec fd >  file  #以写方式打开文件,并关联文件描述符fd
exec fd <> file  #以读写方式打开文件,并关联文件描述符
# 测试
exec 8>file
echo "hello word!" 1>&8

eval

为了让程序有一定的扩展性,不想写死fd,因而引入了变量。

file_fd=8
exec ${file_fd}>file
# 测试
bash test.sh
test.sh: line 7: exec: 8: not found

原因:shell在重定向操作符(<、>)左边不进行变量展开。因而引入eval命令,强制shell进行变量展开。 
eval exec "${fd}>file"简单的说,eval将右边参数整体作为一个命令,进行变量的替换,然后将替换后的输出结果给shell去执行。

进程关系

命令执行

function process_run()
{
    cmd=$1
    if [ -z "$cmd" ]; then
        echo "please input command to run"
        _delete_pipe    
        exit 1
    fi
    _process_get
    {
        $cmd
        _process_post
    }&
}

_process_get从管道中取得一个令牌,创建一个进程执行任务,任务执行完毕后,通过_process_post将令牌放回管道。

进程创建

chengsun@153_92:~/test> bash process_util.sh
chengsun@153_92:~/test> pstree -a
|`-sshd
|    |-bash
|    |   `-bash process_util.sh         #爷爷
|    |       |-bash process_util.sh     #爸爸
|    |       |   `-a.out                #儿子
|    |       |-bash process_util.sh        
|    |       |   `-a.out                   
|    |       `-bash process_util.sh
|    |           `-a.out

脚本运行后,通过pstree命令,得到如上父子进程关系。稍微做一下解释:当运行脚本bash process_util.sh的时候,创建一个shell进程,相当于爷爷被创建起来,而遇到{ command1; command2 } &时,shell 又创建一个子shell进程(爸爸进程)处理命令序列;而对于每一个外部命令,shell都会创建一个子进程运行该命令,即儿子进程被创建。

困惑:为什么处理{ command1; command2; } &需要单独创建子进程? 
按照bash manual说法,{ list }并不会创建一个新的shell来运行命令序列。但由于加入&,代表将命令族放入后台执行,就必须新开subshell,否则shell会阻塞。

进程组

chengsun@153_92:~/test> ps -f -e -o pid,ppid,pgid,comm
 PID  PPID  PGID  COMMAND
24904 21976 24904 bash
19885 24904 19885  \_ bash            # 爷爷
19893 19885 19885      \_ bash        # 爸爸
19894 19893 19885      |   \_ a.out   # 儿子
19895 19885 19885      \_ bash
19896 19895 19885      |   \_ a.out
19897 19885 19885      \_ bash
19898 19897 19885          \_ a.out

Shell 将运行process_util的一堆进程置于一个进程组中。其中爷爷进程作为该进程组的组长,pid == pgid。

wait

wait pid:阻塞等待某个进程结束; 如果没有指定参数,wait会等待所有子进程结束。

清理函数

允许任务通过CTRL+C方式提前结束,因而需要清理函数

信号

trap 
类似C语言signal函数,为shell脚本注册信号处理函数。trap arg signals,其中signals为注册的信号列表,arg为收到信号后执行某个命令。

function Print
{
    echo "Hello World!"
}
trap Print SIGKILL

kill 
kill 命令给进程或进程组发送信号;kill pid 给进程发送默认信号SIGTERM, 通知程序终止执行。

void sig_handler(int signo)
{
    printf("sigterm signal\n");
}
int main()
{
    signal(SIGTERM, sig_handler);
    sleep(100);
    return 0;
}
chengsun@153_92:~/test> ./a.out &
[1] 19254
chengsun@153_92:~/test> kill 19254
sigterm signal

kill 0:给当前进程组发送默认信号SIGTERM

chengsun@153_92:~/test> man kill
0  All processes in the current process group are signaled.

清理

function _clean_up
{
    # 清理管道文件
    _delete_pipe
    kill 0
    kill -9 $$
}
trap _clean_up SIGINT SIGHUP SIGTERM SIGKILL

kill -9 $$ 非常重要

实际上,最上层是爷爷进程,当发送Ctrl + C命令的时候,爷爷进程捕获SIGINT信号,调用_clean_up函数。爷爷进程对整个进程组发送SIGTERM信号,并调用kill -9结束自己。爸爸进程接收SIGTERM信号,同时也发送SIGTERN给整个进程组,如果没有kill -9,爸爸进程始终无法结束,进入无限递归环节。儿子为CPP二进制程序,内部没有捕获SIGTERM,该信号默认动作是结束进程。

使用范例

# file: run.sh
#!/bin/sh
#load process library
source ./process_util.sh
function handler()
{
    city=$1
    ./main ${city}
}
process_init 23
for city in $cities
do
    cmd = "handler $city"
    process_run "$cmd"
done
process_wait

到此这篇关于Shell 实现多任务并发的示例代码的文章就介绍到这了,更多相关Shell 多任务并发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Shell中的函数、函数定义、作用域问题介绍

    Shell中的函数、函数定义、作用域问题介绍

    这篇文章主要介绍了Shell中的函数、函数定义、作用域问题介绍,本文讲解了Shell中函数的定义、自定义函数的例子、作用域问题等内容,需要的朋友可以参考下
    2014-11-11
  • Shell脚本统计当前目录下目录和文件的数量

    Shell脚本统计当前目录下目录和文件的数量

    这篇文章主要介绍了Shell脚本统计当前目录下目录和文件的数量,Linux下如何统计当前目录下文件有多少个,目录又有多少个呢,使用本文脚本即可实现,需要的朋友可以参考下
    2014-12-12
  • Linux查看端口占用的几种常用命令

    Linux查看端口占用的几种常用命令

    这篇文章主要介绍了Linux查端口占用的几个常用命令,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-09-09
  • Bash Shell中的select命令简单使用示例

    Bash Shell中的select命令简单使用示例

    这篇文章主要介绍了Bash Shell中的select命令简单使用示例,通常用于流程控制功能的实现,需要的朋友可以参考下
    2015-07-07
  • CentOS下对shell脚本加密的二种方法

    CentOS下对shell脚本加密的二种方法

    这篇文章主要介绍了CentOS下对shell脚本加密的二种方法,本文介绍了gzexe、shc两款加密工具,并分别讲解了加密方法,需要的朋友可以参考下
    2014-12-12
  • Shell实现读取ini格式配置文件方法

    Shell实现读取ini格式配置文件方法

    这篇文章主要介绍了Shell实现读取ini格式配置文件方法,本文直接给出实现代码,需要的朋友可以参考下
    2015-02-02
  • Linux中mysqldump命令实例详解

    Linux中mysqldump命令实例详解

    mysqldump是mysql数据库中备份工具,用于将MYSQL服务器中的数据库以标准的sql语言的方式导出,并保存到文件中。今天通过本文给大家介绍mysqldump命令实例详解,一起看看吧
    2016-12-12
  • awk实现Left、join查询、去除重复值以及局部变量讲解例子

    awk实现Left、join查询、去除重复值以及局部变量讲解例子

    这篇文章主要介绍了awk实现Left、join查询、去除重复值以及局部变量讲解例子,awk的高级使用技巧,需要的朋友可以参考下
    2014-07-07
  • shell脚本配合zabbix实现tomcat的故障自愈功能

    shell脚本配合zabbix实现tomcat的故障自愈功能

    这篇文章主要介绍了shell脚本配合zabbix实现tomcat的故障自愈,服务实现自愈的方式有通过shell脚本+定时任务的方式,蓝鲸Pass故障自愈平台,shell脚本+zabbix触发器动作,本文给大家详细介绍,需要的朋友可以参考下
    2022-03-03
  • linux自动清理日志脚本分享

    linux自动清理日志脚本分享

    这篇文章主要介绍了linux自动清理日志脚本,主要功能是清理所有目录的日志,根据给定日志目录,删除时间 结合crontab进行清理日志
    2014-01-01

最新评论