shell中实现并行运算

linux系统中一切皆文件,都可以以文件的方式进行管理和访问

 

  • 计算机的基本知识

先讲下冯诺依曼体系结构。这个是一个计算机入门第一节课必然会提到的知识,冯诺依曼体系结构提出了计算机采用二进制,计算机应该按照程序顺序执行,它由输入设备、输出设备、存储器(只考虑内存)、控制器+运算器(CPU)组成,注意在不考虑缓存情况下,这里的CPU只能对内存进行读写,不能访问外设(输入或输出设备),外设要输入或输出数据,也只能写入内存或者从内存中读取。

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS),操作系统的存在就是让计算机更方便的统筹和管理计算机的软硬件资源。

那么是怎么管理的?

比如学校的管理结构为学生->辅导员->院内领导->校长。但在这之前需要制定一个制度来管理,每一个层次的人都遵守这个制度,这样才能按部就班的进行工作的分配。

那么操作系统的管理方式就是:先描述,再组织

描述用struct结构体,比如进程用task_struct结构体来描述,组织可以用链表或者其他高效的数据结构。

  • 进程和程序概念

程序是人使用计算机语言编写的可以实现特定目标或解决特定的代码集合,存储在硬盘/软盘/光盘等中。

从用户角度说,进程就是一个正在执行的程序或命令;从操作系统角度说,操作系统运行一个程序,需要描述这个程序的运行过程,这个描述通过一个结构体task_struct{}来描述,统称PCB,因此对操作系统来说进程就是PCB(process control block)程序控制块。

进程的描述(task_struct)信息有:标识符(process ID,PID),进程状态,优先级,程序计数器,上下文数据,内存指针,IO状态信息,记账信息。标识符被用来区分不同的进程。

  • 文件描述符概念

文件描述符(file descriptor,fd)是打开文件时产生的一个非负整数,是一个索引值,用于标识文件。内核会为每一个运行中的进程在进程控制块PCB中维护一个打开文件的记录表,也就是文件对象表,每一个表项都有一个指针指向打开的文件。打开一个进程时,会默认打开标准输入/输出/错误流,分别对应0/1/2文件描述符。同一个文件描述符可以被不同的进程打开,也可以被同一个进程多次打开。文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统,linux系统使用C语言写的。

  • 文件描述符与文件指针FILE的区别

文件指针指向进程中一个被称为FILE的结构,它包含一个缓存区和一个文件描述符,通过文件指针访问文件的时候,还是需要通过文件描述符来找到所指向的FILE结构体。使用fopen/fclose/fread/fwrite对文件进行操作,它们属于C语言库函数,在lib层中,返回值为FILE*,FILE*为文件指针。

系统调用文件的接口为open/close/read/write,它们的返回值为文件描述符(fd)。

  • 查看当前进程和进程下的文件描述符fd

ms@ms-LN:/mnt/c/Users/msl$ ps axj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     1     0     0 ?           -1 Sl       0   0:00 /init
    1    51    51    51 ?           -1 Ss       0   0:00 /init
   51    52    51    51 ?           -1 S        0   0:00 /init
   52    53    53    53 pts/0      147 Ss    1000   0:00 -bash
   53   147   147    53 pts/0      147 S+    1000   0:00 xclock
    1   148   148   148 ?           -1 Ss       0   0:00 /init
  148   149   148   148 ?           -1 R        0   0:00 /init
  149   150   150   150 pts/1      335 Ss    1000   0:00 -bash
    1   183   183   183 ?           -1 Ss       0   0:00 /init
  183   184   183   183 ?           -1 S        0   0:00 /init
  184   185   185   185 pts/2      218 Ss    1000   0:00 -bash
  185   218   218   185 pts/2      218 S+    1000   0:00 /bin/bash -x ./synch_cor.sh
  218   334   218   185 pts/2      218 D+    1000   0:00 gmt psconvert -Tj LVZ_vel1.5.cut70.delta0.5.bp0.03-0.15.ps
  150   335   335   150 pts/1      335 R+    1000   0:00 ps axj
ms@ms-LN:/mnt/c/Users/msl$ ls -l /proc/218/fd
total 0
lrwx------ 1 msl msl 64 Mar  6 01:53 0 -> /dev/pts/2
lrwx------ 1 msl msl 64 Mar  6 01:53 1 -> /dev/pts/2
lrwx------ 1 msl msl 64 Mar  6 01:53 2 -> /dev/pts/2
lr-x------ 1 msl msl 64 Mar  6 01:53 255 -> /mnt/e/maduo_earthquake/qseis_example/synch_cor.sh

######
上述./synch_cor.sh进程的标识符PID为218,进程218的fd有0/1/2/255,fd255指向文件/mnt/e/maduo_earthquake/qseis_example/synch_cor.sh
#####
ps命令和top命令用来查看进程,可以终端help查看用法
#####
linux系统下的进程都位于根目录/proc下,可用ls /proc查看

有关进程的详细管理可参考:百度安全验证https://baijiahao.baidu.com/s?id=1713604773141306667&wfr=spider&for=pc

  • 命名管道(first in first out,fifo)概念

    • 匿名管道为“|”,仅能处理前一个命令传来的正确信息,无法让其它进程连接,而命名管道FIFO可以;
    • 任何进程都可以通过FIFO共享数据;
    • 除非FIFO两端同时有读与写的进程,否则FIFO的数据流通将会阻塞;
    • 匿名管道是由shell自动创建的,存在于内核中;而FIFO则是由程序创建的(比如mkfifo命令),存在于文件系统中;
    • 匿名管道是单向的字节流,而FIFO则是双向的字节流;
  • 实现方式

shell中实现多进程高并发的简单形式是把命令丢到后台去运行,但当有上百个进程时,计算机的资源会被严重消耗。比较好点的方法是多个进程同时运行。利用FIFO可以做进程间通信的桥梁,从而实现多进程并发运算。包括:

1、命令结尾添加:&

2、解决主线程提前退出问题,添加 wait

3、控制后台执行数(线程数),mkfifo

  • 多进程并发实现代码

#!/bin/bash -x


thread_num=10
TMPFIFO=/tmp/$$.fifo			#声明命名管道,'$$'表示脚本当前运行的进程PID(process ID)
mkfifo $TMPFIFO                #创建命名管道
# 使文件描述符为非阻塞式
exec 6<>${TMPFIFO}             #把文件描述符6和FIFO进行绑定,以读写模式操作fifo文件。以读写模式操作管道文件;系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变,换句话说就是在调用进程内部执行一个可执行文件
rm -rf  ${TMPFIFO}   #绑定后,该文件就可以删除了
# 为文件描述符创建占位信息
for ((i=1;i<=${thread_num};i++))
do
{
##写一个空行到命名管道里,因为命名管道文件的读取以行为单位,下面三行等价于echo >&6 } done。该步骤是分配10个进程到文件描述符6中?
    echo 
}
done >&6   #  >&表示把输出(输出空行)送到文件描述符6,&用来强调6不是普通文件,而是文件描述符
#
a=$(date +%H%M%S)
for  (( i=1; i<=60; i++ ))
do
{
read -u 6  #由于文件描述符6已经和命名管道绑定,读取管道中的一行,每次读取后,管道都会少一行
{
depth_[$i]=`awk '{if(NR=='$i') print $3}' slip.dat`
strike_[$i]=`awk '{if(NR=='$i') print $11}' slip.dat`
dip_[$i]=`awk '{if(NR=='$i') print $12}' slip.dat`
rake_[$i]=`awk '{if(NR=='$i') print $13}' slip.dat`
slip_[$i]=`awk '{if(NR=='$i') print $10}' slip.dat`
cp qs_example.inp qs_example_"$i".inp
Mdc_[$i]=`echo '752719001100000000*'${slip_[$i]}'' | bc`
sed -i '27c '${depth_[$i]}'' qs_example_"$i".inp
sed -i '166c '\''ex-'$i''\''      '\''ss-'$i''\''     '\''ds-'$i''\''     '\''cl-'$i''\''     '\''fz-'$i''\''      '\''fh-'$i''\''    |char' qs_example_"$i".inp
sed -i '202c 2 0.00 0.00 '${Mdc_[$i]}' '${strike_[$i]}' '${dip_[$i]}' '${rake_[$i]}' '\''seis-'$i''\''' qs_example_"$i".inp
dist_line_[$i]=`echo '2*'$i'-1' | bc`
az_line_[$i]=`echo '2*'$i'' | bc`
mydist_[$i]=`sed -n ''${dist_line_[$i]}'p' station2epicent_dist_az.txt`
sed -i "46c ${mydist_[$i]}" qs_example_"$i".inp
myaz_[$i]=`sed -n ''${az_line_[$i]}'p' station2epicent_dist_az.txt`
sed -i "208c ${myaz_[$i]}" qs_example_"$i".inp
./qseis_fxbl.exe qs_example_"$i".inp
echo >&6 #每次执行完上面的命令后,再增加一个空行,这样下面的进程才可以继续执行。
}& #&表示在后台执行,否则并发实现不了
}
done
wait #等待以上所有操作(包括后台的进程)都结束后,再往下执行
# 关闭fd6管道,>&-表示关闭标准输入
exec 6>&-
b=$(date +%H%M%S)
echo -e "startTime:\t$a"
echo -e "endTime:\t$b"
  •  shell中exec的两种用法

    1、exec 命令:代替shell程序,exec命令在执行时会把当前的shell process关闭,然后换到后面的命令继续执行。比如 exec ls,终端执行完ls命令后退出。
    2、exec 文件重定向:可以使用命令 exec ls >> ls.txt 后,可以在当前路径下找到 ls.txt,并且ls.txt文件中的内容即为当前目录下的文件的名称。

  • exec与.(source)和system的异同

        1、exec和.(source)都可以在终端来执行程序或者脚本。
        2、exec执行时候会关闭当前shell进程,并且fork一个相同pid的shell进程来执行,系统调用新的exec的process来替代原来的进程执行。没有新的进程创建,原来进程的代码段、数据段、堆栈都被新的process所代替。
        3、.(source)执行的时候是当前shell环境下执行,执行完成后把状态返回给当前的shell。
        4、system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程。
        5、system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程。

  • 参考:

Linux -- system、.(source)、exec的区别_青椒*^_^*凤爪爪的博客-CSDN博客这两天在项目中需要一个功能:一个程序启动另外一个程序并且当前的程序退出,所以第一时间使用到了system函数,但是没有起到想要的效果,后来查阅资料,换成了exec函数实现了功能,在这里记录一下相关的知识点,权当做笔记使用。比如当前执行的程序名称为main,在条件满足的时候需要启动a.out程序,并且退出main程序。1、main程序由main.c编译而来,且main.c为:#incl...https://blog.csdn.net/songshuai0223/article/details/88032769

Linux进程概念_skrskr66的博客-CSDN博客_linux进程进程是什么?在操作系统中,我们经常能听到这样的话。我们要终止一个进程或者杀死一个进程,父进程创建了子进程这一类的话。往往我们听到都会觉得很高大上,这跟编程语言完全不同的感觉,操作了整个计算机。进程的概念冯诺依曼体系结构在进程之前首先要提一下我们的“祖师爷”——冯诺依曼体系结构。这个是一个计算机入门第一节课必然会提到的知识。冯诺依曼体系结构提出了计算机采用二进制;计算机应该按照程序顺序执行...https://blog.csdn.net/skrskr66/article/details/89147940 linux文件描述符与标识符,文件描述符fd和文件指针FILE的区别_策策的荣耀百科的博客-CSDN博客文件描述符fd和文件指针FILE的区别文件描述符fd和文件指针FILE的区别1.文件描述符fd的定义:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Lin...https://blog.csdn.net/weixin_31001855/article/details/117596315

初识 fifo文件和exec_醉世老翁的博客-CSDN博客_fifo文件io的重定向通常与fd有关,shell的fd通常为10个,也就是0-9,常用的fd有三个,0(stdin标准输入),1(stdout标准输出),2(stderr,标准错误输出)可以查看ls /dev/fd mkfifo创建FIFO特殊文件,是一个命名管道(可以用来做进程之间通信的桥梁)mkfifo tmp.fifo#创建一个fifo文件然后在打开一个窗口,来测试这个文件...https://blog.csdn.net/wojiuwangla/article/details/84654211 shell命名管道FIFO_weixin_33941350的博客-CSDN博客在shell脚本中,我们想要实现多进程高并发,最简单的方法是把命令丢到后台去,如果量不大的话,没问题。 但是如果有几百个进程同一时间丢到后台去就很恐怖了,对于服务器资源的消耗非常大,甚至导致宕机。那有没有好的解决方案呢? 当然有!我们先来学习下面的常识。1 文件描述符文件描述符(缩写fd)在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文...https://blog.csdn.net/weixin_33941350/article/details/92763650

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值