欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

操作系统作业:Linux进程的创建与并发

程序员文章站 2022-05-05 09:57:25
...

题目:

一、编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程输出不同的内容。试观察记录屏幕上的显示结果,并分析原因。

二、修改上述程序,每一个进程循环显示一句话。子进程显示'daughter  …'及'son  ……',父进程显示 'parent  ……',观察结果,分析原因。

三、再调用exec( )用新的程序替换该子进程的内容 ,并利用wait( )来控制进程执行顺序。调用Exit()使子进程结束。

四、利用linux的信号量机制实现生产者-消费者问题。(基于进程)

第一题

1、打开终端
右键桌面->打开终端
操作系统作业:Linux进程的创建与并发

2、创建fork.c
(1)在终端输入vim fork.c(没有vim的话请先安装vim,输入 sudo apt-get install vim即可)
(2)在该.c文件里输入下列代码
注意:单击i进入插入状态;:wq保存并退出;:q退出;:q!表示强制退出不保存
操作系统作业:Linux进程的创建与并发

//第一个题目:fork( )创建两个子进程两个子进程,运行观察输出结果 

#include <stdio.h>

#include<unistd.h>

int main( )

{

int p1,p2;

while((p1=fork( ))==-1); //创建子进程 p1

if (p1==0) 

printf("daughter\n");

else

{

while((p2=fork( ))==-1); //创建子进程 p2

if(p2==0)

printf("son\n");

else 

printf("parent\n");

}

}
3、上述程序保存后在终端依次输入gcc fork -o fork  和  ./fork得到结果

操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发
4、分析结果
本来从进程并发执行来看,各种情况都有可能。上面的三个进程没有同步措施,父进程与子进程会同时输出。输出次序随机。

注意:一定要多运行几组,不然结果存在偶然性!上面图1就具有偶然性导致输出结果相同!


第二题

1、在文件夹中找到 fork.c文件并双击进入该文件
2、将原代码修改为:
(可以选择将原代码屏蔽或者删除,我选择了注释源代码)
#include<stdio.h>

#include<unistd.h>

int main( )

{

int p1,p2,i;

while((p1=fork( ))== -1); /*创建子进程 p1*/

if (p1==0)

for(i=0;i<10;i++)

printf("daughter %d\n",i);

else

{

while((p2=fork( ))== -1); /*创建子进程 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);

}

}


3、在终端输入gcc fork.c -o fork  然后输入  ./fork
得到结果如图:

注意:一定要在修改完fork.c文件后在终端输入gcc fork.c -o fork重新编译该文件!完成后在终端输入./fork即可

操作系统作业:Linux进程的创建与并发

操作系统作业:Linux进程的创建与并发
4、结果分析
由于函数fork()创建进程所需的时间多于输出时间且程序运行不存在中断,故字符串内部字符顺序输出不变。但由于进程并发执行的顺序和父子进程抢占处理机的存在,输出字符串的顺序和先后随着执行的不同而发生变化。

第三题

1、在终端输入vim fork.c
2、将终端中的程序替代为下面的代码
#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include <sys/wait.h>

void main( )

{

int pid;

pid=fork( ); //创建子进程 

switch(pid)

{

case -1: //创建失败 

printf("fork fail!\n");

exit(1);

case 0: // 子进程 

execl("/bin/ls","ls","-1","-color",NULL);

printf("exec fail!\n");

exit(1);

default: //父进程 

wait(NULL); //同步 

printf("ls completed !\n");

exit(0);

}

}

3、保存并返回终端,在终端输入
gcc fork.c -o fork  和   ./fork

操作系统作业:Linux进程的创建与并发

操作系统作业:Linux进程的创建与并发


第四题

关于生产者与消费者问题,

操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发

操作系统作业:Linux进程的创建与并发

实现具体步骤:
1、在终端输入vim fork.c

2、将原代码屏蔽,然后输入下列代码:(源自博客:点击打开链接

操作系统作业:Linux进程的创建与并发

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <time.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

#include <sys/types.h>

 

#define MAX_BUFFER_SIZE 10

#define SHM_MODE 0600

#define SEM_MODE 0600

 

#define SEM_FULL 0

#define SEM_EMPTY 1

#define MUTEX 2 

union semun{

	int val;

	struct semid_ds *buf;

	unsigned short *array;

};

#endif

union semun su;//sem union,用于初始化信号量

*/

 

struct my_buffer

{

	int head;

	int tail;

	char str[MAX_BUFFER_SIZE];

	int num;  //缓冲区里字母数量

	int is_empty;

};

 

const int N_CONSUMER = 2;//消费者数量

const int N_PRODUCER = 2;//生产者数量

const int N_BUFFER = 10;//缓冲区容量

const int N_WORKTIME = 10;//工作次数

int shm_id = -1;

int sem_id = -1;

pid_t child;

pid_t parent;

 

//得到10以内的一个随机数  

int get_random()  

{  

    int digit;  

    srand((unsigned)(getpid() + time(NULL)));  

    digit = rand() % 10;  

    return digit;  

}  

 

//得到A~Z的一个随机字母  

char getRandChar()

{

    char letter;  

    srand((unsigned)(getpid() + time(NULL)));  

    letter = (char)((rand() % 26) + 'A');  

    return letter;  

}

 

//sem_id 表示信号量集合的 id

//sem_num 表示要处理的信号量在信号量集合中的索引

//P操作

void waitSem(int sem_id,int sem_num)

{

	struct sembuf sb;

	sb.sem_num = sem_num;

	sb.sem_op = -1;//表示要把信号量减一

	sb.sem_flg = SEM_UNDO;//

	//第二个参数是 sembuf [] 类型的,表示数组

	//第三个参数表示 第二个参数代表的数组的大小

	if(semop(sem_id,&sb,1) < 0){

		perror("waitSem failed");

		exit(1);

	}

}

 

//V操作

void sigSem(int sem_id,int sem_num)

{

	struct sembuf sb;

	sb.sem_num = sem_num;

	sb.sem_op = 1;

	sb.sem_flg = SEM_UNDO;

	//第二个参数是 sembuf [] 类型的,表示数组

	//第三个参数表示 第二个参数代表的数组的大小

	if(semop(sem_id,&sb,1) < 0){

		perror("sigSem failed");

		exit(1);

	}

}

 

//打印进程运行结果

void printTime()

{

	//打印时间

	time_t now;

	struct tm *timenow;         //实例化tm结构指针

	time(&now);

	timenow = localtime(&now);

	printf("执行时间: %s ",asctime(timenow));

}

 

int main(int argc, char ** argv)

{

	shm_id = shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE);   //申请共享内存

	if(shm_id < 0)

	{

		perror("create shared memory failed");

		exit(1);

	}

 

	struct my_buffer *shmptr;  

	shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间

	if (shmptr == (void*)-1)

	{  

        perror("add buffer to using process space failed!\n");  

        exit(1);  

    }  

 

	if((sem_id = semget(IPC_PRIVATE,3,SEM_MODE)) < 0)

	{                  								//创建三个信号量,SEM_EMPTY,SEM_FULL和MUTEX

		perror("create semaphore failed! \n");

		exit(1);

	}

 

	if(semctl(sem_id,SEM_FULL,SETVAL,0) == -1)

	{												//将索引为0的信号量设置为0-->SEM_FULL

		perror("sem set value error! \n");		

		exit(1);

	}

 

	if(semctl(sem_id,SEM_EMPTY,SETVAL,10) == -1)

	{												//将索引为1的信号量设置为10-->SEM_EMPTY

	 	perror("sem set value error! \n");

	 	exit(1);

	}

	if(semctl(sem_id,MUTEX,SETVAL,1) == -1)

	{												//将索引为3的信号量设置为1-->MUTEX

	 	perror("sem set value error! \n");

	 	exit(1);

	}

 

	shmptr -> head = 0;  

    shmptr -> tail = 0;  

    shmptr -> is_empty = 1;  

    shmptr -> num = 0;

 

	for(int i = 0; i < N_PRODUCER; i++)

	{

		parent = fork();

		if(parent < 0)

		{

			perror("the fork failed");

			exit(1);

		}

		else if(parent == 0)

		{

			shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间

			if (shmptr == (void*)-1)

			{  

        		perror("add buffer to using process space failed!\n");  

        		exit(1);  

    		}  

			int count = 0;

			for(int j = 0; j < N_WORKTIME; j++)

			{

				waitSem(sem_id, SEM_EMPTY);

				waitSem(sem_id, MUTEX);

				sleep(get_random()); 

 

				printf("-------------------------------------------------------------\n");

				printf("我是第 %d 个生产者进程,PID = %d\n", i + 1, getpid());

 

				/*生产产品*/

				char c = getRandChar();                                      //随机获取字母

				shmptr -> str[shmptr->tail] = c;

                shmptr -> tail = (shmptr->tail + 1) % MAX_BUFFER_SIZE;  

                shmptr -> is_empty = 0;           //写入新产品  

				shmptr -> num++;

 

				/*打印输出结果*/

				printTime();              //程序运行时间

 

				int p;

				printf("缓冲区数据(%d个):",shmptr -> num);                   //打印缓冲区中的数据

				p = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1 + MAX_BUFFER_SIZE);  

                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  

                {  

                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  

                }  

                printf("\t 生产者 %d  放入 '%c'. \n", i + 1, c);  

				printf("-------------------------------------------------------------\n");

 

				fflush(stdout);

				sigSem(sem_id, MUTEX);

				sigSem(sem_id, SEM_FULL);

			}

			//将共享段与进程之间解除连接  

            shmdt(shmptr);  

            exit(0); 

		} 

	}

 

	for(int i = 0; i < N_CONSUMER; i++)

	{

		child = fork();

		if(child < 0)//调用fork失败

		{

			perror("the fork failed");

			exit(1);

		}

		else if(child == 0)

		{

			int count = 0; 

			shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间

			if (shmptr == (void*)-1)

			{  

        		perror("add buffer to using process space failed!\n");  

        		exit(1);  

    		} 

			for(int j = 0; j < N_WORKTIME; j++)

			{

 

				waitSem(sem_id, SEM_FULL);

				waitSem(sem_id, MUTEX);

				sleep(get_random()); 

 

				printf("-------------------------------------------------------------\n");

				printf("我是第 %d 个消费者进程,PID = %d\n", i + 1, getpid());

				/*消费数据*/

				char lt = shmptr -> str[shmptr -> head];  

                shmptr -> head = (shmptr -> head + 1) % MAX_BUFFER_SIZE;  

                shmptr -> is_empty = (shmptr->head == shmptr->tail);  //

				shmptr -> num--;

				/*打印输出结果*/

				printTime(); //程序运行时间

 

				int p;

				printf("缓冲区数据(%d个):",shmptr -> num);                   //打印缓冲区中的数据

				p = (shmptr -> tail - 1 >= shmptr -> head) ? (shmptr -> tail-1) : (shmptr -> tail - 1 + MAX_BUFFER_SIZE);  

                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  

                {  

                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  

                }  

                printf("\t 消费者 %d  取出 '%c'. \n", i + 1, lt);  

				printf("-------------------------------------------------------------\n");

 

				fflush(stdout);

				sigSem(sem_id,MUTEX);

				sigSem(sem_id,SEM_EMPTY);

			}

			//将共享段与进程之间解除连接  

        	shmdt(shmptr);  

        	exit(0);

		}  

	}

 

	    //主进程最后退出  

    while (wait(0) != -1);  

    //将共享段与进程之间解除连接  

    shmdt(shmptr);  

    //对共享内存区执行控制操作  

    shmctl(shm_id,IPC_RMID,0);//当cmd为IPC_RMID时,删除该共享段  

    shmctl(sem_id,IPC_RMID,0);  

    printf("主进程运行结束!\n");  

    fflush(stdout);  

    exit(0); 

	return 0;

}

3、保存上述代码返回终端后在终端依次输入

gcc fork.c -o fork  和  ./fork
得到输出结果:
操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发
操作系统作业:Linux进程的创建与并发

操作系统作业:Linux进程的创建与并发

以上程序运行结束!
感受:以上就是本次作业的完成,有了第一次系统调用(给Linux系统新增一个系统调用)的基础,这次对虚拟机的操作变得简单了不少,知道了自己需要什么,自己在卡顿的地方怎么去查询相关资料。操作系统的课程就这样结束了,但是在操作系统的这几次课后作业里我收获颇多,在锻炼自己动手能力的同时还学习到了操作系统的重要知识!给未来的自己立了一个 不错的榜样。后面继续努力吧!