Perl 进程管理

Linux 系统中, 每执行一条命令或运行一个程序,都会产生一个主进程, 在主进程运行的时候,也有可能会产生多个子进程, 我们可以通过命令: ps -ef | grep xxx 来查看进程相关的信息.但是对于有些命令, 执行速度很快, 比如说ls /, 这样我们通过ps -ef 并不能看到与ls 相关的进程信息, 虽然看不到, 但是这不能表示ls 在执行的时候不会产生进程, 只是因为执行速度太快,难以捕获罢了.

perl 对进程的管理功能也是比较强大的, 但是笔者用perl 只是为了写一些脚本程序, 所以只需要掌握以下内容就行了:

  • perl 程序中调用系统命令或执行其它系统脚本
  • perl 向其它进程发送信号(关闭其它进程)
  • perl 接收其它进程发送的信号(被Ctrl+C 中断程序执行)

1. 调用系统命令或执行其它程序

perl 语言中调用执行系统命令或其它程序时会创建一个新的子进程, 执行命令有三种模式: system, qx, exec

  • 父进程: perl 脚本所占用的进程为父进程, 又称主进程
  • 子进程: perl 脚本调用系统命令或执行其它程序占用的进程
命令 格式 描述
system 主进程休眠,启动子进程执行其它程序,不捕获输出 返回状态码, 0 为成功, 其它为失败
qx / `` 主进程休眠, 启动子进程执行其它程序, 捕获输出 返回子进程标准输出的内容
exec 主进程结束, 启动子进程执行其它程序, 不捕获输出 无返回值

1.1 system

  • 命令格式1: system " xxx "; 双引号中的$ 引用的是perl标量, $ 引用的是shell 的系统环境变量
  • 命令格式2: system ' xxx '; 单引号中的$ 引用的是shell 中的环境变量, $ 无实用意义
#输出当前系统时间
system "echo date";
#输出perl标量
system "echo $JAVA_HOME";
#输出环境变量
system "echo \$JAVA_HOME";
#输出环境变量
system 'echo $JAVA_HOME';

1.2 qx

反引号`` 是qx 的简写形式, qx 可以用(), "", '' 等作为限定符, 返回值可由标量或数组进行捕获.在标量上下文中输出的内容为一个字符串, 在数组上下文中, 按换行符分隔为一个一个的元素.

  • 命令格式1: $line = xxx; 反引号中$ 引用的是perl 标量, $ 引用的是shell 系统环境变量
  • 命令格式2: $line = qx ' xxx '; qx 单引号中$引用的是shell 系统环境变量, $ 无实用意义
  • 命令格式3: @lines = qx " xxx "; qx 双引号中$引用的是perl 变量, $ 无实用意义
  • 命令格式4: @lines = qx ( xxx ); qx 双引号中$引用的是perl 变量, $ 无实用意义
$JAVA_HOME = qx' echo $JAVA_HOME ';
print "shell:JAVA_HOME=$JAVA_HOME ";

$JAVA_HOME = qx ( echo \$JAVA_HOME );
print "shell:JAVA_HOME=$JAVA_HOME ";

$JAVA_HOME = `echo \$JAVA_HOME`;
print "shell:JAVA_HOME:$JAVA_HOME ";

1.3 exec

  • exec 启动子进程后, 主进程会结束, 所以exec 通常是脚本中的最后一行代码
  • exec 常用于为子程序设置运行环境, 可通过直接修改%ENV 哈希中的环境变量直接修改运行环境, 修改的系统环境变量会影响其启动的子进程,修改的环境变量只在当前脚本中有效, 不会对真正的系统环境变量有所影响.
# 为子程序设置环境变量, 指定jdk 环境
$ENV{JAVA_HOME} = '/opt/app/tomcat/jdk/jdk1.8_0131';

# 启动tomcat
exec '/opt/app/tomcat/tomcat-8-8080/bin/startup.sh &';

2. 向其它进程发送信号

  • 命令格式: kill sigInt, pid1 pid2 ...;
  • sigInt: 信号数字, 常用的是9, 强制杀死进程
  • pid: 进程id , 可同时杀死多个进程
  • 返回成功杀死进程的数量
kill 9, 5161 6161;

3. 接收其它进程发送的信号

  • perl 程序运行时, 也会启动一个进程, 所以perl 进程也可以接收其它进程发送的信号
  • perl 内置了%SIG 存储了perl 可接收的所有信号参数, 存储格式为: key=method
  • 我们只需要给对应的key对应的value设置相应的方法即可,这样当程序接收到信号,就会执行对应的方法.
  • 注意: 当perl 主程序接收到相应的信号之后,会暂时中断目前正在执行的流程, 而去执行信号对应的方法, 当方法完成之后,再回到原来执行的位置,继续执行.
  • 信号方法的注册必须在脚本程序最前面,因为随时都可能执行.
#必须在程序最开始设置, 设置接收到ctrl + C 信号时执行的方法
$SIG{'INT'} = 'clean';

#接收到ctrl+C 信号时处理方法
sub clean{
     print 'excute clean operation...';
     exit;  #接收到信号时,需要手工调用exit 函数,否则程序不会退出
}

4. 测试用例

  • 测试脚本运行的时候, 需要启动两个shell 窗口, 一个窗口执行脚本, 另一个窗口监控进程
  • 脚本名称为proce.pl, 如果修改脚本名称的话, 查看进程的命令需要修改 = 脚本运行中, 需要配合使用Ctrl+C 来向进程中发送终止信号.

4.1 测试脚本

#!/usr/bin/perl

#指定程序接收到Ctrl+C 信号时最后执行的方法clean,必须放在程序最前面
#INT 信号代表从键盘输入Ctrl+C结束程序
$SIG{'INT'} = 'clean';

#定义clean方法
sub clean {
   print "程序异常结束, 执行清理操作...\n";

   #显示调用退出函数, 否则会从接收到Ctrl+C信号的地方继续执行行
   #exit;
}


print "\n#################### 1.1 system 调用其它程序  ####################\n";
$JAVA_HOME = "java home";

print "perl  JAVA_HOME:"; system "echo $JAVA_HOME";
print "shell JAVA_HOME:"; system "echo \$JAVA_HOME";
print "shell JAVA_HOME:"; system 'echo $JAVA_HOME';

print "\n#################### system 返回结果  ####################\n";
$code = system "date";
print "正确执行返回值: $code \n";

$code = system "hello";
print "错误执行返回值: $code\n";

print "\n############# 1.2 qx()/`` 捕获系统输出 ##################\n";

$JAVA_HOME = qx' echo $JAVA_HOME ';
print "shell:JAVA_HOME=$JAVA_HOME ";

$JAVA_HOME = qx ( echo \$JAVA_HOME );
print "shell:JAVA_HOME=$JAVA_HOME ";

$JAVA_HOME = `echo \$JAVA_HOME`;
print "shell:JAVA_HOME:$JAVA_HOME ";

print "\n####################  1.3 修改子程序的环境变量 ################\n";
#通过修改ENV哈希中的值, 直接影响子程序的系统环境变量
$ENV{JAVA_HOME}="JAVA_HOME";

print "子程序:JAVA_HOME:"; system 'echo $JAVA_HOME';

print "程序即将休眠30秒,请在另外窗口中执行命令查看进程, 查看后使用Ctrl+C 发送中断信号\n";
print "查看命令:ps -ef | grep -v grep | grep -E \"process.pl|sleep\"\n";
system "sleep 30000";
print "\n####################  接收Ctrl + C 信号 ################\n";

print "\n####################  exec  ################\n";
print "程序即将终止主进程, 并进入exec 进程,请在另外窗口中执行命令查看剩余进程, 查看后使用Ctrl+C 发送中断信号\n";
print "查看命令:ps -ef | grep -v grep | grep -E \"process.pl|sleep\"\n";
exec "sleep 100000 \n";

4.2 脚本输出

[admin@localhost perl]$ ./process.pl 

#################### 1.1 system 调用其它程序  ####################
perl  JAVA_HOME:java home
shell JAVA_HOME:/opt/app/jdk/jdk1.6.0_31
shell JAVA_HOME:/opt/app/jdk/jdk1.6.0_31

#################### system 返回结果  ####################
Thu Jul  6 13:21:54 CST 2017
正确执行返回值: 0 
错误执行返回值: -1

############# 1.2 qx()/`` 捕获系统输出 ##################
shell:JAVA_HOME=/opt/app/jdk/jdk1.6.0_31
 shell:JAVA_HOME=/opt/app/jdk/jdk1.6.0_31
 shell:JAVA_HOME:/opt/app/jdk/jdk1.6.0_31

####################  1.3 修改子程序的环境变量 ################
子程序:JAVA_HOME:JAVA_HOME
程序即将休眠30秒,请在另外窗口中执行命令查看进程, 查看后使用Ctrl+C 发送中断信号
查看命令:ps -ef | grep -v grep | grep -E "process.pl|sleep"
^C
####################  接收Ctrl + C 信号 ################

####################  exec  ################
程序即将终止主进程, 并进入exec 进程,请在另外窗口中执行命令查看剩余进程, 查看后使用Ctrl+C 发送中断信号
查看命令:ps -ef | grep -v grep | grep -E "process.pl|sleep"
^C

4.3 第一次暂停时查看系统进程

  • 有两个进程, 一个是主进程, 一个是system 启动的休眠子进程
[admin@localhost ~]$ ps -ef | grep -v grep | grep -E "process.pl|sleep"
admin     4201 32469  0 13:20 pts/1    00:00:00 /usr/bin/perl ./process.pl
admin     4218  4201  0 13:20 pts/1    00:00:00 sleep 30000

4.2 第二次暂停时, 查看系统进程

  • 只有一个进程, 主进程结束, 只剩下由exec 启动的休眠子进程.
[admin@localhost ~]$ ps -ef | grep -v grep | grep -E "process.pl|sleep"
admin     4201 32469  0 13:20 pts/1    00:00:00 sleep 100000

results matching ""

    No results matching ""