实验三 进程的通信
&符号
Linux 终端命令的末尾加上一个 &
符号表示将这个任务放到后台去执行
PS命令
ps -c显示进程的实际名称
kill
调用格式:kill(pid,sig)
说明:pid表示一个或一组进程标示符,pid值的定义如下:
>0:将信号发送给指定的进程
=0:将信号发送给同组的所有进程
=-1:将信号发送给所有满足如下条件的进程:进程的用户标示符等于发送信号进程的有效用户标示符
Sig为软中断信号(序号或名称)
wait()
当进程调用 wait() 时,wait()会暂停目前进程的执行(即阻塞),由 wait() 来自动分析是否当前进程的某个子进程已经退出,如果找到了这样一个已经变成僵尸进程的子进程,wait 就会收集这个子进程的信息,并将其彻底销毁后返回;如果没有找到这样一个子进程,wait 就会一直阻塞在这里,直到出现僵尸进程。
signal()
signal()函数对捕捉到的信号进行处理,其中函数的第一个参数表示捕捉到的信号,第二个参数函数指针SIG_DFL,它表示默认信号处理程序,如果是SIG_IGN,则表示忽略信号的处理程序。当用户按下第一个Ctrl+C 组合键时,将调用int_func函数,此时信号处理动作将恢复为默认动作,当程序接收到第二个SIGINT信号时,程序采取默认动作,结束运行。
pid=fork()
负值:创建子进程失败。
零:说明是新创建的子进程。
正值:说明是父进程,并且该值就是新创建的子进程的进程ID
信号号码 9 SIGKILL 含义是用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
信号号码 18 SIGCONT,含义是让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
信号号码 19 SIGSTOP,含义是停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
信号号码 12 SIGUSR2,是用户自定义信号。
代码解释
#include<signal.h>
int k1;
void myfunction()
{
k1=0;
printf("child got a signal from father!\n");
}
main()
{
int k,p1;
while((p1=fork())==-1);
if(p1>0)
{
for(k=1;k<3;k++)
{
printf("i am father!\n");
sleep(1);
}
kill(p1,12);
wait(0);
printf("my child have exited and I am father !\n");
exit(0);
}
else
{
signal(12,myfunction);
k1=1;
while(k1==1)
{
printf("I am a child\n");
sleep(1);
}
printf("Child exited!\n");
exit(0);
}
}
- 整个程序的执行次序如下
- 首先p1=fork()创建了父进程和子进程
- 两个进程依次进入线程队列
- 首先是父进程的执行
- 执行for循环时打印father,然后休眠1s
- 这里的sleep函数代表挂起父进程这时候让子进程进入运行
- 于是便有while循环里的子进程运行打印child,然后休眠挂起子进程
- 继续运行父进程 此时k=2
- 重复上述内容后,我们来到kill(p1,12)这个命令
- 软中断 将该12信号发送给p1,子进程于是接收到信号
- 接着wait()挂起父进程,运行子进程直到子进程的结束
- signal接收到信号于是运行myfunction这个函数
- 此时可以跳过while循环输出后面的内容了
- 退出子进程 完成父进程的运行
通道的基本使用
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#define MAX_LINE 80
int main(){
int thePipe[2], ret;
char buf[MAX_LINE+1];
const char *testbuf="a test string.";
if ( pipe( thePipe ) == 0 ) {
if (fork() == 0) {
ret = read( thePipe[0], buf, MAX_LINE );
buf[ret] = 0;
printf( "Child read %s\n", buf );
} else {
ret = write( thePipe[1], testbuf, strlen(testbuf) );
ret = wait( NULL );
}
}
close(thePipe[0] );
close(thePipe[1] );
return 0;
}
- 无名通道Pipe可以实现父子进程之间的信息流动
无名管道
无名管道(也称为匿名管道)的创建
系统调用:int pipe(int PipeName[2]);
参数说明:
PipeName[2],管道名,为一2元整型数组
PipeName[0]专用于读进程
PipeName[1]专用于写进程
返回值:整型。0代表管道创建正确;-1代表管道创建失败。
该管道由创建进程及其子孙进程共享
使用头文件:unistd.h
-
管道小程序
-
编写程序pipeuse.c,涉及到4个进程和3个管道,需要完成如下功能:
P1:从键盘接受字符串,写入管道pipe1。
P2:从pipe1接受字符串,若字符串的长度为小于10,将其写入管道pipe 2,否则写入管道pipe3
P3:从pipe2读出字符串,并且显示”I am p3, I read from pipe2, this is short string.”
P4:从pipe3读出字符串,并且显示” I am p4, I read from pipe3, this is long string."
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#define MAX_LINE 80
int main()
{
int thePipe[2], ret;
char buf[MAX_LINE + 1];
const char *testbuf = "a test string.";
int pid = 0;
if (pipe(thePipe) == 0)
{
printf("s1 %d\n", pid);
if ((pid = fork()) == 0)
{
printf("s2 %d\n", pid);
ret = read(thePipe[0], buf, MAX_LINE);
buf[ret] = 0;
printf("Child read %s\n", buf);
}
else
{
ret = write(thePipe[1], testbuf, strlen(testbuf));
close(thePipe[0]);
close(thePipe[1]);
printf("s3 %d\n", pid);
}
printf("s4 %d\n", pid);
}
printf("s5 %d\n", pid);
return 0;
}
- 本次实验通过打印调试,得出了如下关于父子进程调度的结论
- 首先父子进程的调度是由操作系统支配并且是异步的
- 可以是父子进程交叉打印的结果
- 其次 使用wait(0)函数可以使父进程在wait后的内容等待子进程运行结束后再运行