设为首页 加入收藏

TOP

基于toyix的进程和轻权进程的学习(二)
2017-01-02 08:15:12 】 浏览:149
Tags:基于 toyix 进程 学习

就会创建与这个程序完成相关的进程,不需要我们手动创建。我们可以用get_pid函数得到调用进程的进程号:



do1执行程序,结果如下:



发现结果进程的序列号为1,在进程就绪状态进程监视器显示的进程号也为1


我们再用do111执行三个进程:



可以看到程序创建了三个进程,它们的进程号分别为123.


也就是说,这里进程号是从1开始连续分配的,每执行一次程序就创建一个进程,那么一个程序可以创建开启多个进程吗?我们可以使用fork函数和frk函数来创建多个进程。


Fork函数的作用是创建一个与父进程相同的子进程:



执行结果为:



Fork创建了一个子进程,所以打印出了2个进程号。我们把创建新进程前的进程叫做父进程,把创建新进程后的进程叫做子进程,那么这两个进程哪个是父进程,哪个是子进程呢?我们是无法通过它们的进程号来区分的,但是我们知道fork函数在创建进程成功后,在父进程中会返回子进程的进程号,在子进程中会返回0,也就是说,在父进程中fork()的值是一个非0值,而在子进程中,fork()的值为0.那么我们可以通过判断fork()的值是否为0来判断这个进程是父进程还是子进程:



执行结果如下:



有程序的执行结果可知,进程1是父进程,进程2是子进程。


Fork函数有两个特点:1、父子进程不共享数据段。2、父进程结束后不撤销子进程。


什么是数据段呢?进程是程序的执行过程,它的执行需要调用程序里设置的数据来给cpu进行计算得到需要的结果,这些程序里设置的数据就存储在一段存储空间里,这个空间就叫做这个进程的数据段,它是进程所需资源的一种,是在创建进程时由操作系统分配给进程的。


进程一般由控制信息和本体信息组成,进程号就属于进程的控制信息,本体信息一般是由代码段、数据段、栈段所组成。栈段是用来保存如cpu现场等进程的特有信息,我们知道局部变量就是用栈段存储的,比如上面的变量i,它在父子进程中的值是不同的,所以我们可以知道栈段是不可能被父子进程共享的。我们知道全局变量是存放在数据段里的,要验证数据段是否共享,我们可以对全局变量进行操作和打印:



执行结果为:



所以这里打印的a的值都为1,而如果两个进程共享一个数据段,那么a会自加2次,输出的结果应该为2。所以这里的两个进程没有共享数据段。


要判断两个进程的代码段是否共享,只需要看看它们代码段的地址是否相同即可,我们打印这两个进程的地址:



执行结果如下:



我们可以看到这两个进程的代码段并不一样,所以它们的代码段不是共享的。所以fork函数创建的两个进程的代码段、数据段、栈段都是不共享的。而且我们注意到父进程是先打印的,也就是说父进程是先执行的,在父进程执行后子进程并没有被撤销。


那么frk函数和fork函数有什么区别呢?由函数手册可知,frk函数也是创建一个与父进程相同的子进程,而它的不同之处是这两个进程是共享数据段的,而且如果父进程结束了,子进程也会被撤销。


我们将上面的函数里的fork函数改成frk函数来执行:


第一个程序修改如下,这里在a++后面调用delay函数是为了在父进程a++执行完后,子进程有足够的时间去完成a++操作,如果父进程的a++执行后就执行下面的打印语句,子进程可能还没有执行a++,那么可能的结果就是父进程打印出来的a的值为1,子进程打印出来的值为2



执行结果如下:



这里打印出来a的值都为2,说明了这两个进程是共享数据段的。


我们把第二个程序修改为如下:



这里程序最后的delay100)的作用也是让父进程暂停等待子进程执行,否则父进程结束后会撤销子进程,那么子进程的打印语句就无法执行。


执行结果如下:



这里的打印出来的两个函数的代码段地址不同,所以frk创建的子进程也不与父进程共享代码段。我们注意到子进程打印出来的地址是一个错误的地址,因为我们是用十六进制打印的地址,而r是不属于十六进制的数的,如果用十进制打印,结果如下:



这个地址应该是正确地地址,那么为什么用十六进制打印会出现错误的地址呢?按理说%x不应该输出大于f的值,那么我觉得可能是这里printf里对%x的实现有问题。


如果上面的程序最后不加delay100),执行结果如下:



因为父进程在执行完后撤销了子进程,所以只有父进程执行了printf函数。


所以我们得到的结论是frk函数创建的子进程与父进程共享数据段,不共享代码段和栈段。


我们之前在讨论进程的三态模型时发现在正常情况下,进程是从运行态退出的,但是frk函数创建的子进程可能没有执行完就因为父进程的结束而被撤销了,这是为什么呢?查询资料发现,实际上还存在从就绪态或者阻塞态到结束状态的释放转换。进程的退出可以分为正常退出和异常退出,异常退出的原因包括进程执行超时、内存不足、非法指令或地址访问、I/O操作失败、被其他进程所终止等,比如父进程可以在任何时间终止子进程,只是fork中设置的是父进程退出不撤销子进程,而frk中设置的是父进程结束时撤销子进程,我们也可以写一个函数创建子进程,并在父进程执行时就撤销它。


那么为什么fork函数和frk函数的功能差不多,但是一个共享数据段、一个不共享数据段呢?我们知道进程包含数据段,而fork创建的子进程不与父进程共享数据段,所以在创建时系统要把父进程数据段的内容复制到子进程的数据段中,这会造成一定的开销,而且也不利于父子进程间交换数据,所以forkfrk创建的进程各有特点。为了区别fork创建的进程,我们把frk创建的进程叫做轻权进程。


虽然frkfork创建的子进程都不与父进程共享代码段,但是父子进程代码段的内容都是一样的,怎么让子进程的代码段的内容与父进程不一样呢?我们可以用exec函数,它的功能是执行一个可执行文件,创建一个进程覆盖当前的进程,这样我们就可以在生成的子进程中创建一个新的进程,而这个新的进程可以执行与父进程完全不同的功能。如下,编写程序1为:



程序2为:



执行结果为:



程序中子进程在执行exec函数后被新建的2.prg的进程所覆盖,从而执行2.prg的内容,这样就可以使进程1的子进程运行时启动2.prg的进程了。


如果把exec放到父进程里面会怎么样呢?



执行结果如下:



父进程被覆盖了,那么再把fork换成frk试试看:




执行结果如下:



只执行了主进程的内容,这是因为frk的父进程在结束时会撤销轻权进程,这是因为主进程和轻权进程共享一个数据段,主进程结束后会释放数据段,如果这时轻权进程还存在的话,就会继续使用数据段,但是这时数据段已经被释放了,可能内存空间已经被别的程序使用,如果继续使用会出错,所以必须要撤销轻权进程。但是如果我们在主进程里添加延时函数或者输入函数,等到轻权进程执行,就可以达到之前程序的结果。


那么如果exec是在轻权进程中执行,它所创建的进程还是轻权进程吗?Exec函数是执行一个完全不同的程序,那么这个程序的数据段肯定与当前程序不同,即新的进程不与主进程共享数据段,那么它就不是一个轻权进程。所以我们在轻权进程中用exec创建的进程是一个普通进程而不是轻权进程。我们可以实现程序来证明:


程序1为:



程序2为:



执行结果为:



结果发现在执行2.prg的过程中,也就是输出a的过程中我们按回车结束主进程,但是此时还是在继续输出a,这说明当主进程结束后,exec创建的进程并没有被

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇配置TC2.0运行环境 下一篇高级语言里的函数在汇编里的实现..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目