计算机系统结构与操作系统-实验一--进程管理与进程通信

实验目的

1、熟悉LINUX的常用基本命令,学会编译、调试C程序。
2、认识进程的创建、控制、互斥及守护进程的建立。
3、了解和熟悉LINUX支持的信号量机制、管道机制、消息通信机制及共享存储区机制。

实验内容

1.进程的创建实验

1-1. 调用fork()创建两个子进程,父进程显示’a’,子进程分别显示字符’b’和字符’c’。观察结果,分析原因。

#include <stdio.h>
#include <unistd.h> // fork
int main()
{
	int p1,p2;
	while((p1=fork())== -1);        /*create sonProcess-p1*/
	if (p1==0){            /*current sonProcess is p1*/
		putchar('b');
	} else { 
		while((p2=fork())== -1);   /*create sonProcess-p1*/
		if(p2==0)  putchar('c');   /*current sonProcess is p2*/
		else  putchar('a');     
	}
}

在这里插入图片描述
分析:
例如:abc / acb,父进程创建子进程后,子进程并未立即执行完,父进程比两个子进程先行执行完,输出a,紧接两个子进程输出b、c或c、b;
例如:bac / bca,父进程创建子进程b后,子进程b优先执行完,先输出b,c而后父进程和子进程c执行结束后a、c或c、a;
总之,输出结果不确定,取决于进程的调度顺序和时间片分配方式。

1-2. 修改上述程序,子进程显示’daughter …‘及’son ……’,父进程显示 ‘parent ……’,观察结果,分析原因。

#include <stdio.h>
#include <unistd.h> // fork
int main(){
	int p1,p2,i;
	while((p1=fork())== -1);         /*create sonProcess-p1*/
	if (p1==0)
	  	for(i=0;i<10;i++)
		printf("daughter  %d\n",i);
	else
	{
                while((p2=fork())== -1);    /*create sonProcess-p2*/
		if(p2==0)
                  for(i=0;i<10;i++)
                       printf("son  %d\n",i);
		else
                  for(i=0;i<10;i++)
                       printf("parent  %d\n",i);
	}
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述
分析:
同第一问类似,可以看到结果非常随机,也很好地证明了进程的并发性。另外值得注意的是,函数printf( )在输出字符串时不会被中断,因此,字符串内部字符顺序输出不变。

2.进程的控制实验

用fork( )创建一个进程,再调用execl( )用新的程序替换该子进程的内容,并利用wait( )来控制进程执行顺序

#include<stdio.h>
#include<stdlib.h> /*exit()*/
#include<unistd.h>  /*fork()*/
#include<sys/wait.h> /*wait()*/
int main(){
	int pid;
	pid=fork();
	switch(pid){
		case -1:	/*create fail*/
			printf("fork fail\n");
			exit(1);
		case 0:	/*son*/
			execl("/bin/ls","ls","-l","-color",NULL);
			printf("exec fail\n");
			exit(1);
		default:	/*parent*/
			wait(NULL);
			printf("ls completed!\n");
			exit(0);
	}
}

3.进程的互斥实验

利用lockf()实现进程间的互斥
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.守护进程实验

利用管理员权限创建日志文件/var/log/Mydaemon.log,每分钟向其写入时间戳。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
int main(){
	FILE *fp;                   //建立文件指针
	fp = fopen("/var/log/Mydaemon.log","a");
	setlinebuf(fp);             //设置行缓冲以确保内容立即写入文件

	pid_t pid;                  //定义守护进程
	pid = fork();

	// 如果fork返回值大于0,表示当前进程是父进程,此时需要退出程序
	if (pid > 0) {
	    printf("Daemon on duty!\n");
	    exit(0);
	}
	// 如果fork返回值小于0,表示出现错误,此时需要退出程序
	else if (pid < 0) {
	    printf("Can't fork!\n");
	    exit(-1);
	}

	// 当前进程为子进程,一直循环执行,每隔10秒向日志文件中写入当前时间戳
	while (1) {
	    if (fp >= 0) {
		sleep(10);            
		printf("Daemon on duty!  ");
		time_t t;                   //建立time_t格式变量
		t = time(NULL);
		printf("The current time is %s\n",ctime(&t));
	    }
	}
	fclose(fp);
}

在这里插入图片描述

5.信号机制实验

编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:
Child process1 is killed by parent!
Child process2 is killed by parent!
父进程等待两个子进程终止后,输出如下的信息后终止:
Parent process is killed!
分析利用软中断通信实现进程同步的机理。

# include<stdio.h>
# include<signal.h>
# include<unistd.h>
# include<sys/wait.h>
#include<stdlib.h>
int wait_mark;
void waiting(){
   while(wait_mark!=0);
}
void stop(){
   wait_mark=0;
}
int main(){ 
	  int  p1, p2;
	  signal(SIGINT,stop); //signal()初始位置
	  while((p1=fork())==-1);
	  if(p1>0){  
	    while((p2=fork())==-1);
	    if(p2>0){ 
	      wait_mark=1;
	      waiting( );      
	      wait(0);
	      wait(0);
	      printf("parent process is killed!\n");
	      exit(0);
	    }else {
		wait_mark=1;
		waiting( );
		lockf(1,1,0);
		printf("child  process 2 is killed by parent!\n");
		lockf(1,0,0);
		exit(0);
	      }
	  } else{
	    wait_mark=1;    
	    waiting( );
	    lockf(1,1,0);
	    printf("child  process 1 is killed by parent!\n");
	    lockf(1,0,0);
	    exit(0);
	  }
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/b264f815ae1c4ea69adeb2f63a452b58.png)
分析:
wait_mark 变量用于实现进程间的同步。当 wait_mark 为 0 时,父进程将继续运行,否则它会阻塞并等待子进程完成。因此,在父进程中,它将 wait_mark 设置为 1 ,然后在 waiting() 函数中轮询变量 wait_mark 的值,直到 wait_mark 变为 0 。在子进程中,当它们完成任务后,它们将 wait_mark 设置为 0 ,从而使父进程结束等待,并继续执行下一步操作,从而实现进程同步。
## 6.进程的管道通信实验
**编写程序实现进程的管道通信。用系统调用pipe()建立一管道,二个子进程P1和P2分别向管道各写一句话:
Child 1 is sending a message!
Child 2 is sending a message!
父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。**
```c++
# include<unistd.h>
# include<signal.h>
# include<stdio.h>
# include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>

int pid1,pid2; 
int main(){
	int fd[2];   //打开文件的文件描述符
  	char OutPipe[100],InPipe[100]; //存储要写入管道的字符串
  	pipe(fd);//创建无名管道
  	while((pid1=fork())==-1);  //用fork函数创建子进程
	if(pid1==0){       //子进程1号
		lockf(fd[1],1,0); 
		sprintf(OutPipe,"Child process 1 is sending a message!"); //存储字符串
		write(fd[1],OutPipe,50);//将字符串写入管道
		sleep(5);              
		lockf(fd[1],0,0);  
		exit(0); 
	}else{
		while((pid2=fork())==-1);   //子进程2号
		if(pid2==0){  
			lockf(fd[1],1,0);
			sprintf(OutPipe,"Child process 2 is sending a message!");
			write(fd[1],OutPipe,50);
			sleep(5);
			lockf(fd[1],0,0);
			exit(0);
		}else{                   //父进程读
			read(fd[0],InPipe,50);  //将字符串1从管道中读出到inpipe
			printf("%s\n",InPipe);  //打印inpipe内数据
			wait(0);              
			read(fd[0],InPipe,50);  //将字符串2从管道中读出到inpipe
			printf("%s\n",InPipe);
			exit(0);
		}
	}
return 0;
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/c0db777648f94c9898f06be20f413bb7.png)
## 7.消息的接收与发送实验
**消息的创建、发送和接收。使用系统调用msgget( ),msgsnd( ),msgrev( ),及msgctl( )编制一长度为1k的消息发送和接收的程序。**
```c++
#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75          /*定义关键词MEGKEY*/
struct msgform                /*消息结构*/
{
    long mtype;
    char mtexe[1030];         /*文本长度*/
}msg;
int msgqid,i;
 
void client(){
    int i;
    msgqid=msgget(MSGKEY,0777);  //创建消息队列
    for(i=10;i>=1;i--)
    {
         msg.mtype=i;
         printf("(client)sent\n");
         msgsnd(msgqid,&msg,1024,0);  //发送消息msg入msgid消息队列
    }
    exit(0);
}
void server(){ 
  msgqid=msgget(MSGKEY,0777|IPC_CREAT); 
  do
  {
      msgrcv(msgqid,&msg,1030,0,0);  //从队列msgid接受消息msg
      printf("(server)receive\n");
  }while(msg.mtype!=1);             //消息类型为1时,释放队列
  msgctl(msgqid, IPC_RMID,0);   //消除消息队列的标识符
  exit(0);
}
int main(){
  if(fork()) server (); //父进程    
else client ();  //子进程
  wait(0);
  wait(0);
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/e55be47f4ff64ad296a59fcdd7ae498a.png)
## 8.共享存储区实验
**编制一长度为1k的共享存储区发送和接收的程序。**
````c++
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<wait.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include <sys/shm.h>
#define SHMKEY  75                   /*定义共享区关键词*/
int shmid,i;
int *addr;
 
void client(){    
    int i;    
    shmid=shmget(SHMKEY,1024,0777);    /*获取共享区,长1024,关键词SHMKEY*/    
    addr=shmat(shmid,0,0);                /*共享区起始地址为addr*/    
    for(i=9;i>=0;i--)     
    {        
        while(*addr!= -1);                          
        printf("(client)sent\n");                 /*打印(client)sent*/        
        *addr=i;                             /*把i赋给addr*/    
    }    
    exit(0);
}
void server(){     
    shmid=shmget(SHMKEY,1024,0777|IPC_CREAT);  /*创建共享区*/    
    addr=shmat(shmid,0,0);                         /*共享区起始地址为addr*/    
    do    
    {    
        *addr=-1;    
        while(*addr==-1);    
        printf("(server)received\n");               /*服务进程使用共享区*/    
    }
    while(*addr);    
    shmctl(shmid,IPC_RMID,0);    
    exit(0);
}
int main(){    
    if(fork()) server();    
    if(fork()) client ();    
    wait(0);    
    wait(0);
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/86cc21ba138d42be9c8d626e01eabd39.png)
# 实验分析与思考
## (一)进程的创建实验
**1-1.系统是怎么样创建进程的**
进程执行fork函数后,初始化新PCB,同时给新进程分配资源(分配存储数据和代码的内存空间),保存当前进程执行的上下文信息,并复制到新进程中,当所有准备工作完成后,操作系统会将控制权转移到新的进程,以便执行新进程。

**1-2.当首次调用新创建进程时,其入口在哪里?**
fork()调用一次,但返回两次,两次返回区别在于:子程序返回值是0,父进程返回值是子进程的ID。调用成功后,父子进程并发执行相同的代码,因此新进程是从fork()后的语句开始执行(也即新进程调用的入口)。
## (二)进程的控制实验
**2-1. 编写一hello程序,实现输出“hello,姓名”。将hello替代上述的execl 中运行的ls命令。**
![在这里插入图片描述](https://img-blog.csdnimg.cn/e1c32a3aef1445a98c04ea8b04b61a07.png)
```c++
#include<stdio.h>
#include<stdlib.h> /*exit()*/
#include<unistd.h>  /*fork()*/
#include<sys/wait.h> /*wait()*/
int main(){
	int pid;
	pid=fork();
	switch(pid){
		case -1:	/*create fail*/
			printf("fork fail\n");
			exit(1);
		case 0:	/*son*/
			execl("/home/lhj/osTest1/hello.out","hello",NULL);
			printf("exec fail\n");
			exit(1);
		default:	/*parent*/
			wait(NULL);
			printf("ls completed!\n");
			exit(0);
	}
}
```
**2-2.什么是进程同步?wait( )是如何实现进程同步的?**
进程同步:保证多个进程在执行过程中相互协调和同步,具体来说,在并发执行的多个进程中,如果一个进程的结果会影响到另一个进程,那么需要保证先执行完一个进程,得到相应结果,再执行另一个进程,保证一定的先后执行顺序。
wait()可以让父进程等待子进程执行完毕后再继续执行。wait()挂起父进程后,直到它的某个子进程结束了执行或收到了某个信号,才会返回进程的pid并且释放子进程占用的系统资源。
## (四)守护进程实验
**4-1.语句“ setlinebuf(fp); //设置行缓冲”起到什么作用?如果没有该语句,程序的执行结果会怎样?**
每次输出完一行数据(即在输入包含'\n'的字符后),就会刷新缓冲区,将缓冲区中的数据写入到要输出的文件中;而不是等到缓冲区满了或者程序结束时再将数据写入文件。如果不写该语句,数据不能及时写入文件中。
## (五)信号机制实验
**5-1.如何修改程序才能得到以下结果**
Child process1 is killed by parent!
Child process2 is killed by parent!
Parent process is killed!

把signal()函数放在程序前面部分执行,让父进程捕捉由键盘发来的中断信号,当系统捕捉到中断信号后,调用预置的stop函数,子进程捕捉到信号后,输出信息并终止,父进程等待两个子进程结束后,也输出信息并终止。

![在这里插入图片描述](https://img-blog.csdnimg.cn/73b340b615f44e51a32f75c9a0878fd3.png)
**5-2.该程序段前面部分用了两个wait(0),它们起什么作用?**
		确保父进程等待2个子进程都结束后再执行。
## (六)进程的管道通信实验
**6-1. 程序中的sleep(5)起什么作用?**
休眠5s,防止写冲突

**6-2. 怎样保证先child1进程,再child2进程?**
对于创建两个子进程的顺序,父进程通过两个while循环,保证先创建子进程1,后创建子进程2。在子进程进行写入操作时,加入锁机制保证互斥控制,具体来说,子进程child1进行写入操作时,先给管道上锁( lockf(fd[1],1,0) ),执行完写入操作再解锁( lockf(fd[1],1,0) ),确保当前进程操作完成后再释放管道资源。接着再轮到child2进行写入操作,从而保证两个子进程的写顺序。

**6-3. 子进程1和2为什么也能对管道进行操作?**
Fork()创建两个子进程,子进程会继承父进程所创建的管道。






1.基本系统进程   Csrss.exe:这是子系统服务器进程,负责控制Windows创建或删除线程以及16位的虚拟DOS环境。   System Idle Process:这个进程是作为单线程运行在每个处理器上,并在系统不处理其它线程的时候分派处理器的时间。   Smss.exe:这是一个会话管理子系统,负责启动用户会话。   Services.exe:系统服务的管理工具。   Lsass.exe:本地的安全授权服务。   Explorer.exe:资源管理器。   Spoolsv.exe:管理缓冲区中的打印和传真作业。   Svchost.exe:这个进程要着重说明一下,有不少朋友都有这种错觉:若是在“任务管理器”中看到多个Svchost.exe在运行,就觉得是有病毒了。其实并不一定,系统启动的时候,Svchost.exe将检查注册表中的位置来创建需要加载的服务列表,如果多个Svchost.exe同时运行,则表明当前有多组服务处于活动状态;多个DLL文件正在调用它。   至于其它一些附加进程,大多为系统服务,是可以酌情结束运行的。由于其数量众多,我们在此也不便于一一列举。   在系统资源紧张的情况下,我们可以选择结束一些附加进程,以增加资源,起到优化系统的作用。在排除基本系统及附加进程后,新增的陌生进程就值得被大家怀疑了。 更多内容请看Windows操作系统安装、系统优化大全、系统安全设置专题,或进入讨论组讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值