linux中的fork

      大家知道Linux中创建子进程的一个很好的方法是函数调用fork,但是很多初学者对fork的理解上可能有点困难。下面举个例子来看看fork的用法吧。

      其实,大家用fork的时候记住fork是 分叉的意思就很好理解了。
      先看一下fork() 的经典模式:
     

      首先来看fork 的返回值,有三种情况 -1,0,>0
      返回-1: 当然是失败了,也不会分裂成两个进程。
      返回0是子进程。
      返回〉0 是父进程,此时返回的值当然是子进程的pid了。  
      这个过程其实可以这样简单的解释: 当进程遇到fork调用时,将此进程整个拷贝一份,即子进程。此时,该进程的返回值被设置为〉0,即刚才拷贝生成的子进程pid,而在拷贝生成的进程中,将返回值设置为0 。也就是此时已经有两个进程,只有pid的值不同(忽略一起其他的设置)。
      此时,两个进程都从fork开始往下执行,只是pid不同,所以 if ..... else if .... esle 会根据pid不同来执行相应的代码,并不是说某一部分是父进程的代码,某一部分是自进程的代码. 只是一些条件判断而已.所以当fork后,整个代码都会被两个进程执行,只是(fork成功时)子进程中的pid 为零,所以 else if条件成立,其它两个不成立,所以执行else if中的代码.父进程中pid >0的,所以else成立,执行其中的代码.  如果fork失败,当然是返回-1,此时是没有子进程的.
      需要注意的是,可以通过getpid() 和getppid()获取自己的进程和父进程的pid; fork之后是父进程先执行还是子进程先执行,这是取决于cpu调用算法的,就是说他们谁先执行都有可能。
      下面再提供一些例子,供大家思考,深入理解fork。
      1. 编写一段程序,使用系统调用 fork ()创建两个了进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符。试观察记录屏幕上的显示结果,并分析原因。
      

   运行结果:[root@localhost ~]# gcc -o lsj lsj.c
                  [root@localhost ~]# ./lsj
                  bca[root@localhost ~]# ./lsj
                  bca[root@localhost ~]# ./lsj
                  bca[root@localhost ~]# ./lsj
                  ba[root@localhost ~]# c./lsj

                  bca[root@localhost ~]# ./lsj
                  bca[root@localhost ~]# ./lsj
                  bca[root@localhost ~]# ./lsj
                  ca[root@localhost ~]# b

   分析原因:①从进程并发执行来看,各种情况都有可能。上面的三个进程没有同步措施,所以父进程与子进程的输出内容会叠加在一起。输出次序带有随机性。

                  ②由于函数printf( )在输出字符串时不会被中断,因此,字符串内部字符顺序输出不变。但由于进程并发执行的调度顺序和父子进程抢占处理机问题,输出字符串的顺序和先后随着执行的不同而发生变化。这与打印单字符的结果相同。

      2.编写一段程序,使用系统调用fork()来创建两个子进程,并由父进程重复显示字符串:“parent和自己的标识数,而子进程则重复显示字符串“child和自己的标识数。

结果:
[root@localhost ~]# gcc -o lsj lsj.c
[root@localhost ~]# ./lsj
child 1’s pid=4852
child 2’s pid=4852
parent’s pid=3224
[root@localhost ~]# ./lsj
child 1’s pid=4903
child 2’s pid=4903
parent’s pid=3224
[root@localhost ~]# ./lsj
child 1’s pid=4918
parent’s pid=3224
[root@localhost ~]# child 2’s pid=1
./lsj
child 1’s pid=4945
child 2’s pid=4945
parent’s pid=3224

……
[root@localhost ~]# ./lsj
child 1’s pid=5270
parent’s pid=3224
[root@localhost ~]# child 2’s pid=5270

分析原因:进程标识数随机产生,输出次序也有随机性

3. 编写一段程序,使用系统调用fork()来创建一个子进程。子进程通过系统调用exec()更换自己的执行代码,显示新的代码“new program.”后,调用exit()结束。而父进程则调用waitpid()等待子进程结束,并在子进程结束后显示子进程的标识符,然后正常结束。

新建C程序new.c:

分析原因:先编译root下的new.c,产生new应用程序,execl(“/root/new”,0);调用root目录下的new应用程序,执行输出new program.

 

 

 

[问题讨论]

1.系统是怎样创建进程的?

答:系统创建的第一个进程是init进程。系统中所有的进程都是由当前进程使用系统调用fork()创建的。子进程被创建后继承了父进程的资源。 子进程共享父进程的虚存空间。写时拷贝 (copy on write):子进程在创建后共享父进程的虚存内存空间,只是在两个进程中某一个进程需要向虚拟内存写入数据时才拷贝相应部分的虚拟内存。子进程在创建后执行的是父进程的程序代码。

 

2.可执行文件加载时进行了哪些处理?

答:当操作系统装载一个可执行文件的时候,首先操作系统判断该文件是否是一个合法的可执行文件。如果是操作系统将按照段表中的指示为可执行程序分配地址空间。

 

3.当首次调用新创建进程时,其入口在哪里?

答:fork系统调用创建的子进程继承了原进程的context,也就是说fork调用成功后,子进程与父进程并发执行相同的代码。但由于子进程也继承了父进程的程序指针,所以子进程是从fork()后的语句开始执行(也就是新进程调用的入口)。另外fork在子进程和父进程中的返回值是不同的。在父进程中返回子进程的PID,而在子进程中返回0。所以可以在程序中检查PID的值,使父进程和子进程执行不同的分支。


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值