Operating Systems: Three Easy Pieces(操作系统:三个简单方面)5穿插章节:进程API/5.1系统调用:fork()

5穿插章节:进程API

前言:该穿插章节将更多的介绍有关系统的实际运用,特别针对于操作系统API,以及如何运用它们。如果你不喜欢实践的东西,你可以跳过这个章节。但是,你最好应该关注一下实践,因为在实际生活中经常用到它们;毕竟,如果你没有实践技能,用人公司一般是不会录用你的。

 

在这个章,我们讨论Unix系统中的进程创建。Unix有种十分有趣的新建进程的方法,系统调用fork()和exec()。还有wait(),用于进程等待另一个进程的结束。现在,我们就用一些例子来讲解关于这些函数的更多细节。至此,我们提出问题:

/*******

      Crux:怎样创建和控制进程

操作系统应该提供什么接口用于进程创建和控制?

这些接口应该如何设计才能简单易用?

********/

5.1系统调用:fork()

fork()函数通常用于创建一个新的进程。不过要注意的是,这可能是你调用的最奇怪的方法。更确切点,你有一个如下所示的程序;看一下这段代码,或者最好自己敲一下并运行。

1#include<stdio.h>

2#include<stdlib.h>

3#include<unistd.h>

4

5int

6main(int argc, char*argv[])

7{

8printf("helloworld (pid:%d)\n", (int) getpid());

9int rc = fork();

10if (rc < 0) { //fork failed; exit

11fprintf(stderr,"fork failed\n");

12exit(1);

13} else if (rc == 0){ // child (new process)

14printf("hello,I am child (pid:%d)\n", (int) getpid());

15} else { // parentgoes down this path (main)

16printf("hello,I am parent of %d (pid:%d)\n",

17rc, (int) getpid());

18}

19return 0;

20 }

当你运行这个程序(程序名:p1.c)时,你会看到如下输出:

prompt>./p1

helloworld (pid:29146)

hello,I am parent of 29147 (pid:29146)

hello,I am child (pid:29147)

prompt>

/**********************************/

让我们来理解一下程序P1.c的内容。当它首次运行时,进程打印了一条helloworld信息;在这条信息后面的是进程号,即所谓的PID。该进程的进程号是29146,在Unix系统中,如果一个人想要对这个进程做什么的话,就可以根据进程号来进行操作,比如终止这个进程。迄今,这个法子都很好用。

至此,最好玩的部分要开始了。在这个进程中,使用了一个系统调用fork()来创建一个新进程。奇怪的是,新创建的进程几乎就和原进程一样。这就意味着,对操作系统来说,此刻有两个同样的p1程序再运行,他们都是返回fork()函数的调用。如你所愿,新创建的进程(一般称为子进程,相对应的程创建该子进程的那个进程为父进程)并不是在main()函数处就开始运行的(注意,helloworld信息只打印了一次);子进程在fork()进程调用以后才产生的。

也许你已经注意到了,子进程并不是完全复制的父进程。尤其是,尽管它复制了地址空间(它私有的内存),私有的寄存器,计算器等等,但是通过fork()函数的调用,它返回的进程号却是不同的。尤其是,当父进程接收到子进程的PID以后,子进程就返回了0而已。这个不同点是很有用的,这样就方便在两个进程里面写不同的代码了。

1#include<stdio.h>

2#include<stdlib.h>

3#include<unistd.h>

4#include<sys/wait.h>

5

6int

7main(int argc, char*argv[])

8{

9printf("helloworld (pid:%d)\n", (int) getpid());

10int rc = fork();

11if (rc < 0) { //fork failed; exit

12fprintf(stderr,"fork failed\n");

13exit(1);

14} else if (rc == 0){ // child (new process)

15printf("hello,I am child (pid:%d)\n", (int) getpid());

16} else { // parentgoes down this path (main)

17int wc = wait(NULL);

18printf("hello,I am parent of %d (wc:%d) (pid:%d)\n",

19rc, wc, (int)getpid());

20}

21return 0;

22 }

程序P2.c调用fork()函数和wait()函数

这里你应该也注意到了,程序(p1.c)的输出并不是确定的。当子进程被创建以后,在系统中我们就有两个运行态进程了:父进程和子进程。假设我们在单CPU系统上运行这个程序,父子进程只能在同一时刻运行其中一个。在我们的例子中(如上),父进程首先开始运行并打印它的输出信息。在其他例子中,也可能有相反的情况发生,如我们所示的输出跟踪:

prompt>./p1

helloworld (pid:29146)

hello,I am child (pid:29147)

hello,I am parent of 29147 (pid:29146)

prompt>

 

很快我们就会在后面对CPU的调度进行详细的讲解,这里CPU调度决定了在确定时间运行哪个进程。由于调度十分复杂,因此我们不能很有把握的预测出CPU到底会先运行哪个进程。这种不确定性,产生了很多好玩的问题,尤其是在多线程程序中;因此当我们本书的第二部分——并发的章节中,我们会对这种不确定性有更进一步的了解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值