进程管理的实验性应用例子

http://www.cnblogs.com/ableid/archive/2009/02/28/1400468.html

进程管理的实验性应用例子

大致实现以下的应用例子,供交流与参考:
(1)       进程的控制
修改已编写的程序,将每个进程输出一个字符改为每个进程输出一句话,在观察程序执行时屏幕上出现的现象,并分析原因。
(2)       编制一段程序,使其实现进程的软中断通信。
要求:使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按Del键);当捕捉到中断信号后,父进程调用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:
 Child process 1 is killed by parent!
 Child process 2 is killed by parent!
父进程等待两个子进程终止后,输出如下的信息后终止:
 Parent process is killed!
在上面的程序中增加语句signal(SIGINT, SIG_IGN)和 signal(SIGQUIT, SIG_IGN),观察执行结果,并分析原因。
(3)       进程的管道通信
编制一段程序,实现进程的管道通信。
使用系统调用pipe( )建立一条管道线;两个进程P1和P2分别向管道各写一句话:
Child 1 is sending a message!
Child 2 is sending a message!
而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。
要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。
 
例一
附源码
 1 /*----   t1.c     ----*/
 2 #include <stdlib.h>
 3 #include <stdio.h>
 4 #include <sys/types.h>
 5 #include <unistd.h>
 6  
 7 int main(void)
 8  
 9 {
10  
11        pid_t pid1;
12        pid_t pid2;
13        pid1=fork();    /*这里定义第一个子进程*/
14        pid2=fork();    /*这里定义第二个子进程*/
15        if(pid1<0 || pid2<0)
16               printf("Error.fock call fail\n");
17        //else if(pid1!=0 && pid2!=0)
18               //print("This is the parent process\n");
19        else if(pid1 == 0)           /*当第一个子进程运行时*/
20               printf("This is the NO.1 child process\n");
21        else if(pid2 == 0)           /*当第二个子进程运行时*/
22               printf("This is the NO.2 child process\n");
23        else
24               printf("This is the parent process\n");
25        printf("Fork end.\n\n");
26        exit(0);
27 }
28 /*- END -*/
29 运行结果为:
30 -----------------------------
31 [able@localhost T1]$ ./t1
32 This is the NO.1 child process
33 Fork end.
34  
35 This is the NO.1 child process
36 Fork end.
37  
38 This is the NO.2 child process
39 Fork end.
40  
41 This is the parent process
42 Fork end.
 
---------------------
 
可见,一共有4个进程经历了运行到结束的过程,而其中所谓的子进程1运行了两次。
造成这个的原因是由于在执行到语句   pid1 = fork(); 时 产生了两个进程。分别是 父进程与子进程1 然后两个进程各自执行向下的代码,即 pid2 = fork(); 父进程产生了一个子进程2,而子进程1号也产生了一个子进程2号,由于子进程1产生的两个下级进程其pid 均为"0" 所以在程序中,打印结果,显是子进程1运行了两个。
 
原理如下图:
 
 
                            +--------------+
                            |pid1=fork();|<--PC
                            |pid2=fork();|
                            +--------------+
                            A                                 第一次fork()之前
----------------------------pid1 = fork()-----------------------------
                                                                第一次fork()之后
            +--------------+               +--------------+
            |pid1=fork();|                |pid1=fork();|
            |pid2=fork();|<--PC     |pid2=fork();|<--PC
            +--------------+               +--------------+
              A                                 B            第二次fork()之前
----------------------------pid2 = fork()------------------------------
                                                                第二次fork()之后
+--------------+               +--------------+               +--------------+                 +--------------+
|pid1=fork();|                |pid1=fork();|                |pid1=fork();|                  |pid1=fork();|
|pid2=fork();|                |pid2=fork();|                |pid2=fork();|                  |pid2=fork();|
|if....       |<--PC             |if....               |<--PC     |if....               |<--PC       |if....               |<--PC
+--------------+               +--------------+               +--------------+                 +--------------+
A                                    C                                    B                                     D
 
通过修改代码可以实现.由一个父进程进产两个子进程:
源码如下:
/*----   t1_e.c     ----*/
#include 
<stdlib.h>
#include 
<stdio.h>
#include 
<sys/types.h>
#include 
<unistd.h>
 
int main(void)
{
       pid_t pid1;
       pid_t pid2;
       pid1
=fork();    /*这里定义第一个子进程*/
       
if(pid1<0)
              printf(
"Error.NO.1 fock call fail\n");
       
else if(pid1 == 0)
              printf(
"This is the NO.1 child process\n");
       
else
       {
              pid2
=fork();           /*第二个子进程在判断为父进程后再创建.这样可以避免了在第一进程中再次创建下一级的一个子进程*/
              
if(pid2<0)
                     printf(
"Error.NO.2 fock call fail\n");
              
else if(pid2 == 0)
                     printf(
"This is the NO.2 child process\n");
              
else
                     printf(
"This is the parent process\n");
       }
       printf(
"Fork end.\n\n");
       exit(
0);
}
/*----   END----*/
运行结果为:
-----------------------------
[able@localhostT1]$ ./t1_e
This is the NO.1 child process
Fork end.
 
This is the NO.2 child process
Fork end.
 
This is the parent process
Fork end.
 
例二
附源码:
 
 
/*---           t2.c            ---*/
#include 
<stdio.h>
#include 
<stdlib.h>
#include 
<string.h>
#include 
<signal.h>
#include 
<unistd.h>
#include 
<sys/types.h>
 
int wait_mark;            /*定义等待标记,当其为1时,表示需要等待。*/
void start()
{
         wait_mark
=0;
}
static void waiting()
{
         
while(wait_mark==1);                  /*循环创建进程至成功为止*/
}
 
main()
{
         
int stat1=0,stat2=0;
         
int pid1,pid2;
         
while((pid1=fork())==-1);            /*循环创建进程至成功为止*/
         
if(pid1>0)           /*当判断为父进程时*/
         {
                   printf(
"chilld process 1 is %d\n",pid1);
                   
while((pid2=fork())==-1);
                   
if(pid2>0)
 
                   {
                            printf(
"child process 2 is %d\n",pid2);
                            printf(
"please press 'delete'\n");
                            wait_mark
=1;              /*将等待标记置0至到信号激会start*/
                            
if(signal(SIGINT,start))
                                     ;
                            
else            /*这个判断可以使在没有接受到ctrl+c中断的情况下,自倒数5秒后,发出中断信号,以使程序正常运行。*/
                            {
                                     alarm(
5);
                                     signal(SIGALRM,start);              
/*SIGALRM是在alarm()后激会的软中断信号,如果5秒后没按下ctrl+c则自行激活这个信号*/
                            }
                            waiting();           
/*在wait_mark置0前,不可往下执行*/
                            
/*下面为往两个子进程发送中断信号*/
                            kill(pid1,
16);                
                            kill(pid2,
17);
                            wait(
0);
                            wait(
0);
                            printf(
"Parent process is killed!\n");
                            exit(
0);
                   }
                   
else
                   {
                            signal(SIGINT, SIG_IGN);
                            
//signal(SIGQUIT, SIG_IGN);              /*此处用于实验两个参数之间的差别*/
                            wait_mark=1;
                            
/*等待标记置1,至到收到信号*/
                            signal(
17,start);                   /*当子进程收到父进程经 KILL发送的信号时,等待标记置0*/
                            waiting();
                            printf(
"Child process 2 is killed by parent!\n");
                            exit(
0);
                   }
         }
         
else            /*此子进程的操作与上一个一致*/
         {
                   
//signal(SIGINT, SIG_IGN);                 /*此处用于实验两个参数之间的差别*/
                   signal(SIGQUIT, SIG_IGN);
                   wait_mark
=1;
                   signal(
16,start);
                   waiting();
                   printf(
"Child process 1 is killed by parent!\n");
                   exit(
0);
         }
}
/*---           END          ---*/
运行结果如下:
--------------------------------------
[able@localhost 11]$ ./t2
chilld process 1 is 3867
child process 2 is 3868
please press 'delete'
Child process 2 is killed by parent!
Parent process is killed!
---------------------
结论:该程序可以实现通过接ctrl+c传入中断信息进行I测试,不过未能通过按下“delete”来实现,有待学习解决。
 
SIGINT,SIGQUIT都是中止信号,只是所属定义的按键(或事件)不同而已.SIG_IGN是忽略此信号,也就是此信号不产生我们所需要的动作.还有SID-DFL是系统默认的信号操作.此实验实现了通过父进程发出的软中断传给子进程从而对其进行终止操作。
子进程1使用了signal(SIGQUIT, SIG_IGN);所以在按下ctrl+c退出时,也一并给终止了,所以未能显示kill 信息
子进程2使用了signal(SIGINT, SIG_IGN); 在按下ctrl+c时,进程2未被中终,所以能接收到父进程传来的17号中断信息,从而正常终止,显示kill信息。
 
例三
附源码:
/*---           t3.c            ---*/
#include 
<stdlib.h>
#include 
<stdio.h>
#include 
<sys/types.h>
#include 
<unistd.h>
 
#define MSGSIZE 30
 
char *msg1 = "Child 1 is sending a message!";
char *msg2 = "Child 2 is sending a message!";
 
void waiting(int flag)
{
         
while(flag == 0);
}
 
int main(void)
{
 
         
char inbuf[MSGSIZE];
         
int p[2];
 
         pid_t pid1;                  
/*定义子进程的变量*/
         pid_t pid2;
 
         
/*创建管道*/
         
if(pipe(p)==1)
         {
                   perror(
"pipe call");
                  exit(
1);
         }
         pid1
=fork();       /*这里定义第一个子进程*/
         
if(pid1<0)
                   printf(
"Error.NO.1 fock call fail\n");
         
else if(pid1 == 0)                 /*当第一个子进程运行时*/
         {
                   printf(
"This is the NO.1 child process\n");
                   
/*对子进程关闭读端*/
                   close(p[
0]);
                   printf(
"NO.1 Child process writing msg..\n");
                   write(p[
1],msg1,MSGSIZE);                 /*向管道写信息*/
                   printf(
"NO.1 Child process finish writing..\n");
         }
         
else
         {
                   pid2
=fork();                /*第二个子进程在判断为父进程后再创建.这样可以避免了在第一进程中再次创建下一级的一个子进程*/
                   
if(pid2<0)
                            printf(
"Error.NO.2 fock call fail\n");
                   
else if(pid2 == 0)                 /*当第二个子进程运行时*/
                   {
                            printf(
"This is the NO.2 child process\n");
                            
/*对子进程关闭读端*/
                            close(p[
0]);
                            printf(
"NO.2 Child process writing msg..\n");
                            write(p[
1],msg2,MSGSIZE);
                            printf(
"NO.2 Child process finish writing..\n");
                   }
                   
else
                   {
                            printf(
"This is the parent process\n");
                            
/*对父进程关闭写端*/
                            close(p[
1]);
                            read(p[
0],inbuf,MSGSIZE);
                            printf(
"%s\n",inbuf);
                            read(p[
0],inbuf,MSGSIZE);
                            printf(
"%s\n",inbuf);
                   }
         }
         printf(
"Fork end.\n\n");
         exit(
0);
}
/*---           END          ---*/
其运行结果如下:
------------------------------------------
[able@localhost t3]$ ./t3
This is the NO.1 child process
NO.1 Child process writing msg..
NO.1 Child process finish writing..
Fork end.
 
This is the NO.2 child process
NO.2 Child process writing msg..
NO.2 Child process finish writing..
Fork end.
 
This is the parent process
Child 1 is sending a message!
Child 2 is sending a message!
Fork end.
 
--------------------
从运行结果可见
整个程式的运行顺序为:
 
子进程1建立->子进程1向管道写信息->子进程2建立->子进程2向管道写信息->父进程接收子进程1传递的信息->父进程接收子进徎传递的信息->结束。
 
不过可能地因为系统调度的原因。有时会出现小部分的顺序先后差异,如父进程先接收子进程1的信息后,子进程2再各管道写入信息,不过总体处理顺序不变,父进程依次向12子进程读取信息并输出。

 

 

 

 

fork()不仅创建出与父进程代码相同的子进程,而且父进程在fork执行点的所有上下文场景也被自动复制到子进程中,包括:
—全局和局部变量
—打开的文件句柄
—共享内存、消息等同步对象
由于pipe调用相当于创建了2个文件句柄,因此在fork后这两个句柄也被自动复制到子进程中,对这两个句柄的操作效果与在主进程中的操作效果是一致的,这就使父子进程之间很容易通过该机制实现数据交换,如:
假设pipe产生的句柄为P[0],P[1],在fork后父子进程都拥有了P[0],P[1]句柄,那么:
—父进程可向自己的P[1]中写入数据,子进程从P[0]中即可读出该数据;切记此时父进程不能也从P[0]读数据,否则自己写入的数据可能先被自己读走了
—反之亦然,子进程向P[1]中写入数据后,父进程从P[0]中可读出该数据;切记此时子进程不要从P[0]读走数据
你可能难以理解为什么进程内部的数据读写会被传递到另一个进程,但别忘了,pipe匿名管道和文件,socket等一样是属于操作系统的管理对象,对其进行读写都是由OS的内核代码来进行控制的。在父进程调用pipe创建出管道后,该管道对象是存储在OS内部的,父进程得到的P[0]和P[1]都只是对该对象的引用(相当于指针);在fork出子进程后,子进程复制出的也只是同一个指针,所指向的还是OS中的同一个对象,这就是为什么父子进程能通过其进行互相通信的原因。

posted on 2009-10-24 15:36  水无恨  阅读(967)  评论(0编辑  收藏  举报

导航