Skip to content

Perl 进程管理

进程创建

fork 函数

perl
use strict;
use warnings;

my $pid = fork;

if ($pid == 0) {
    # 子进程
    print "Child process (PID: $$)\n";
    sleep 2;
    print "Child exiting\n";
    exit(0);
} elsif ($pid > 0) {
    # 父进程
    print "Parent process (PID: $$)\n";
    print "Child PID: $pid\n";
    waitpid($pid, 0);  # 等待子进程结束
    print "Child exited\n";
} else {
    # fork 失败
    die "Cannot fork: $!";
}

exec 函数

perl
use strict;
use warnings;

my $pid = fork;

if ($pid == 0) {
    # 子进程执行其他程序
    exec("ls", "-l") or die "Cannot exec: $!";
    # exec 不会返回
} else {
    waitpid($pid, 0);
    print "Executed command\n";
}

system 函数

perl
use strict;
use warnings;

# 执行命令并返回退出状态
my $status = system("ls -l");

if ($status == 0) {
    print "Command succeeded\n";
} else {
    print "Command failed with status: $status\n";
}

# 使用列表形式(更安全)
my @command = ("ls", "-l", "/tmp");
$status = system(@command);

反引号(捕获输出)

perl
use strict;
use warnings;

# 捕获命令输出
my $output = `ls -l`;
print $output;

# 列表形式
my @output = `ls -l`;
print "Lines: " . scalar(@output) . "\n";

# 使用 qx// 操作符
my $output2 = qx/date/;
print $output2;

进程通信

管道(Pipe)

perl
use strict;
use warnings;
use IPC::Open2;

# 创建管道
my ($read_fh, $write_fh);
pipe($read_fh, $write_fh) or die "Cannot pipe: $!";

my $pid = fork;

if ($pid == 0) {
    # 子进程:写入数据
    close($read_fh);
    print $write_fh "Hello from child\n";
    print $write_fh "Second line\n";
    close($write_fh);
    exit(0);
} else {
    # 父进程:读取数据
    close($write_fh);
    
    while (my $line = <$read_fh>) {
        print "Parent received: $line";
    }
    
    close($read_fh);
    waitpid($pid, 0);
}

双向管道

perl
use strict;
use warnings;
use IPC::Open2;

my $pid = open2(my $read_fh, my $write_fh, "bc");

# 写入数据
print $write_fh "2 + 2\n";
print $write_fh "10 * 5\n";
print $write_fh "quit\n";

# 读取结果
while (my $line = <$read_fh>) {
    print "Result: $line";
}

close($read_fh);
close($write_fh);
waitpid($pid, 0);

共享内存

perl
use strict;
use warnings;
use IPC::SysV qw(IPC_PRIVATE IPC_RMID IPC_CREAT S_IRWXU);
use IPC::SharedMem;

# 创建共享内存
my $shm = IPC::SharedMem->new(IPC_PRIVATE, 1024, IPC_CREAT | S_IRWXU)
    or die "Cannot create shared memory: $!";

my $pid = fork;

if ($pid == 0) {
    # 子进程写入
    $shm->write("Hello from child", 0, 20);
    sleep 2;
    exit(0);
} else {
    # 父进程读取
    sleep 1;
    my $data = $shm->read(0, 20);
    print "Parent read: $data\n";
    
    waitpid($pid, 0);
    $shm->remove();
}

信号量

perl
use strict;
use warnings;
use IPC::Semaphore;

my $sem = IPC::Semaphore->new(1234, 1, IPC_CREAT | 0666)
    or die "Cannot create semaphore: $!";

my $pid = fork;

if ($pid == 0) {
    # 子进程:获取信号量
    $sem->op(0, -1, 0);  # 等待并获取
    print "Child acquired semaphore\n";
    sleep 2;
    print "Child releasing semaphore\n";
    $sem->op(0, 1, 0);  # 释放
    exit(0);
} else {
    # 父进程:等待子进程完成
    waitpid($pid, 0);
    print "Parent done\n";
}

$sem->remove();

进程控制

wait 和 waitpid

perl
use strict;
use warnings;

my @pids;

# 创建多个子进程
for (1..3) {
    my $pid = fork;
    
    if ($pid == 0) {
        print "Child $$ running\n";
        sleep int(rand(3));
        print "Child $$ exiting\n";
        exit(0);
    } else {
        push @pids, $pid;
    }
}

# 等待所有子进程
foreach my $pid (@pids) {
    my $done_pid = waitpid($pid, 0);
    print "Child $done_pid exited\n";
}

kill 函数

perl
use strict;
use warnings;

my $pid = fork;

if ($pid == 0) {
    # 子进程
    $SIG{INT} = sub {
        print "Child caught INT signal\n";
        exit(0);
    };
    
    print "Child running\n";
    sleep 10;
    print "Child done\n";
} else {
    # 父进程
    sleep 2;
    print "Sending INT to child\n";
    kill 'INT', $pid;
    
    waitpid($pid, 0);
    print "Child exited\n";
}

进程状态检查

perl
use strict;
use warnings;

my $pid = fork;

if ($pid == 0) {
    print "Child PID: $$\n";
    sleep 5;
    exit(0);
} else {
    for (1..3) {
        sleep 1;
        
        # 检查进程是否还在运行
        my $result = kill 0, $pid;
        
        if ($result) {
            print "Child $pid is still running\n";
        } else {
            print "Child $pid has exited\n";
            last;
        }
    }
    
    waitpid($pid, 0);
}

信号处理

基本信号处理

perl
use strict;
use warnings;

# 设置信号处理器
$SIG{INT} = sub {
    print "\nCaught SIGINT\n";
    exit(0);
};

$SIG{TERM} = sub {
    print "\nCaught SIGTERM\n";
    cleanup();
    exit(0);
};

sub cleanup {
    print "Cleaning up...\n";
    # 清理代码
}

print "Running (press Ctrl+C to stop)\n";
while (1) {
    sleep 1;
}

忽略信号

perl
use strict;
use warnings;

# 忽略 INT 信号
$SIG{INT} = 'IGNORE';

print "SIGINT ignored (press Ctrl+C won't work)\n";
sleep 10;
print "Done\n";

默认信号处理

perl
use strict;
use warnings;

# 使用默认处理
$SIG{INT} = 'DEFAULT';

print "SIGINT using default handler\n";
sleep 10;

实践示例

示例 1:进程池

perl
#!/usr/bin/perl
use strict;
use warnings;

sub create_process_pool {
    my ($num_workers, $worker_sub) = @_;
    my @workers;
    
    for (1..$num_workers) {
        my $pid = fork;
        
        if ($pid == 0) {
            # 工作进程
            while (1) {
                $worker_sub->();
            }
            exit(0);
        } else {
            push @workers, $pid;
        }
    }
    
    return \@workers;
}

# 使用
my $workers = create_process_pool(3, sub {
    print "Worker $$ processing\n";
    sleep 2;
});

# 等待 Ctrl+C
$SIG{INT} = sub {
    print "Stopping workers...\n";
    kill 'TERM', @$_ for $workers;
    waitpid($_, 0) for @$workers;
    exit(0);
};

while (1) {
    sleep 1;
}

示例 2:并行任务执行

perl
#!/usr/bin/perl
use strict;
use warnings;

my @tasks = (
    { name => "Task 1", duration => 3 },
    { name => "Task 2", duration => 2 },
    { name => "Task 3", duration => 4 },
    { name => "Task 4", duration => 1 }
);

sub run_task {
    my ($task) = @_;
    print "Starting $task->{name}\n";
    sleep $task->{duration};
    print "Completed $task->{name}\n";
}

# 并行执行所有任务
my @pids;

foreach my $task (@tasks) {
    my $pid = fork;
    
    if ($pid == 0) {
        run_task($task);
        exit(0);
    } else {
        push @pids, $pid;
    }
}

# 等待所有任务完成
waitpid($_, 0) for @pids;

print "All tasks completed\n";

示例 3:守护进程

perl
#!/usr/bin/perl
use strict;
use warnings;

sub daemonize {
    # Fork 第一次
    my $pid = fork;
    exit if $pid;
    die "Cannot fork: $!" unless defined $pid;
    
    # 创建新会话
    setsid() or die "Cannot setsid: $!";
    
    # Fork 第二次
    $pid = fork;
    exit if $pid;
    die "Cannot fork: $!" unless defined $pid;
    
    # 重定向标准输入输出
    open(STDIN, '<', '/dev/null') or die "Cannot redirect STDIN: $!";
    open(STDOUT, '>', '/dev/null') or die "Cannot redirect STDOUT: $!";
    open(STDERR, '>&STDOUT') or die "Cannot redirect STDERR: $!";
    
    # 信号处理
    $SIG{INT} = 'IGNORE';
    $SIG{TERM} = sub {
        print STDERR "Daemon stopping\n";
        exit(0);
    };
}

# 使用
daemonize();

my $count = 0;
while (1) {
    sleep 5;
    $count++;
    open(my $fh, '>>', 'daemon.log') or die "Cannot open log: $!";
    print $fh "Heartbeat $count at " . localtime() . "\n";
    close($fh);
}

小结

本章节学习了 Perl 的进程管理:

  1. ✅ 进程创建(fork、exec、system)
  2. ✅ 进程通信(管道、共享内存、信号量)
  3. ✅ 进程控制(wait、waitpid、kill)
  4. ✅ 信号处理
  5. ✅ 实践示例

接下来,我们将学习 Perl 参考资料