操作系统实验导航
实验一:银行家算法 https://blog.csdn.net/weixin_46291251/article/details/115384510
实验二:多级队列调度和多级反馈队列调度算法 https://blog.csdn.net/weixin_46291251/article/details/115530582
实验三:动态分区式内存管理 https://blog.csdn.net/weixin_46291251/article/details/115772341
实验四:Linux下多进程通信 https://blog.csdn.net/weixin_46291251/article/details/116274665
实验五:进程通信的三种方式 https://blog.csdn.net/weixin_46291251/article/details/116301250
实验六:Linux文件系统实验 https://blog.csdn.net/weixin_46291251/article/details/116423798
实验七:自制简单U盘引导程序 https://blog.csdn.net/weixin_46291251/article/details/116427629
实验八:磁盘调度算法 https://blog.csdn.net/weixin_46291251/article/details/116431907
实验九:请求分页系统中的置换算法 https://blog.csdn.net/weixin_46291251/article/details/116443021
学习笔记:操作系统复习笔记 https://blog.csdn.net/weixin_46291251/article/details/117086851
题目描述:
- 父进程使用系统调用pipe()建立一个管道,然后使用系统调用fork()创建两个子进程:子进程1和子进程2
- 子进程1每隔1秒通过管道向子进程2发送数据:I send message x times.(x初值为1,以后发送一次后做加一操作)
- 子进程2从管道读出信息,并显示在屏幕上
- 父进程用系统调用signal()来捕捉来自键盘的中断信号SIGINT(即按Ctrl+C键,);当捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,
- 子进程捕捉到信号后分别输出如下信息后终止:
Child Process 1 is killed by Parent!
Child Process 2 is killed by Parent!
- 父进程等待两个子进程终止后,释放管道并输出如下的信息后终止
Parent Process is Killed!
算法设计:
多进程:
这里主要是利用系统调用fork:
- fork是Linux下创建进程的一个系统调用
调用fork的进程为主进程,一次调用会产生一个子进程。
- fork的特点:一次调用两次返回:
主进程和子进程的差异就从fork这条语句开始,fork给调用他的主进程的返回值是子进程的PID (若成功),给子进程的返回值是0,故可由此判断当前进程是子进程还是父进程,如:
int pid = fork();
if(pid==0){
//说明是子进程,这里写子进程的相关操作
}
else{
//说明是父进程,这里写父进程的相关操作
}
如何创建多个子进程?
错误的方法:
for(int i=0;i<10:i++)
fork();
这显然不正确,因为不仅有父进程可以执行fork()语句,子进程也能执行,这就会导致实际创建的进程比预期的要多,因为有类似子进程的子进程存在。
避免这个问题也很简单,只在父进程执行fork()就行了,区分父子进程的方法上面已经给出。
我的具体实现代码:
int childpid[10];//用来保存所有进程的PID,0号是主进程
childpid[0]=getpid();//012分别是父进程和两个子进程的pid
for(int i=1;i<=2;i++){
int pid=fork();
if(pid!=0)
childpid[i] =pid;
else{
childpid[i] =getpid();
break;
}
}
最终,主程序的逻辑大概如下:
if(getpid()==childpid[0]){
}
if(getpid()==childpid[1]){
}
if(getpid()==childpid[2]){
}
信号机制:
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
使用signal()函数处理时,只需指出要处理的信号和处理函数即可。
signal()原型:
/*signal-handler*/* signal(int sig, /*signal-handler*/* handler);
- 参数:
sig:要设置信号处理函数的信号。它可以是实现定义值或预定义的宏(见下方):
handler:信号处理函数。这必须是下列之一:
SIG_DFL 宏。信号处理函数被设为默认信号处理函数。
SIG_IGN 宏。忽略信号。
指向函数指针。
- 返回值
成功时为先前的信号处理函数,失败时为 SIG_ERR (某些实现上能禁用设置信号处理函数)。
- 信号处理函数的预定义宏
SIGINT:ctrl+c 终止信号
SIGQUIT:ctrl+\ 终止信号
SIGTSTP:ctrl+z 暂停信号
SIGALRM:闹钟信号 收到此信号后定时结束,结束进程
SIGCHLD:子进程状态改变,父进程收到信号
SIGKILL:杀死信号
int kill(pid_t pid, int sig);
功能:发送信号
参数:pid:指定进程
sig:要发送的信号
返回值:成功 0;失败 -1
这里需要用到几个函数:
程序功能的具体实现:
signal(SIGINT,fun_pare);//发出中断信号
void fun_pare(int sig)//父进程响应Ctrl C 信号函数
{
kill(childpid[1],SIGUSR1);
kill(childpid[2],SIGUSR2);
}
signal(SIGUSR1,fun_chid);
signal(SIGUSR2,fun_chid);
注意在子进程中应该屏蔽Ctrl+C信号,否则,在父进程响应Ctrl+C时,子进程就会调用默认的响应函数而退出了:
signal(SIGINT,SIG_IGN);
void fun_chid(int sig)//子进程1响应信号函数
{
if(sig==SIGUSR1)
printf("\nChild Process 1 is killed by parent!\n");
if(sig==SIGUSR2)
printf("\nChild Process 2 is killed by parent!\n");
exit(822);//默认正常退出返回0
}
这里利用响应函数的参数对接收到的函数进行判断,从而将两个进程的响应放在一块实现。
管道通信:
-
pipe函数的原型:
int pipe (int fd[2]); //成功返回0,出错返回-1
fd参数是数组/指针,可以带回两个值(文件描述符),
fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
-
pipe通信(父子进程间)的流程:
1.父进程创建管道,得到两个件描述符指向管道的两端
2.发进程关闭fd[1](读端),利用write()向管道写入
3.收进程关闭fd[0](写端),利用read()从管道读出
-
我这里的实现:
int flg,fd[2];
flg = pipe(fd); //创建管道
if (flg == -1)
perror("pipe error");
int *child,*msg,len;
write(fd[1],child,len);//表示向fd[1]写入child字符串的前len位
read(fd[0],msg,sizeof(msg))//表示从fd[0]读出一个字符串
整体实现代码:
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int childpid[10]={-1,-1,-1,-1},status=999;
void fun_pare(int sig)//父进程响应Ctrl C 信号函数
{
kill(childpid[1],SIGUSR1);
kill(childpid[2],SIGUSR2);
}
void fun_chid(int sig)//子进程1响应信号函数
{
if(sig==SIGUSR1)
printf("\nChild Process 1 is killed by parent!\n");
if(sig==SIGUSR2)
printf("\nChild Process 2 is killed by parent!\n");
exit(822);//默认正常退出返回0
}
int main(){
int ret,fd[2];
ret = pipe(fd); //创建管道
if (ret == -1)
perror("pipe error");
childpid[0]=getpid();//012分别是父进程和两个子进程的pid
for(int i=1;i<=2;i++){
int pid=fork();
if(pid!=0)
childpid[i] =pid;
else{
childpid[i] =getpid();
break;
}
}
if(getpid()==childpid[0]){//父进程
printf("我是父进程,PID:%d PPID:%d\n",getpid(),getppid());
printf("\t子进程1是:PID:%d\n",childpid[1]);
printf("\t子进程2是:PID:%d\n",childpid[2]);
signal(SIGINT,fun_pare);//发出中断信号
int child_num = 2;//子进程个数
while(child_num --){
int t = wait(&status);//等待两个子进程结束
printf("一个进程结束了,他的 PID:%d 返回码 :%d\n",t,status);
}
printf("Parent process is killed!\n");//两个子进程都结束后主进程才可以结束
exit(822);//默认正常退出返回0
}
else{//否则是子进程
signal(SIGINT,SIG_IGN);
signal(SIGUSR1,fun_chid);
signal(SIGUSR2,fun_chid);
}
if(getpid()==childpid[1]){//子进程1
int i=0;
close(fd[0]);
char child[100]="I send message x times !\0";
while(++i)
{
child[15] = '0' + i;
write(fd[1],child,strlen(child)+1);
sleep(1);
}
}
if(getpid()==childpid[2]){//子进程2
close(fd[1]);
char msg[100];
while(1)
{
if(read(fd[0],msg,sizeof(msg)))
printf("%s\n",msg);
}
}
return 0;
}
运行截图: