glibc 知:手册26:进程

本文深入探讨了进程的概念,包括进程创建(如fork、_Fork、vfork)、执行文件(exec系列函数)以及进程的识别和控制。重点介绍了system函数用于简单执行命令,而waitpid用于等待子进程完成。同时,文章讲解了进程完成状态的检查以及BSD进程等待函数。示例代码展示了如何创建并控制子进程执行命令。

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 进程

Processes

进程是分配系统资源的原始单位。每个进程都有自己的地址空间和(通常)一个控制线程。一个进程执行一个程序;您可以让多个进程执行同一个程序,但每个进程在其自己的地址空间内都有自己的程序副本,并独立于其他副本执行它。

进程是分层组织的。每个进程都有一个明确安排创建它的父进程。给定父进程创建的进程称为其子进程。子进程从父进程继承了许多属性。

本章描述程序如何创建、终止和控制子进程。实际上,涉及到三个不同的操作:创建一个新的子进程,使新进程执行一个程序,以及协调子进程与原程序的完成。

系统函数为运行另一个程序提供了一种简单、可移植的机制;它会自动完成所有三个步骤。如果您需要更多地控制如何完成此操作的细节,您可以使用原始函数来单独执行每个步骤。

2.1. 运行命令

Running a Command

运行另一个程序的简单方法是使用系统函数。这个函数完成了运行子程序的所有工作,但它不能让你对细节有太多的控制:你必须等到子程序终止才能做任何其他事情。

函数:int system (const char *command)

Preliminary: | MT-Safe | AS-Unsafe plugin heap lock | AC-Unsafe lock mem | See POSIX Safety Concepts.

此函数将命令作为 shell 命令执行。在 GNU C 库中,它总是使用默认的 shell sh 来运行命令。特别是,它搜索 PATH 中的目录以查找要执行的程序。如果无法创建 shell 进程,则返回值为 -1,否则为 shell 进程的状态。有关如何解释此状态代码的详细信息,请参阅进程完成

如果命令参数是空指针,则返回值为零表示没有可用的命令处理器。

这个函数是多线程程序中的一个取消点。如果线程在调用系统时分配了一些资源(如内存、文件描述符、信号量或其他),则会出现问题。如果线程被取消,这些资源将保持分配状态,直到程序结束。为了避免这种对系统的调用应该使用取消处理程序来保护。

系统函数在头文件 stdlib.h 中声明。

可移植性注意:一些 C 实现可能没有任何可以执行其他程序的命令处理器的概念。可以通过执行system(NULL)来判断命令处理器是否存在;如果返回值非零,则命令处理器可用。

popen 和 pclose 函数(参见管道到子进程)与系统函数密切相关。它们允许父进程与正在执行的命令的标准输入和输出通道进行通信。

2.2. 进程创建概念

Process Creation Concepts

本节概述了进程以及创建进程并使其运行另一个程序所涉及的步骤。

当调用函数 posix_spawn、fork、_Fork 或 vfork 之一时,将创建一个新进程。(system 和 popen 也在内部创建新进程。)由于 fork 函数的名称,创建新进程的行为有时称为 fork 进程。每个新进程(子进程或子进程)都分配有一个进程 ID,与父进程的进程 ID 不同。请参阅进程标识

在 fork 一个子进程之后,父进程和子进程都继续正常执行。如果您希望您的程序在继续之前等待子进程完成执行,则必须在 fork 操作之后通过调用 wait 或 waitpid 显式执行此操作(请参阅进程完成)。这些函数为您提供有关子进程终止原因的有限信息——例如,它的退出状态代码。

新派生的子进程在 fork 或 _Fork 调用返回的位置继续执行与其父进程相同的程序。您可以使用 fork 或 _Fork 的返回值来判断程序是在父进程中运行还是在子进程中运行。

让多个进程运行同一个程序只是偶尔有用。但是孩子可以使用其中一个 exec 函数执行另一个程序;请参阅执行文件。进程正在执行的程序称为它的进程映像。开始执行一个新程序会导致进程忘记它之前的进程映像;当新程序退出时,进程也退出,而不是返回到之前的进程映像。

2.3. 进程识别

Process Identification

每个进程都由一个进程 ID 号命名,一个 pid_t 类型的值。进程 ID 在创建时分配给每个进程。进程 ID 会随着时间的推移而重复使用。当相应进程的父进程在进程终止后等待进程 ID 时,进程的生命周期结束。请参阅进程完成。(父进程可以隐式地安排这种等待。)进程 ID 仅在进程的生命周期内唯一标识进程。根据经验,这意味着该进程必须仍在运行。

进程 ID 还可以表示进程组和会话。请参阅作业控制

在 Linux 上,由 pthread_create 创建的线程也接收一个线程 ID。初始(主)线程的线程 ID 与整个进程的进程 ID 相同。随后创建的线程的线程 ID 是不同的。它们是从与进程 ID 相同的编号空间分配的。进程 ID 和线程 ID 有时也统称为任务 ID。与进程相比,线程永远不会被显式等待,因此一旦线程退出或被取消,线程 ID 就可以重用。即使对于可连接的线程也是如此,而不仅仅是分离的线程。线程被分配给一个线程组。在 Linux 上运行的 GNU C 库实现中,进程 ID 是进程中所有线程的线程组 ID。

您可以通过调用 getpid 来获取某个进程的进程 ID。getppid函数返回当前进程的父进程ID(这也称为父进程ID)。您的程序应包含头文件 unistd.h 和 sys/types.h 以使用这些功能。

数据类型:pid_t

pid_t 数据类型是有符号整数类型,能够表示进程 ID。在 GNU C 库中,这是一个 int。

函数:pid_t getpid (void)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

getpid 函数返回当前进程的进程 ID。

函数:pid_t getppid (void)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

getppid 函数返回当前进程的父进程的进程 ID。

函数:pid_t gettid (void)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

gettid 函数返回当前线程的线程 ID。返回值是从Linux内核获取的,不受缓存。请参阅上面对线程 ID 的讨论,特别是关于重用已退出线程的 ID。

这个函数是 Linux 特有的。

2.4. 创建进程

Creating a Process

fork 函数是创建进程的原语。它在头文件 unistd.h 中声明。

函数:pid_t fork (void)

Preliminary: | MT-Safe | AS-Unsafe plugin | AC-Unsafe lock | See POSIX Safety Concepts.

fork 函数创建一个新进程。

如果操作成功,那么父进程和子进程都有,都看到fork返回,但是值不同:在子进程中返回值0,在父进程中返回子进程ID。

如果进程创建失败,则 fork 在父进程中返回值 -1。为 fork 定义了以下 errno 错误条件:

EAGAIN

没有足够的系统资源来创建另一个进程,或者用户已经有太多的进程在运行。这意味着超过 RLIMIT_NPROC 资源限制,通常可以增加;请参阅限制资源使用

ENOMEM

该过程需要的空间多于系统所能提供的空间。

子进程与父进程不同的具体属性有:

  • 子进程有自己唯一的进程 ID。
  • 子进程的父进程ID是其父进程的进程ID。
  • 子进程获取自己的父进程打开文件描述符的副本。随后更改父进程中文件描述符的属性不会影响子进程中的文件描述符,反之亦然。请参阅对文件的控制操作。但是,与每个描述符关联的文件位置由两个进程共享;请参阅文件位置
  • 子进程经过的处理器时间设置为零;请参阅处理器时间查询
  • 子进程不继承父进程设置的文件锁。请参阅
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canpool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值