@zhongdao
2018-12-21T13:16:06.000000Z
字数 13259
阅读 1452
未分类
16.0. Introduction
Perl may be many things to many people, but to most of us it is the glue that connects diverse components. This chapter is about launching commands and connecting separate processes together. It's about managing their creation, communication, and ultimate demise. It's about systems programming.
When it comes to systems programming, Perl, as usual, makes easy things easy and hard things possible. If you want to use it as you would the shell, Perl is happy to assist you. If you want to roll up your sleeves for low-level hacking like a hardcore C programmer, you can do that, too.
Because Perl lets you get so close to the system, portability issues can sneak in. This chapter is the most Unix-centric chapter of the book. It will be tremendously useful to those on Unix systems, but of limited use to others. (If you're not on Unix, consult the perlport(3) manpage that came with Perl to see which of the techniques we describe are available on other operating systems or emulated by Perl.) We deal with features that aren't as universal as strings and numbers and basic arithmetic. Most basic operations work more or less the same everywhere. But if you're not using some kind of Unix or other POSIX conformant system, most of the interesting features in this chapter may work differently for you—or not at all. Check the documentation that came with your Perl port if you aren't sure.
You might even be pleasantly surprised. Windows users, for example, are often astonished to learn that Perl's fork function, long unique to Unix, is supported on their platform. See perlfork(1).
perl对很多人来说可能是很多东西,但对于我们大多数人来说,它是连接各种组件的胶水。本章介绍如何启动命令并将各个进程连接在一起。它是关于管理他们的创造,沟通和最终消亡。这是关于系统编程。
在系统编程方面,Perl像往常一样,简单易用,简单易事。如果您想像使用它一样使用它,Perl很乐意为您提供帮助。如果你想像硬核C程序员一样卷起低级别黑客的袖子,你也可以这样做。
因为Perl让你如此接近系统,所以可移植性问题可以潜入。本章是本书中以Unix为中心的最重要章节。它对Unix系统上的用户非常有用,但对其他系统的用途有限。(如果您不在Unix上,请参阅Perl附带的 perlport(3)联机帮助页,以了解我们描述的哪些技术在其他操作系统上可用或由Perl模拟。)我们处理的功能并不普遍作为字符串和数字以及基本算术。大多数基本操作在各地或多或少都相同。但是,如果您没有使用某种类型的Unix或其他符合POSIX标准的系统,本章中的大多数有趣功能可能对您有所不同 - 或者根本不同。如果您不确定,请查看Perl端口随附的文档。
你甚至可能会惊喜。例如,Windows用户通常会惊讶地发现Perl的fork 功能在Unix平台上是独一无二的。见 perlfork(1)。
In this chapter, we cover the proper care and feeding of your own child processes. Sometimes this means launching a standalone command and letting it have its own way with the world (using system). Other times it means keeping a tight rein on your child, feeding it carefully filtered input or taking hold of its output stream (backticks and piped open s). Without even starting a new process, you can use exec to replace your current program with a completely different program.
16.0.1进程创建
在本章中,我们将介绍您自己的子进程的正确护理和喂养。有时这意味着启动独立命令并让它与世界(使用系统)有自己的方式 。其他时候,这意味着要严格控制您的孩子进程,仔细过滤输入或保持其输出流(反引号和管道打开 s)。如果没有启动新进程,您可以使用 exec用完全不同的程序替换当前程序。
We first show how to use the most portable and commonly used operations for managing processes: backticks, system, open, and the manipulation of the %SIG hash. Those are the easy things, but we don't stop there. We also show what to do when the simple approach isn't good enough.
我们首先展示如何使用最便携和常用的操作来管理进程:反引号, 系统,打开以及%SIG哈希的操作。这些都很简单,但我们并不止于此。我们还展示了当简单方法不够好时该怎么做。
For example, you might want to interrupt your program while it's running a different program. Maybe you need to process a child program's standard error separately from its standard output. Perhaps you need to control both the input and output of a program simultaneously. When you tire of having just one thread of control and begin to take advantage of multitasking, you'll want to learn how to split your current program into several, simultaneously running processes that all talk to each other.
例如,您可能希望在运行其他程序时中断程序。也许您需要与其标准输出分开处理子程序的标准错误。也许您需要同时控制程序的输入和输出。如果您厌倦了只有一个控制线程并开始利用多任务处理,那么您将需要学习如何将当前程序拆分为几个同时运行的进程,这些进程都是相互通信的。
For tasks like these, you have to drop back to the underlying syscalls: pipe, fork, and exec. The pipe function creates two connected filehandles, a reader and writer, whereby anything written to the writer can be read from the reader. The fork function is the basis of multitasking, but unfortunately it has not been supported on all non-Unix systems. It clones off a duplicate process identical in virtually every aspect to its parent, including variable settings and open files. The most noticeable changes are the process ID and parent process ID. New programs are started by forking, then using exec to replace the program in the child process with a new one. You don't always both fork and exec together, so having them as separate primitives is more expressive and powerful than if all you could do is run system. In practice, you're more apt to use fork by itself than exec by itself.
对于像这样的任务,您必须回退到底层的系统调用: pipe,fork和 exec。该管函数创建两个连接的文件句柄,一个读取者reader和写入者writer,由此,写入到写入者的任何东西可以从读取者读取。该 fork函数的功能是多任务的基础,但遗憾的是它并没有被支持所有非Unix系统。它克隆了与其父级几乎每个方面相同的重复过程,包括变量设置和打开文件。最明显的变化是进程ID和父进程ID。新程序通过分叉forking启动,然后使用exec 用新的程序替换子进程中的程序。你并不总是两个fork和exec 在一起,所以将它们作为单独的原语比你所能做的就是运行系统更具表现力和强大功能 。在实践中,你本身比exec更容易使用 fork。
When a child process dies, its memory is returned to the operating system, but its entry in the process table isn't freed. This lets a parent check the exit status of its child processes. Processes that have died but haven't been removed from the process table are called zombies, and you should clean them up lest they fill the whole process table. Backticks and the system and close functions automatically take care of this, and will work on most non-Unix systems. You have more to worry about when you go beyond these simple portable functions and use low-level primitives to launch programs. One thing to worry about is signals.
当子进程终止时,其内存将返回到操作系统,但不会释放其在进程表中的条目。这使父级可以检查其子进程的退出状态。已经死亡但尚未从进程表中删除的进程称为 僵尸,您应该清理它们,以免它们填满整个进程表。反引号和 系统以及关闭函数会自动处理这个问题,并且可以在大多数非Unix系统上运行。当您超越这些简单的可移植功能并使用低级原语启动程序时,您还有更多的担心。有一点需要担心的是信号。
Your process is notified of the death of a child it created with a signal. Signals are a kind of notification delivered by the operating system. They are used for errors (when the kernel says, "Hey, you can't touch that area of memory!") and for events (death of a child, expiration of a per-process timer, interrupt with Ctrl-C). If you're launching processes manually, you normally arrange for a subroutine of your choosing to be called whenever one of your children exits.
Each process has a default disposition for each possible signal. You may install your own handler or otherwise change the disposition of most signals. Only SIGKILL and SIGSTOP cannot be changed. The rest you can ignore, trap, or block.
16.0.2信号
通过信号创建的孩子死亡通知您的过程 。信号是操作系统提供的一种通知。它们用于错误(当内核说,“嘿,你不能触摸那个内存区域!”)和事件(孩子死亡,每个进程计时器到期,用Ctrl-C中断)。如果您手动启动流程,通常会安排您选择的子程序,只要您的一个孩子退出。
每个进程都有每个可能信号的默认处置。您可以安装自己的处理程序或以其他方式更改大多数信号的处置。只能更改SIGKILL和 SIGSTOP。其余的你可以忽略,陷阱或阻止。
Briefly, here's a rundown of the more important signals:
SIGINT
Normally triggered by Ctrl-C. This requests that a process interrupt what it's doing. Simple programs like filters usually just die, but more important ones like shells, editors, or FTP programs usually use SIGINT to stop long-running operations so you can tell them to do something else.
SIGQUIT
Also normally generated by a terminal, usually Ctrl-. Its default behavior is to generate a core dump.
SIGTERM
Sent by the kill shell command when no signal name is explicitly given. Think of it as a polite request for a process to die.
SIGUSR1 and SIGUSR2
Never caused by system events, so user applications can safely use them for their own purposes.
SIGPIPE
Sent by the kernel when your process tries to write to a pipe or socket when the process on the other end has closed its connection, usually because it no longer exists.
SIGALRM
Sent when the timer set by the alarm function expires, as described in Recipe 16.21.
SIGHUP
Sent to a process when its controlling terminal gets a hang-up (e.g., the modem lost its carrier), but it also often indicates that a program should restart or reread its configuration.
SIGCHLD
Probably the most important signal when it comes to low-level systems programming. The system sends your process a SIGCHLD when one of its child processes stops running—or, more likely, when that child exits. See Recipe 16.19 for more on SIGCHLD.
Signal names are a convenience for humans. Each signal has an associated number that the operating system uses instead of names. Although we talk about SIGCHLD, your operating system knows it only as a number, like 20 (these numbers vary across operating systems). Perl translates between signal names and numbers for you, so you can think in terms of signal names.
简而言之,这里是更重要信号的概述:
SIGINT
通常由Ctrl-C触发。这要求进程中断它正在做的事情。像过滤器这样的简单程序通常只会死掉,但更重要的程序如shell,编辑器或FTP程序通常会使用 SIGINT来停止长时间运行的操作,这样你就可以告诉他们做其他事情了。
SIGQUIT
通常也是由终端生成的,通常是Ctrl- \。它的默认行为是生成核心转储。
SIGTERM
当没有明确给出信号名称时,由kill shell命令发送 。将其视为对死亡过程的礼貌要求。
SIGUSR1和SIGUSR2
永远不会由系统事件引起,因此用户应用程序可以安全地将它们用于自己的目的。
SIGPIPE
当进程在另一端的进程关闭其连接时尝试写入管道或套接字时由内核发送,通常是因为它不再存在。
SIGALRM
当警报功能设置的计时器到期时发送,如配方16.21中所述。
SIGHUP
当其控制终端发生挂断(例如,调制解调器丢失其载波)时发送到进程,但它也经常指示程序应重新启动或重新读取其配置。
SIGCHLD
对于低级系统编程,可能是最重要的信号。当其中一个子进程停止运行时,系统会向您的进程发送SIGCHLD,或者更有可能是该子进程退出时。有关SIGCHLD的更多信息, 请参见配方16.19。
信号名称对人类来说很方便。每个信号都有一个操作系统使用的相关编号,而不是名称。虽然我们谈论SIGCHLD,但您的操作系统仅将其视为一个数字,例如20(这些数字因操作系统而异)。Perl为您翻译信号名称和数字,因此您可以根据信号名称进行思考。
You want to send a signal to a process. This could be sent to your own process or to another on the same system. For instance, you caught SIGINT and want to pass it on to your children.
您想要向进程发送信号。这可以发送到您自己的进程或同一系统上的另一个进程。例如,您捕获了SIGINT并希望将其传递给您的孩子进程。
Use kill to send a signal by name or number to the process IDs listed in the remaining arguments:
使用kill通过名称或编号将信号发送到其余参数中列出的进程ID:
kill 9 => pid; # sendpid a signal 9
kill -1 => pgrp; # send whole job a signal 1
kill USR1 =>$; # send myself a SIGUSR1
kill HUP => @pids; # send a SIGHUP to processes in @pids
Perl's kill function is an interface to the syscall of the same name. The first argument is the signal to send, identified by number or by name; subsequent arguments are process IDs to send the signal to. It returns the count of processes successfully signaled. You can only send signals to processes running under the same real or saved UID as your real or effective UID—unless you're the superuser.
Perl的kill函数是同名系统调用的接口。第一个参数是要发送的信号,由数字或名称标识; 后续参数是发送信号的进程ID。它返回成功发出信号的进程计数。除了您是超级用户之外,您只能向运行在与真实或有效UID相同的真实或已保存UID下的进程发送信号。
If the signal number is negative, Perl interprets remaining arguments as process group IDs and sends that signal to all those groups' processes using the killpg(2) syscall.
如果信号编号为负数,Perl会将剩余参数解释为进程组ID,并使用killpg(2)系统调用将该信号发送到所有这些组的进程 。
A process group is essentially a job. It's how the operating system ties related processes together. For example, when you use your shell to pipe one command into another, you've started two processes, but only one job. When you use Ctrl-C to interrupt the current job or Ctrl-Z to suspend it, this sends the appropriate signals to the entire job, which may be more than one process.
进程组本质上是一项工作。这是操作系统将相关过程联系在一起的方式。例如,当您使用shell将一个命令传递到另一个命令时,您已经启动了两个进程,但只启动了一个作业。当您使用Ctrl-C中断当前作业或使用Ctrl-Z暂停它时,它会将相应的信号发送到整个作业,这可能是多个进程。
kill can also check whether a process is alive. Sending the special pseudo-signal number 0 checks whether it's legal for you to send a signal to the process—without actually sending one. If it returns true, the process is still alive. If it returns false, the process either has changed its effective UID (in which case ! is ESRCH). Zombie processes (as described in Recipe 16.19) also report back as ESRCH.
kill也可以检查进程是否还活着。发送特殊的伪信号编号0会检查您是否合法地向进程发送信号 - 而不实际发送信号。如果它返回true,则该过程仍然存在。如果它返回false,则进程已更改其有效UID(在这种情况下!是ESRCH)。僵尸进程(如配方16.19中所述)也报告为ESRCH。
use POSIX qw(:errno_h);
if (kill 0 => $minion) {
print "$minion is alive!\n";
} elsif ($! == EPERM) { # changed uid
print "$minion has escaped my control!\n";
} elsif ($! == ESRCH) {
print "$minion is deceased.\n"; # or zombied
} else {
warn "Odd; I couldn't check on the status of $minion: $!\n";
}
16.14.4. See Also
The "Signals" sections in Chapter 16 of Programming Perl and in perlipc(1); your system's sigaction(2), signal(3), and kill(2) manpages (if you have them); the kill function in Chapter 29 of Programming Perl and perlfunc(1)
You want to control how your program responds to signals. You need to do this if you want to catch Ctrl-C, avoid accumulating finished subprocesses, or prevent your process from dying when it writes to a child that has gone away.
Use the %SIG hash to install your own handler by name or by code reference:
$SIG{QUIT} = \&got_sig_quit; # call &got_sig_quit for every SIGQUIT
$SIG{PIPE} = 'got_sig_pipe'; # call main::got_sig_pipe for every SIGPIPE
$SIG{INT} = sub { $ouch++ }; # increment $ouch for every SIGINT
%SIG also lets you ignore a signal:
$SIG{INT} = 'IGNORE'; # ignore the signal INT
It also restores handling for that signal to the default:
$SIG{STOP} = 'DEFAULT'; # restore default STOP signal handling
Perl uses the %SIG hash to control what happens when signals are received. Each key in %SIG corresponds to a signal. Each value is the action to take when Perl receives the corresponding signal. Perl provides two special behaviors: "IGNORE" to take no action when a particular signal is received, and "DEFAULT" to perform the default Unix action for that signal.
Although a C programmer might think of a signal as SIGINT, Perl uses just INT. Perl figures you only use signal names in functions that deal with signals, so the SIG prefix is redundant. This means that you'll assign to $SIG{CHLD} to change what your process does when it gets a SIGCHLD.
If you want to run your own code when a given signal is received, you have two choices of what to put in the hash: either a code reference or a subroutine name. (This means you can't name a signal handler IGNORE or DEFAULT if you store the string, but they'd be mighty strange names for signal handlers anyway.) If you use a subroutine name that isn't qualified by a package, Perl will interpret this name to be a function in the main:: package, not one in the package in which the handler was installed. A code reference refers to a subroutine in a particular package, so it is a better choice.
Perl calls your handler code with a single argument: the name of the signal that triggered it, such as "INT" or "USR1". Returning from a signal handler takes you back to whatever you were doing when the signal hit.
Perl defines two special signals, _ DIE _ and _ WARN _, whose handlers are called whenever a Perl program emits warnings through warn or dies through die. This lets you catch such warnings, and selectively trap or propagate them. The die and warn handlers are disabled while they run, so you can safely die from a _ DIE _ handler or warn from a _ WARN _ handler without fear of recursion.
The "Signals" sections in Chapter 16 of Programming Perl and in perlipc(1); your system's sigaction(2), signal(3), and kill(2) manpages (if you have them)