Perl多进程与信号量

简介: 通过简单实例展示Perl多进程与信号量。

一、Perl多进程

今天这里就不多说什么是多进程、什么是多线程了,简单来说目的都是充分利用硬件资源,最大化机器性能。
任何语言的新手在遇到多进程编程时,心里往往会发怵,多进程好像很高大上、好复杂的样子,其实不然。我们要区分多进程实现本身和多进程应用编程,多进程本身的实现确实是很复杂的,但是我们更多的是集中在多进程应用编程,这部分就比较简单了。
话不多说,下面先看用perl实现的一个多进程编程例子。

#!/bin/perl
use strict;

# fork returns the child pid to parent, 0 to child or undef if failed to fork.
my $pid = fork();
die "fork failed" unless defined $pid;
if($pid > 0){
  my $i = 1000;
  while($i > 0){
    print "parent process i = $i\n";
    $i--;
  }
}elsif($pid == 0){
  my $i = 1000;
  while($i > 0){
    print "child process i = $i\n";
    $i--;
  }
}

上述例子中,实现多进程最关键的方法就是fork,其功能是将当前代码完全拷贝一份,也就是形成两份相同的程序:父进程和子进程。其中,在父进程中,fork的返回值是子进程的进程号(大于0的值);而在子进程中,fork返回值是0;fork失败时返回的值为负数。fork失败的情况一般是资源耗尽,我自己在编程时遇到过几次,都是fork太多的进程耗尽了系统资源。
通过以上三个返回值,我们就能确定fork有没有成功,以及成功时谁是父进程、谁是子进程,这样就可以决定在父进程或者子进程中做什么样的操作。
例子中父、子进程都是循环输出1000至1,同时给出父子进程提示信息。
输出结果如下:

parent process i = 36
child process i = 254
parent process i = 35
child process i = 253
parent process i = 34
child process i = 252
parent process i = 33
child process i = 251
parent process i = 32
child process i = 250
parent process i = 31
child process i = 249
parent process i = 30
child process i = 248
parent process i = 29
child process i = 247
parent process i = 28
child process i = 246
parent process i = 27
child process i = 245
parent process i = 26
child process i = 244
parent process i = 25
child process i = 243
parent process i = 24
child process i = 242
child process i = 241
child process i = 240
parent process i = 23
child process i = 239
parent process i = 22
child process i = 238
parent process i = 21
child process i = 237
parent process i = 20
child process i = 236
parent process i = 19
child process i = 235
parent process i = 18
child process i = 234
parent process i = 17
child process i = 233
parent process i = 16
child process i = 232

二、僵尸进程

1、什么是僵尸进程?

简单来讲(我最喜欢简单来讲),子进程完成使命(死了),却没有被父进程回收(收尸),什么作用都没有了,却还暂留在系统中,就像僵尸。进程变为僵尸进程时其所占用的资源都会被回收掉,因此不会造成太大的资源泄露,当然,进程本身的信息(进程号,创建时间等)还是存在的。

2、为什么会产生僵尸进程?如何回收?

一般情况是父进程比子进程提前结束并且没有任何其它进程来回收子进程时就会产生僵尸进程,例如我们在程序中没有指明父进程需要等待子进程全部结束并回收子进程时,子进程就会成为僵尸进程。
避免产生僵尸进程需要用到wait或者waitpid函数,这里我使用的是waitpid($pid,0)函数。该函数的功能是等待$pid进程结束并回收它。
代码如下:

#!/bin/perl
use strict;

# fork returns the child pid to parent, 0 to child or undef if failed to fork.
my $pid = fork();
die "fork failed" unless defined $pid;
if($pid > 0){
  my $i = 1000;
  while($i > 0){
    print "parent process i = $i\n";
    $i--;
  }
  # 等待子进程结束并回收它
  waitpid($pid,0);
}elsif($pid == 0){
  my $i = 1000;
  while($i > 0){
    print "child process i = $i\n";
    $i--;
  }
}

三、信号量(进程同步)

不需要多么仔细的观察就能发现第一个样例程序中,两个进程是并行执行的,这或许正是我们使用进程的目的。但是,有些时候,比如说两个进程都需要使用某个资源,而这个资源不能被两个进程同时使用,因此我们希望这两个进程串行执行。这个时候我们就需要使用锁这种东西来确保进程同步。
在这里,我使用了IPC的信号量,并让信号量资源值为1,当资源值为1时,其作用相当于锁。

# new一个信号量集(信号量数组,下标从0开始),第二个参数1表示该信号量集中只有一个信号量(下标为0),其它值为系统定义参数
our $sem = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT);
# 设定信号量集中下标为0的信号量资源值为1
$sem->setval(0,1);
# 该操作表示P操作,请求一个资源,第一个参数表示信号量下标,中间参数表示请求一个资源,最后一个参数表示如果进程结束则直接释放资源
$sem->op(0,-1,SEM_UNDO);
# 该操作表示V操作,释放一个资源
$sem->op(0,1,SEM_UNDO);
# 移除信号量集,如果不移除,则信号量会一直留在系统中
$sem->remove() if defined $sem;

Linux系统中使用ipcs -a命令可以查看当前系统中所有的信号量及共享内存段等使用。有些时候程序意外终止,信号量未释放,那么就可以使用该命令查看信号量,并使用ipcrm -s semid移除用semid标识的信号。

完整代码如下:

#!/bin/perl
use strict;
use IPC::Semaphore;
use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT IPC_NOWAIT SEM_UNDO);

our $sem = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT);
$sem->setval(0,1);
# fork returns the child pid to parent, 0 to child or undef if failed to fork.
my $pid = fork();
die "fork failed" unless defined $pid;
if($pid > 0){
  $sem->op(0,-1,SEM_UNDO);
  my $i = 1000;
  while($i > 0){
    print "parent process i = $i\n";
    $i--;
  }
  $sem->op(0,1,SEM_UNDO);
}elsif($pid == 0){
  $sem->op(0,-1,SEM_UNDO);
  my $i = 1000;
  while($i > 0){
    print "child process i = $i\n";
    $i--;
  }
}
$sem->remove() if defined $sem;

输出结果如下,可以看到,两个进程时串行执行的,达到了进程同步的效果。

parent process i = 16
parent process i = 15
parent process i = 14
parent process i = 13
parent process i = 12
parent process i = 11
parent process i = 10
parent process i = 9
parent process i = 8
parent process i = 7
parent process i = 6
parent process i = 5
parent process i = 4
parent process i = 3
parent process i = 2
parent process i = 1
child process i = 1000
child process i = 999
child process i = 998
child process i = 997
child process i = 996
child process i = 995
child process i = 994
child process i = 993
child process i = 992
child process i = 991
child process i = 990
child process i = 989
child process i = 988
child process i = 987
child process i = 986
child process i = 985
child process i = 984
child process i = 983
child process i = 982
child process i = 981
child process i = 980
child process i = 979
目录
相关文章
|
3月前
|
监控 开发者 Perl
探索研究Perl 进程管理
【9月更文挑战第21天】
30 6
|
3月前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。
|
4月前
|
消息中间件 Linux 开发者
Linux进程间通信秘籍:管道、消息队列、信号量,一文让你彻底解锁!
【8月更文挑战第25天】本文概述了Linux系统中常用的五种进程间通信(IPC)模式:管道、消息队列、信号量、共享内存与套接字。通过示例代码展示了每种模式的应用场景。了解这些IPC机制及其特点有助于开发者根据具体需求选择合适的通信方式,促进多进程间的高效协作。
188 3
|
4月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
78 0
|
4月前
|
数据采集 Java Python
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
python 递归锁、信号量、事件、线程队列、进程池和线程池、回调函数、定时器
|
5月前
|
安全 API Python
`multiprocessing`是Python的一个标准库,用于支持生成进程,并通过管道和队列、信号量、锁和条件变量等同步原语进行进程间通信(IPC)。
`multiprocessing`是Python的一个标准库,用于支持生成进程,并通过管道和队列、信号量、锁和条件变量等同步原语进行进程间通信(IPC)。
|
5月前
|
消息中间件 Linux
【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(下)
【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(下)
78 0
|
5月前
|
消息中间件 存储 Linux
【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(上)
【Linux】进程间通信——system V(共享内存 | 消息队列 | 信号量)(上)
86 0
|
6月前
|
存储 安全 Python
进程通信 , 信号量 , 队列 , 管道 , 共享内存
进程通信 , 信号量 , 队列 , 管道 , 共享内存
|
7月前
|
C++
【操作系统】信号量机制(整型信号量、记录型信号量),用信号量实现进程互斥、同步、前驱关系
【操作系统】信号量机制(整型信号量、记录型信号量),用信号量实现进程互斥、同步、前驱关系
302 6