操作系统实验三之进程间共享内存

一、题目

  使用fork()编写一个c语言程序。它在子进程中生成Fibonacci数列,数列号码在命令行中提供。例如,如果提供的是5,Fibonacci数列中的前5个数将由子进程产生。在子进程生成数列之后,该数列由父进程输出。使用共享内存实现。该题目原题如下:
操作系统实验三之进程间共享内存_第1张图片
操作系统实验三之进程间共享内存_第2张图片
这里写图片描述
操作系统实验三之进程间共享内存_第3张图片
这里写图片描述

二、学习基本知识

1. linux下的错误捕获
linux下错误的捕获:errno和strerror
errno是一个全局变量,包含在头文件#include

#include 
#include 
#include  
int main(void)
{
int   fd;
extern int errno;
if((fd =open("/dev/dsp",O_WRONLY)) < 0)
{
   printf("errno=%d\n",errno); 
}

exit(0);
}

如果dsp设备忙的话errno值将是16.

strerror用于解释errno.用法如下:

#include 
#include 
#include  
int main(void)
{
int   fd;
extern int errno;
if((fd =open("/dev/dsp",O_WRONLY)) < 0)
{
   printf("errno=%d\n",errno); 
char * mesg = strerror(errno);
   printf("Mesg:%s\n",mesg); 
}

exit(0);
}

dsp设备忙的话将输出如下:
errno=16
Mesg:Device or resource busy

更多:http://blog.csdn.net/starstar1992/article/details/52756387

2. shmget, shmat, shmdt, shmctl

int shmget(key_t key, size_t size, int flag);
/*
创建或打开共享存储区
key: 标识符的规则
size:共享存储段的字节数
flag:读写的权限
返回值:成功返回共享存储的id,失败返回-1
*/


void *shmat(int shmid, const void *addr, int flag);
/*
连接共享存储区
shmid:共享存储的id
addr:一般为0,表示连接到由内核选择的第一个可用地址上,否则,如果flag没有指定SHM_RND,则连接到addr所指定的地址上,如果flag为SHM_RND,则地址取整
flag:如前所述,一般为0
返回值:如果成功,返回共享存储段地址,出错返回-1
*/


int shmdt(void *addr);
/*
拆除共享存储区连接
addr:共享存储段的地址,以前调用shmat时的返回值
当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段
*/


int shmctl(int shmid,int cmd,struct shmid_ds *buf)
/*
共享存储区控制
shmid:共享存储段的id
cmd:一些命令
    IPC_STAT 得到共享内存的状态
    IPC_SET 改变共享内存的状态
    IPC_RMID 删除共享内存 
IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。 

请注意,共享内存不会随着程序结束而自动消除,要么调用shmctl删除,要么自己用手敲命令去删除,否则永远留在系统中。
*/

更多:http://lobert.iteye.com/blog/1746041

3. int main(int argc,char* argv[])详解

argc是命令行总的参数个数 ,记录用户输入的参数个数。

argv[]为保存命令行参数的字符串指针,其中第0个参数是程序的全名,以后的参数为命令行后面跟的用户输入的参数,argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。 指针数组的长度即为参数个数argc。数组元素初值由系统自动赋予。比如:

int main(int argc, char* argv[])  
{  
int i;  
for(i = 0; icout<cin>>i;
return   0;
}  

执行时敲入

F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE aaaa bbb ccc ddd
输出如下:

F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE
aaaa
bbb
ccc
ddd

更多:http://www.cnblogs.com/clumsy1006/archive/2013/10/11/3362658.html
4. assert的用法
assert宏的原型定义在assert.h中, 其作用是如果它的条件返回错误,则终止程序执行,原型定义:

#include <assert.h>
void assert( int expression );

常用在函数开始处检验传入参数的合法性,如:

int resetBufferSize(int nNewSize)
{
  //功能:改变缓冲区大小,
  //参数:nNewSize 缓冲区新长度
  //返回值:缓冲区当前长度 
  //说明:保持原信息内容不变     nNewSize<=0表示清除缓冲区
  assert(nNewSize >= 0);
  assert(nNewSize <= MAX_BUFFER_SIZE);
  ...
}

更多:http://www.cnblogs.com/ggzss/archive/2011/08/18/2145017.html

三、代码实现

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 

#define MAX_SEQUENCE 10
#define PERM S_IRUSR | S_IWUSR//使当前用户可以读写这个区域
typedef struct{
    int fib_sequence[MAX_SEQUENCE];
    int sequence_size;
}share_data;

void Fibonacci(int n, int *Fibo){//获得斐波那契数列
    //assert(n >= 10);
    if(n == 0){
        Fibo[0] = 0;
        return;
    }

    Fibo[0] = 0;
    Fibo[1] = 1;
    if(n == 1){

        return;
    }
    for(int i=2; i< n;i++){
        Fibo[i] = Fibo[i-1] + Fibo[i-2];    
    } 
 }

int main(){
    int  num;
    int shmid;//共享内存ID
    char *shmptr;//父类共享内存地址指针
    share_data *shMemory;//创建结构体指针
    pid_t pid;

    printf("Please enter a positive number(no more than 10): ");
    while(1){
        scanf("%d", &num);
        if(num < 0)  printf("The number less than 0. Please try agian: ");
        else if(num > 10) printf("The number more than 0. Please try agian: ");
        else break;
    }

    //IPC_PRIVATE 保证使用唯一ID
    if((shmid = shmget(IPC_PRIVATE, sizeof(share_data), PERM)) < 0)
    {//创建共享内存
        printf("shmget error:%s\n", strerror(errno));
        return -1;
    }
    if((shMemory = (share_data*)shmat(shmid, 0, 0)) == (void*)-1)
    {//将共享内存连接到可用地址上
        printf("shmat error:%s\n", strerror(errno));
        return -1;
    }
    shMemory->sequence_size = num;

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

    if(pid == 0){//在子进程中生成斐波那契数列
        Fibonacci(shMemory->sequence_size, shMemory->fib_sequence);
        exit(0);
    }
    else if(pid > 0){//父进程中,执行 输出
        wait(0);
        for(int i = 0; i < shMemory -> sequence_size; i++){
            printf("%d ", shMemory->fib_sequence[i]);
        }
        printf("\n");
        shmdt(shMemory);//取消此内存
        shmctl(shmid, IPC_PRIVATE, NULL);//回收该内存段
        exit(0);
    }
    return 0;
 }

1. 注意不要漏了头文件wait.h,否则编译过程中会出现警告。
这里写图片描述
2. 由于头文件sys/ipc.h的存在 ,编译要使用下面的命令:
这里写图片描述
否则会出现警告:

这里写图片描述
原因与解决:http://www.justskins.com/forums/_svid_source-and-_xopen_source-263518.html

编译运行结果
这里写图片描述

  本次实验讲解到这里结束,希望对大家有帮助。

你可能感兴趣的:(操作系统实验)