大致实现以下的应用例子,供交流与参考:
(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.
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---- */
运行结果为:
#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 --- */
运行结果如下:
#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 --- */
#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
再各管道写入信息,不过总体处理顺序不变,父进程依次向
1
,
2
子进程读取信息并输出。