操作系统概念 实验二-进程控制

本文介绍了三种进程间通信方法:通过fork()创建子进程并直接输出数据、利用共享内存交换序列数据以及通过管道通信改变字符串大小写。每个部分都提供了详细的代码示例。

年轻人要讲抄德,抄袭作业又蠢又坏,大家耗子尾汁


Collatz 猜想:任意写出一个正整数 N,并且按照以下的规律进行变换:
如果是个奇数,则下一步变成 3N+1;如果是个偶数,则下一步变成 N/2。
无论 N 是怎样的一个数字,最终都无法逃脱回到谷底 1。
例如:如果 N=35,则有序列 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1。

实验内容一、

要求

采用系统调用 fork(),编写一个 C 程序,以便在子进程中生成这个序列。
要求:
(1)从命令行提供启动数字
(2)由子进程输出数字序列
(3)父进程等子进程结束后再退出。

解决方案

在Linux下方便地创建子进程并进行简单的打印输出:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc,char* argv[]){
	int N=0;
	//如果在命令行启动程序时给定了初始的参数N
	if(argv[1]!='\0'){
		N = atoi(argv[1]);
	}
	//创建子进程
	pid_t fpid;
	fpid=fork();
	if(fpid<0){
		printf("creating Child process error!");
	}
	else if(fpid==0){
		printf("I am the child process, my pid is %d\n", getpid());
		printf("The Seq is: %d ",N);
		while(N!=1){
			if(N%2==0){
				N=N/2;
				printf("%d ",N);
			}
			else{
				N=3*N+1;
				printf("%d ",N);
			}
		}
		printf("\n");
	}
	else{
	//父进程等待子进程结束
		wait(NULL);
		printf("I am the parent process, my pid is %d\n", getpid());
		
	}
	return 0;
}

运行结果

实验内容二、

要求

以共享内存技术编程实现 Collatz 猜想。
要求在父子进程之间建立一个共享内存对象,允许子进程将序列内容写入共
享内存对象,当子进程完成时,父进程输出序列。
父进程包括如下步骤:
建立共享内存对象(shm_open(), ftruncate(), mmap())
建立子进程并等待他终止
输出共享内存的内容
删除共享内存对象。

解决方案

1. 共享内存的概念
在这里插入图片描述
进程之间的通信有两种方式:消息传递和共享内存。消息传递就是利用操作系统内核的一些函数比如send()、receive()来像网络通信一样进行数据交流;共享内存则是专门开辟一块空间,两个进程都可以访问、改写这里面的数据,这样就实现了数据的交流。

2. 共享内存调用的函数
LINUX共享内存的POSIX接口是shm_open这个函数。其实这个接口就是在/dev/shm目录创建一个文件,然后使用这个文件来实现共享内存的功能。
int shm_open(const char *name, int oflag, mode_t mode);创建或打开一个共享内存,成功返回一个整数的文件描述符,错误返回-1。
三个参数的意义:
1.name:共享内存区的名字;
2.标志位:open的标志一样
3.权限位
ftruncate(shm_fd, SIZE);已知共享内存shm_fd,调整这一块空间大小为SIZE

编译时要加库文件 -lrt

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <wait.h>
int main(int argc,char* argv[]){
	const int SIZE=4096;
	const char *name = "OS";
	int N=0;
	//如果在命令行启动程序时就有初始参数
	if(argv[1]!='\0'){
		N = atoi(argv[1]);
	}
	//创建子进程
	pid_t fpid;
	fpid=fork();
	if(fpid<0){
		printf("error!");
	}
	else if(fpid==0){
		printf("I\'m the child process %d\nDo Seq of: %d\n",getpid(),N);
		char *message;
		void *ptr1;
		//父进程一直在等待,所以创建共享内存空间由子进程完成
		//共享内存的name随便写一个名字就行
		int shm_fd1 = shm_open(name, O_CREAT|O_RDWR, 0666);
		if(shm_fd1 < 0){
			printf("Open the shm1 failed!\n");
			return -1;
		}
		else{
			char current_num[10]={0};
			sprintf(current_num,"%d",N);
			message = (char *)malloc(sizeof(char));
			message = realloc(message,strlen(message)+strlen(current_num));
			sprintf(message,"%s%s", message,current_num);
			//每次产生一个新数字就重新更新message的分配空间大小,并添加新数字
			while(N!=1){
				if(N%2==0){
					N/=2;
				}
				else{
					N = 3*N+1;
				}
				sprintf(current_num,"%d",N);
				message = realloc(message,strlen(message)+strlen(current_num));
				//新数字与旧数字之间隔一个空格
				sprintf(message,"%s %s", message,current_num);
			}
			printf("Successfully done, transmitting...\n");
			//改变共享内存文件的大小为SIZE
			ftruncate(shm_fd1, SIZE);
			ptr1 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd1, 0);
			sprintf(ptr1,"%s", message);
			ptr1 += strlen(message);
		}
	}
	else{
		wait(NULL);
		printf("I\'m the parent process %d\nThe Seq is:\n", getpid());
		void *ptr2;
		//访问现有的name为名的共享内存
		int shm_fd2 = shm_open(name, O_CREAT|O_RDWR, 0666);
		if(shm_fd2 < 0){
			printf("Open the shm2 failed!\n");
			return -1;
		}
		else{
			ptr2 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd2, 0);
			printf("%s\n",(char *)ptr2);
			//断开共享内存
			shm_unlink(name);
		}
	}
	return 0;
}

实验内容三、

要求

设计一个程序,通过普通管道进行通信,让一个进程发送一个字符串消息给
第二个进程,第二个进程收到此消息后,变更字母的大小写,然后再发送给第一
个进程。比如,第一个进程发消息:“I am Here”,第二个进程收到后,将它改变
为:“i AM hERE”之后,再发给第一个进程。
提示:
(1)需要创建子进程,父子进程之间通过普通管道进行通信。
(2)需要建立两个普通管道。

解决方案

一个普通的管道具有一个“write-end”写端和一个“read-end”读端。这里采用的是单向管道,即进程A建立一个管道并不断向B写,B也可以同时用另一个管道不断向A写。
管道一般分为两种:

  1. 普通管道–无法从创建它的进程外部访问。通常,父进程创建一个管道,并使用它与它创建的子进程通信。
  2. 命名管道–可以在没有父子关系的情况下被访问。
    在这里插入图片描述
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 25
#define READ_END 0
#define WRITE_END 1
int main(void){
	//创建两个管道
	int fd1[2],fd2[2];
	if (pipe(fd1) == -1 || pipe(fd2) == -1) {
		fprintf(stderr,"Pipe failed");
		return -1;
	}
	/*创建子进程*/
	pid_t pid;
	pid = fork();
	if (pid < 0) { /* error occurred */
		fprintf(stderr, "Fork Failed");
		return -1;
	}
	if (pid > 0) {
		char write_msg1[BUFFER_SIZE] = "I am Here";
		char read_msg1[BUFFER_SIZE];
		//因为这一端要先当写端,所以要把这个进程的读过程先关闭
		close(fd1[READ_END]);
		printf("Parent send the message: %s\n", write_msg1);
		write(fd1[WRITE_END], write_msg1, strlen(write_msg1)+1);
		close(fd1[WRITE_END]);
		//关闭写端,开始读
		close(fd2[WRITE_END]);
		read(fd2[READ_END], read_msg1, BUFFER_SIZE);
		printf("Parent get the message: %s\n", read_msg1);
		close(fd2[READ_END]);
	}
	else {
		char write_msg2[BUFFER_SIZE];
		char read_msg2[BUFFER_SIZE];
		close(fd1[WRITE_END]);
		read(fd1[READ_END], read_msg2, BUFFER_SIZE);
		printf("Child get the message: %s\n", read_msg2);
		close(fd1[READ_END]);
		//A-Z <---> a-z
		for(int i = 0; i < strlen(read_msg2); i++){
			if(read_msg2[i]>='a' && read_msg2[i]<='z'){
				read_msg2[i] = read_msg2[i]-32;
			}
			else if(read_msg2[i]>='A' && read_msg2[i]<='Z'){
				read_msg2[i] = read_msg2[i]+32;
			}
		}
		strcpy(write_msg2,read_msg2);
		close(fd2[READ_END]);
		printf("Child send the message: %s\n", write_msg2);
		write(fd2[WRITE_END], write_msg2, strlen(write_msg2)+1);
		close(fd2[WRITE_END]);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值