进程的创建和终止(超详细)(cmp指令)
进程的创建和终止(超详细)
大多数系统的进程能够并发执行,它们可以动态创建和删除。因此,操作系统必须提供机制,用于创建进程和终止进程。
进程创建
进程在执行过程中可能创建多个新的进程。每个新进程可以再创建其他进程,从而形成。大多数的操作系统(包括 UNIX、Linux 和 Windows)对进程的识别采用的是唯一的,pid 通常是一个整数值。系统内的每个进程都有一个唯一 pid,它可以用作索引,以便访问内核中的进程的各种属性。图 1 典型Linux系统的一个进程树图 1 显示了 Linux 操作系统的一个典型进程树,包括进程的名称和 pid(我们通常使用进程这个术语,不过 Linux 偏爱""这个术语)。进程 init(它的 pid 总是 1),作为所有用户进程的根进程或父进程。一旦系统启动后,进程init可以创建各种用户进程,如 Web 服务器、打印服务器、ssh 服务器等。在图 1 中,kthreadd 和 sshd 为 init 的两个子进程。kthreadd 进程负责创建额外进程,以便执行内核任务(这里为 khelper 和 pdflush)。sshd 进程负责管理通过 ssh 连到系统的客户端。login 进程负责管理直接登录到系统的客户端。在这个例子中,客户已登录,并且使用 bash 外壳,它所分配的 pid 为 8416。采用 bash 命令行界面,这个进程还创建了进程 ps 和 emacs 编辑器。对于 UNIX 和 Linux 系统,我们可以通过 ps 命令得到一个进程列表。例如,命令ps -el
可以列出系统中的所有当前活动进程的完整信息。通过递归跟踪父进程一直到进程 init,可以轻松构造类似图 1 所示的进程树。一般来说,当一个进程创建子进程时,该子进程需要一定的资源(CPU 时间、内存、文件、I/O 设备等)来完成任务。子进程可以从操作系统那里直接获得资源,也可以只从父进程那里获得资源子集。父进程可能要在子进程之间分配资源或共享资源(如内存或文件)。限制子进程只能使用父进程的资源,可以防止创建过多进程,导致系统超载。除了提供各种物理和逻辑资源外,父进程也可能向子进程传递初始化数据(或输入)。例如,假设有一个进程,其功能是在终端屏幕上显示文件如 image.jpg 的状态。当该进程被创建时,它会从父进程处得到输入,即文件名称 image.jpg。通过这个名称,它会打开文件,进而写出内容。它也可以得到输出设备名称。另外,有的操作系统会向子进程传递资源。对于这种系统,新进程可得到两个打开文件,即 image.jpg 和终端设备,并且可以在这两者之间进行数据传输。当进程创建新进程时,可有两种执行可能:- 父进程与子进程并发执行。
- 父进程等待,直到某个或全部子进程执行完。
- 子进程是父进程的复制品(它具有与父进程同样的程序和数据)。
- 子进程加载另一个新程序。
include以上所示的 C 程序说明了上述 UNIX 系统调用例子中。这里有两个不同进程,但运行同一程序。这两个进程的唯一差别是:子进程的 pid 值为0,而父进程的 pid 值大于0(实际上,它就是子进程的 pid)。子进程继承了父进程的权限、调度属性以及某些资源,诸如打开文件。通过系统调用 execlp()(这是系统调用 exec() 的一个版本),子进程采用 UNIX 命令 /bin/ls(用来列出目录清单)来覆盖其地址空间。通过系统调用 wait(),父进程等待子进程的完成。当子进程完成后(通过显示或隐式调用 exit()),父进程会从 wait() 调用处开始继续,并且结束时会调用系统调用 exit()。这可用图 2 表示。图 2 通过系统调用 fork() 创建进程当然,没有什么可以阻止子进程不调用 exec(),而是继续作为父进程的副本来执行。在这种情况下,父进程和子进程会并发执行,并采用同样的代码指令。由于子进程是父进程的一个副本,这两个进程都有各自的数据副本。作为另一个例子,接下来看一看 Windows 的进程创建。进程创建采用 Windows API 函数 CreateProcess(),它类似于 fork()(这是父进程用于创建子进程的)。不过,fork() 让子进程继承了父进程的地址空间,而 CreateProcess() 在进程创建时要求将一个特定程序加载到子进程的地址空间。再者,fork() 不需要传递任何参数,而 CreateProcess() 需要传递至少 10 个参数。include include int main() { pid_t pid; /* fork a child process */ pid = fork(); if (pid < 0) { /* error occurred */\ fprintf(stderr, "Fork Failed"); return 1; } else if (pid == 0) { /* child process */ execlp("/bin/ls","ls",NULL); } else { /* parent process */ /* parent will wait for the child to complete */ wait(NULL); printf("Child Complete"); } return 0; }
include以上所示的 C 程序演示了函数 CreateProcess(),它创建了一个子进程,并且加载了应用程序 mspaint.exe。这里选择了 10 个参数中的许多默认值来传递给 CreateProcess()。传递给 CreateProcess() 的两个参数,为结构 STARTUPINFO 和 PROCESS-INFORMATION 的实例:include int main(VOID) { STARTUPINFO si; PR0CESS_INF0RMATI0N pi; /* allocate memory */ ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); /* create child process */ if (!CreateProcess(NULL, /* use command line */ "C: \\WIND0WS\\system32\\mspaint. exe" , /* command */ NULL, /* don,t inherit process handle */ NULL, /* don^ inherit thread handle */ FALSE, /* disable handle inheritance */ 0, /* no creation flags */ NULL, /* use parentJs environment block */ NULL, /* use parent1s existing directory */ &si, &pi)) { fprintf (stderr, "Create Process Failed"); return -1; } /* parent will wait for the child to complete */ WaitForSingleObject(pi.hProcess,INFINITE); printf("Child Complete"); /* close handles */ CloseHandle(pi.hProcess); CloseHandle(pi.hThread); }
- 结构 STARTUPINFO 指定新进程的许多特性,如窗口大小和外观、标准输入与输出的文件句柄等;
- 结构 PR0CESS_INF0RMATI0N 含新进程及其线程的句柄与标识符。
进程终止
当进程完成执行最后语句并且通过系统调用 请求操作系统删除自身时,进程终止。这时,进程可以返回状态值(通常为整数)到父进程(通过系统调用 wait())。所有进程资源,如物理和虚拟内存、打开文件和 I/O 缓冲区等,会由操作系统释放。在其他情况下也会出现进程终止。进程通过适当系统调用(如 Windows 的 Terminate-Process()),可以终止另一进程。通常,只有终止进程的父进程才能执行这一系统调用。否则,用户可以任意终止彼此的作业。记住,如果终止子进程,则父进程需要知道这些子进程的标识符。因此,当一个进程创建新进程时,新创建进程的标识符要传递到父进程。父进程终止子进程的原因有很多,如:- 子进程使用了超过它所分配的资源。(为判定是否发生这种情况,父进程应有一个机制,以检查子进程的状态)。
- 分配给子进程的任务,不再需要。
- 父进程正在退出,而且操作系统不允许无父进程的子进程继续执行。
/* exit with status 1 */exit(1);
事实上,在正常终止时,exit() 可以直接调用(如上所示),也可以间接调用(通过 main() 的返回语句)。父进程可以通过系统调用 wait(),等待子进程的终止。系统调用 wait() 可以通过参数,让父进程获得子进程的退出状态;这个系统调用也返回终止子进程的标识符,这样父进程能够知道哪个子进程已经终止了:pid_t pid;int status;pid = wait(festatus);
当一个进程终止时,操作系统会释放其资源。不过,它位于进程表中的条目还是在的,直到它的父进程调用 wait();这是因为进程表包含了进程的退出状态。当进程已经终止,但是其父进程尚未调用 wait(),这样的进程称为。所有进程终止时都会过渡到这种状态,但是一般而言僵尸只是短暂存在。一旦父进程调用了 wait(),僵尸进程的进程标识符和它在进程表中的条目就会释放。如果父进程没有调用 wait() 就终止,以致于子进程成为孤儿进程,那么这会发生什么?Linux 和 UNIX 对这种情况的处理是:将 init 进程作为孤儿进程的父进程。进程 init 定期调用 wait(),以便收集任何孤儿进程的退出状态,并释放孤儿进程标识符和进程表条目。版权声明:
本站所有资源均为站长或网友整理自互联网或站长购买自互联网,站长无法分辨资源版权出自何处,所以不承担任何版权以及其他问题带来的法律责任,如有侵权或者其他问题请联系站长删除!站长QQ754403226 谢谢。