自己动手实现 Shell多进程套套符
$ cmd1 | cmd2 | cmd3 | cmd4 | cmd5
我们要使用 Python 语言,因为 Go 和 Java 语言都不支持 fork 函数。最终需要的是下面这张图,这张图很简单,但是为了构造出这张图,是需要费一番功夫的。
程序的代码文件名是 pipe.py,程序的运行形式如下
python pipe.py "cat pipe.py | grep def | wc -l"
统计 pipe.py 文件代码中包含 def 单词的个数,输出
3
指令执行
每一条指令的运行都需要至少携带一个管道,左边的管道或者右边的管道。第一个指令和最后一个指令只有一个管道,中间的指令有两个管道。管道的标识是它的一对读写描述符(r, w)。
图片
左边管道的读描述符 left_pipe[0] 对接进程的标准输入。右边管道的写描述符 right_pipe[1] 对接进程的标准输出。调整完描述符后,就可以使用 exec 函数来执行指令。
图片
def run_cmd(cmd, left_pipe, right_pipe):
if left_pipe:
os.dup2(left_pipe[0], sys.stdin.fileno())
os.close(left_pipe[0])
os.close(left_pipe[1])
if right_pipe:
os.dup2(right_pipe[1], sys.stdout.fileno())
os.close(right_pipe[0])
os.close(right_pipe[1])
# 分割指令参数
args = [arg.strip() for arg in cmd.split()]
args = [arg for arg in args if arg]
try:
# 传入指令名称、指令参数数组
# 指令参数数组的第一个参数就是指令名称
os.execvp(args[0], args)
except OSError as ex:
print "exec error:", ex
进程关系
shell 需要运行多个进程,就必须用到 fork 函数来创建子进程,然后使用子进程来执行指令。
子又生孙,孙又生子,子子孙孙无穷尽也。理论上使用管道可以串接非常多的进程输入输出流。
# 指令的列表以及下一条指令左边的管道作为参数
def run_cmds(cmds, left_pipe):
# 取出指令串的第一个指令,即将执行这第一个指令
cur_cmd = cmds[0]
other_cmds = cmds[1:]
# 创建管道
pipe_fds = ()
if other_cmds:
pipe_fds = os.pipe()
# 创建子进程
pid = os.fork()
if pid < 0:
print "fork process failed"
return
if pid > 0:
# 父进程来执行指令
# 同时传入左边和右边的管道(可能为空)
run_cmd(cur_cmd, left_pipe, pipe_fds)
elif other_cmds:
# 莫忘记关闭不再使用的描述符
if left_pipe:
os.close(left_pipe[0])
os.close(left_pipe[1])
# 子进程递归继续执行后续指令,携带新创建的管道
run_cmds(other_cmds, pipe_fds)
启动脚本
需要对命令行参数按竖线进行分割得出多条指令,开始进入递归执行
def main(cmdtext):
cmds = [cmd.strip() for cmd in cmdtext.split("|")]
# 第一条指令左边没有管道
run_cmds(cmds, ())
if __name__ == '__main__':
main(argv[1])
观察进程关系
因为例子中的几条指令执行时间太短,无法通过 ps 命令来观察进程关系。所以我们在代码里加了一句调试用的输出代码,输出当前进程执行的指令名称、进程号和父进程号。
def run_cmd(cmd, left_pipe, right_pipe):
print cmd, os.getpid(), os.getppid()
...
运行脚本时观察输出
$ python pipe.py "cat pipe.py | grep def | wc -l"
cat pipe.py 49782 4503
grep def 49783 49782
wc -l 49784 49783
3
从输出中可以明显看出父子进程的关系,第 N 条指令进程是第 N+1 条指令进程的父进程。在 run_cmds 函数中,fork 出子进程后由父进程来负责执行当前指令,剩余的指令交给子进程执行。所以才形成了上面的进程关系。读者可以尝试调整交互执行顺序,让子进程负责执行当前指令,然后再观察输出
$ python pipe.py "cat pipe.py | grep def | wc -l"
cat pipe.py 49949 49948
grep def 49950 49948
wc -l 49951 49948
3
图片
你会发现这三个指令进程都共享同一个父进程,这个父进程就是 Python 进程。如上图所示,我们平时使用的 shell 在执行指令的时候形成的进程关系都是这种形式的,这种形式在逻辑结构上看起来更加清晰。
原文发布时间为: 2018-10-21
本文作者: 码洞
本文来自云栖社区合作伙伴“ 码洞”,了解相关信息可以关注“码洞”。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
MSSQL · 最佳实践 · 使用混合密钥实现列加密
摘要 在SQL Server安全系列专题的上两期月报分享中,我们分别分享了:如何使用对称密钥实现SQL Server列加密技术和使用非对称密钥加密方式实现SQL Server列加密。本期月报我们分享使用混合密钥加密方式实现SQL Server列加密技术,最大限度减少性能损失,最大程度保护用户数据安全。 场景引入 对称加密是指加密和解密过程使用同一个密钥的加密算法,非对称加密是指加密和解密过程使用不同的密钥进行的加密算法。因此,通常来说对称加密安全性较弱,非对象加密安全性相对较高。凡事都具有两面性,非对称密钥加密的安全性较好,但通常算法相比对称密钥复杂许多,因此会带来性能上的损失也更大。有没有一种方法既可以最大限度保证数据安全性,又能够最大限度的减少性能损失呢?这便是本期月报分享的价值所在:SQL Server使用混合密钥实现列加密技术。
- 下一篇
c#开发移动APP-Xamarin入门扩展
原文: c#开发移动APP-Xamarin入门扩展 这节主要演示了如何通过添加第二个屏幕来跟踪应用程序的call历史来扩展Phoneword应用程序。最终如下: 按如下步骤扩展Phoneword 在Phoneword项目右键新建Content Page,命名为CallHistoryPage 修改后CallHistoryPage.xaml如下: <?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:local="clr-namespace:Phoneword;assembly=Phoneword" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Phoneword.CallHistoryPage" Title="Call History"> <ContentPage.Padding> <OnPlatform x:...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2整合Redis,开启缓存,提高访问速度
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)