年轻人要讲抄德,抄袭作业又蠢又坏,大家耗子尾汁
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写。
管道一般分为两种:
- 普通管道–无法从创建它的进程外部访问。通常,父进程创建一个管道,并使用它与它创建的子进程通信。
- 命名管道–可以在没有父子关系的情况下被访问。

#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;
}


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

920

被折叠的 条评论
为什么被折叠?



