- 浏览: 141472 次
文章分类
最新评论
每个进程都有一个非负整型表示的唯一进程 ID。虽说是唯一的,但进程 ID 是可复用的,当一个进程终止时,其进程 ID 就成为复用的候选者。多数 UNIX 系统使用延迟复用算法,使得赋予新建进程的 ID 不同于最近终止进程的 ID,以免将新进程误认为是使用同一 ID 的某个已终止的先前进程。
系统中有一些专用进程,但具体细节随实现而不同。ID 为 0 的进程通常是调度进程,常常被称为交换进程。该进程是内核的一部分,并不执行任何磁盘上的程序,因此也被称为系统进程。进程 ID 1 通常是 init 进程(在 Mac OS X 10.4 中是 launchd 进程),在自举过程结束时由内核调用,以启动一个 UNIX 系统。该进程的程序文件一般是 /etc/init 或 /sbin/init,它通常读取与系统有关的初始化文件,如 /etc/rc* 文件、/etc/inittab 文件 和 /etc/init.d 中的文件等,并将系统引导到一个状态(如多用户)。init 进程不会终止,它是一个普通的用户进程而非内核中的系统进程,但它是以超级用户特权运行的。此外,每个 UNIX 系统实现都有它自己的一套提供操作系统服务的内核进程,例如,在某些 UNIX 的虚拟存储器实现中,进程 ID 2 是页守护进程,负责支持虚拟存储器系统的分页操作。
除了进程 ID,每个进程还有其他一些标识符。下列函数可返回这些标识符(它们都没有出错返回)。
一个现有进程可以调用 fork 函数创建一个新的子进程(某些平台提供了 fork 的几种变体,比如 vfork 以及 Linux 3.2.0 提供的 clone 系统调用,它允许调用者控制哪些部分由父进程和子进程共享)。
fork 函数被调用一次,但返回两次:子进程返回 0,而父进程返回新建子进程的进程 ID。子进程和父进程会继续执行 fork 调用之后的指令。子进程是父进程的副本,例如,子进程获得父进程的数据空间、堆和栈的副本,而不是共享这些存储空间部分,但子进程和父进程共享正文段。不过由于在 fork 之后经常跟随着 exec,所以现在很多实现并不执行一个父进程数据段、堆和栈的完全副本,而是使用了写时复制(Copy-On-Write,COW)技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储系统中的一页。
下面是一个 fork 函数使用示例,从中可以看到子进程对变量的修改并不影响父进程。
运行结果如下。
一般来说,fork 之后父进程和子进程的执行先后顺序是不确定的,这取决于内核所使用的调度算法。如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。
本程序中需要注意 fork 与 I/O 函数之间的交互关系。由于 write 函数是不带缓冲的,write 又是在 fork 之前调用,所以其数据写到标准输出一次。但是标准 I/O 库是带缓冲的,如果标准输出连到终端设备,则它是行缓冲的;否则它是全缓冲的。所以当以交互方式运行该程序时,只得到该 printf 输出的行一次,因为标准输出缓冲区由换行符冲洗。而当将标准输出重定向到一个文件时,却得到 printf 输出行两次。这是因为在 fork 之前调用了 printf 一次,但当调用 fork 时,该行仍在缓冲区中,然后在将父进程数据空间复制到子进程中时,该缓冲区数据也被复制到子进程中,此时父进程和子进程各自有了该行内容的缓冲区。在 exit 之前的第二个 printf 将其数据追加到已有的缓冲区中。当每个进程终止时,其缓冲区中的内容都被写到相应文件中。
另外,还需要注意的是,fork 的一个特性是父进程的所有打开文件描述符都会被复制到子进程中。重要的一点是,父进程和子进程共享同一个文件偏移量(具体可参考文件共享一节)。所以如果上面程序中若没有调用 sleep() 之类的函数来等待子进程退出的话,它们的输出就可能是相互混合的(当然这里调用 sleep 其实也不一定能保证)。
除了打开文件之外,父进程的其它大部分属性也由子进程继承,比如进程组 ID、实际组 ID、存储映像和资源限制等。
父进程和子进程的区别主要如下:
* fork 的返回值不同。
* 进程 ID 不同。
* 各自的父进程 ID 不同。
* 子进程的 tms_utime、tms_stime、tms_cutime 和 tms_ustime 的值设置为 0。
* 子进程不继承父进程设置的文件锁。
* 子进程的未处理闹钟被清除。
* 子进程的未处理信号集设置为空集。
一般使 fork 失败的两个主要原因是:(a)系统中已经有了太多的进程,(b)该实际用户 ID 的进程总数超过了限制。
系统中有一些专用进程,但具体细节随实现而不同。ID 为 0 的进程通常是调度进程,常常被称为交换进程。该进程是内核的一部分,并不执行任何磁盘上的程序,因此也被称为系统进程。进程 ID 1 通常是 init 进程(在 Mac OS X 10.4 中是 launchd 进程),在自举过程结束时由内核调用,以启动一个 UNIX 系统。该进程的程序文件一般是 /etc/init 或 /sbin/init,它通常读取与系统有关的初始化文件,如 /etc/rc* 文件、/etc/inittab 文件 和 /etc/init.d 中的文件等,并将系统引导到一个状态(如多用户)。init 进程不会终止,它是一个普通的用户进程而非内核中的系统进程,但它是以超级用户特权运行的。此外,每个 UNIX 系统实现都有它自己的一套提供操作系统服务的内核进程,例如,在某些 UNIX 的虚拟存储器实现中,进程 ID 2 是页守护进程,负责支持虚拟存储器系统的分页操作。
除了进程 ID,每个进程还有其他一些标识符。下列函数可返回这些标识符(它们都没有出错返回)。
#include <unistd.h> pid_t getpid(void); /* 返回值:调用进程的进程 ID */ pid_t getppid(void); /* 返回值:调用进程的父进程 ID */ uid_t getuid(void); /* 返回值:调用进程的实际用户 ID */ uid_t geteuid(void); /* 返回值:调用进程的有效用户 ID */ gid_t getgid(void); /* 返回值:调用进程的实际组 ID */ gid_t getegid(void); /* 返回值:调用进程的有效组 ID */
一个现有进程可以调用 fork 函数创建一个新的子进程(某些平台提供了 fork 的几种变体,比如 vfork 以及 Linux 3.2.0 提供的 clone 系统调用,它允许调用者控制哪些部分由父进程和子进程共享)。
#include <unistd.h> pid_t fork(void); /* 返回值:子进程返回 0,父进程返回子进程 ID;若出错,返回 -1 */
fork 函数被调用一次,但返回两次:子进程返回 0,而父进程返回新建子进程的进程 ID。子进程和父进程会继续执行 fork 调用之后的指令。子进程是父进程的副本,例如,子进程获得父进程的数据空间、堆和栈的副本,而不是共享这些存储空间部分,但子进程和父进程共享正文段。不过由于在 fork 之后经常跟随着 exec,所以现在很多实现并不执行一个父进程数据段、堆和栈的完全副本,而是使用了写时复制(Copy-On-Write,COW)技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储系统中的一页。
下面是一个 fork 函数使用示例,从中可以看到子进程对变量的修改并不影响父进程。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int globval = 6; // external variable in initialized data. char buf[] = "a write to stdout\n"; int main(void){ int var = 88; // automatic variable on the stack pid_t pid; // 不写末尾的 null 字节 if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1){ printf("write error\n"); exit(2); } printf("before fork\n"); // we don't flush stdout if((pid=fork()) < 0){ printf("fork error\n"); exit(2); }else if(pid == 0){ // child globval++; var++; }else{ // parent sleep(2); } printf("pid=%ld, glob=%d, var=%d\n", (long)getpid(), globval, var); exit(0); }
运行结果如下。
$ ./forkDemo.out a write to stdout before fork pid=430, glob=7, var=89 # 子进程的变量值改变了 pid=429, blob=6, var=88 $ $ ./forkDemo.out > temp.out $ cat temp.out a write to stdout before fork # 子进程输出一次 pid=432, blob=7, var=89 before fork # 父进程输出一次 pid=431, blob=6, var=88 $
一般来说,fork 之后父进程和子进程的执行先后顺序是不确定的,这取决于内核所使用的调度算法。如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。
本程序中需要注意 fork 与 I/O 函数之间的交互关系。由于 write 函数是不带缓冲的,write 又是在 fork 之前调用,所以其数据写到标准输出一次。但是标准 I/O 库是带缓冲的,如果标准输出连到终端设备,则它是行缓冲的;否则它是全缓冲的。所以当以交互方式运行该程序时,只得到该 printf 输出的行一次,因为标准输出缓冲区由换行符冲洗。而当将标准输出重定向到一个文件时,却得到 printf 输出行两次。这是因为在 fork 之前调用了 printf 一次,但当调用 fork 时,该行仍在缓冲区中,然后在将父进程数据空间复制到子进程中时,该缓冲区数据也被复制到子进程中,此时父进程和子进程各自有了该行内容的缓冲区。在 exit 之前的第二个 printf 将其数据追加到已有的缓冲区中。当每个进程终止时,其缓冲区中的内容都被写到相应文件中。
另外,还需要注意的是,fork 的一个特性是父进程的所有打开文件描述符都会被复制到子进程中。重要的一点是,父进程和子进程共享同一个文件偏移量(具体可参考文件共享一节)。所以如果上面程序中若没有调用 sleep() 之类的函数来等待子进程退出的话,它们的输出就可能是相互混合的(当然这里调用 sleep 其实也不一定能保证)。
除了打开文件之外,父进程的其它大部分属性也由子进程继承,比如进程组 ID、实际组 ID、存储映像和资源限制等。
父进程和子进程的区别主要如下:
* fork 的返回值不同。
* 进程 ID 不同。
* 各自的父进程 ID 不同。
* 子进程的 tms_utime、tms_stime、tms_cutime 和 tms_ustime 的值设置为 0。
* 子进程不继承父进程设置的文件锁。
* 子进程的未处理闹钟被清除。
* 子进程的未处理信号集设置为空集。
一般使 fork 失败的两个主要原因是:(a)系统中已经有了太多的进程,(b)该实际用户 ID 的进程总数超过了限制。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1249在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1542伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 795多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 944终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 567尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 937虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 707tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 810终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 434终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 577在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 945在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 614在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 492消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 482有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 413FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 384管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 879readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 453POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 596记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 434UNIX 系统中,守护进程遵循下列通用惯例。 ...
相关推荐
它创建了一个完全相同的子进程副本,并返回一个进程标识符(PID)。fork 函数的返回值在父进程和子进程中不同:在父进程中,返回子进程的 PID;在子进程中,返回 0。 fork 函数的基本功能可以通过以下示例程序来...
其次,进程标识符(Process Identifier,简称PID)是操作系统为每个进程分配的唯一标识号。它是一个整数类型的值,用于区分系统中的每一个进程。操作系统通过PID来管理进程的各种状态,如运行、挂起、停止等,并且...
“Windows 进程终止的消息标识符”这一标题直接指出了本文档的主要内容:Windows操作系统中进程终止时所涉及的一些特定消息标识符。这些标识符对于理解进程终止的具体原因、调试问题以及进行系统维护具有重要意义。 ...
/*进程标识符*/ int prio; /*进程优先数*/ int round; /*进程时间轮转时间片*/ int cputime; /*进程占用CPU时间*/ int needtime; /*进程到完成还要的时间*/ int count; /*计数器*/ char state; /*进程的状态*...
Linux安全攻略——僵尸进程.pdf中讨论了Linux操作系统中的进程管理机制,包括进程的概念、进程调度、进程树、进程标识符、进程生命周期等方面的知识点。 进程概念 在Linux操作系统中,进程是动态的,程序是静态的...
在IT领域,尤其是在系统编程和软件开发中,进程标识符(PID)和窗口句柄(HWND)是两个关键概念。本文将深入探讨易语言中如何使用这些概念,以及相关的函数和方法,如`EnumWindowsProc`、`GetWindowThreadProcessId`...
在Windows操作系统中,获取进程名和PID(进程标识符)是一项常见的系统编程任务,尤其对于开发者来说,了解如何实现这一功能至关重要。在这个场景下,我们关注的是使用VC++(Visual C++)和MFC(Microsoft ...
在探讨如何通过编程手段终止Excel进程的函数时,我们首先需要理解进程管理和Windows操作系统环境下的进程操作机制。在本文中,我们将深入解析如何利用C#语言中的`System.Diagnostics.Process`类来实现对Excel进程的...
进程控制块(PCB)是进程调度的核心结构,每个进程对应一个PCB,用于存储进程的相关信息,如进程标识符、进程状态、寄存器内容等。在单处理器系统中,PCB通常包括以下组成部分: * 进程标识符:用于唯一标识每个...
首先可利用GetCurrentProcessId() 函数来查看的进程特性是进程标识符 (PID),返回的PID在整个系统中都可使用。其他的可显示当前进程信息的API函数如GetStartupInfo()和GetProcessShutdownParameters() 可给出进程的...
3. 用户输入进程标识符以及进程所需的时间,申请空间存放进程 PCB 信息 4. 每一个时间片完毕输出各进程的进程号,CPU 时间(即已经占用的 CPU 时间),所需时间(即还需要的 CPU 时间),以及状态(即用 W 表示等待...
3. 用户输入进程标识符和所需时间:用户将输入进程标识符和所需时间,我们将根据用户的输入来初始化进程的相关信息。 4. 每一个时间片结束输出各进程的信息:在每个时间片结束时,我们将输出各进程的进程号、已运行...
实验目的是通过实际操作加深对进程同步互斥概念的理解,掌握信号量的使用方法和P、V操作函数的定义。 1. **信号量与P、V操作**: - 信号量是一种同步工具,用于解决多个进程对共享资源的访问冲突。 - P操作(即...
1. **进程表项**:包括进程标识符(PID)、用户标识符(UID)、进程状态、事件描述符等。 2. **U区**:用于存放进程表项的扩充信息,如进程表项指针、用户标识符等。 3. **系统区表项**:用于记录各个段在物理存储器...
它创建一个新的进程,并返回进程标识符(PID)。fork()调用返回两次,一次是在父进程中,返回子进程的 PID;另一次是在子进程中,返回 0。fork()调用可以用来创建多个子进程,以实现并发执行。 三、wait()系统调用 ...
每个进程都有一个独特的进程标识符(process ID),可以通过 getpid 函数获得,还有一个记录父进程 pid 的变量,可以通过 getppid 函数获得变量的值。 fork 函数执行完毕后,出现两个进程,它们的内容基本完全一样...
答:子进程被创建后,核心将其分配一个进程表项和进程标识符,检查同时运行的进程数目,并且拷贝进程表项的数据,由子进程继承父进程所有文件。 * 管道通信的实现方法是什么?答:管道通信是指使用pipe()系统调用...
它包含了进程的所有信息,如进程标识符、进程状态、优先级、内存地址等。PCB 是进程的唯一标识符,它是操作系统管理进程的关键。 3. 创建新的进程: 在实验中,我们使用 C 语言编写的 create 函数来创建新的进程。...
进程ID是操作系统分配给每个进程的唯一标识符,可以通过`OpenProcess`函数获得进程句柄,再通过`GetProcessId`函数获取对应的进程ID。`OpenProcess`需要进程ID和访问权限作为参数,访问权限可以包括读取、写入、挂起...
在`fork()`成功执行后,调用进程(父进程)和新创建的进程(子进程)都会继续执行`fork()`之后的代码,但它们拥有各自的进程标识符(PID)、内存空间、文件描述符等资源。 1. 进程复制:`fork()`会复制父进程的所有...