精华内容
下载资源
问答
  • 实验报告 课程名称 操作系统 实验项目 windows进程管理 姓 名 专 业 计算机科学与技术 班 级 学 号: 计算机科学与技术学院 计算机系 2019年 4 月 23 日 实验项目名称 windows 进程管理 一 实验目的 学习windows系统...
  • 精选文库 成绩 实 验 报 告 课程名称 操作系统 实验项目 windows 进程管理 姓 名 专 业 计算机科学与技术 班 级 学 号 精选文库 计算机科学与技术学院 计算机系 2019 年 4 月 23 日 精选文库 实验项目名称 windows ...
  • 《操作系统原理》实验报告 一、实验目的 (1)理解操作系统生成概念和过程; (2)理解操作系统两类用户界面(操作界面,系统调用)概念; 二、实验内容 (1)在Unbantu或Fedora环境下裁剪和编译Linux内核,并启用新内核。...

    《操作系统原理》实验报告

    一、实验目的
    (1)理解操作系统生成的概念和过程;
    (2)理解操作系统两类用户界面(操作界面,系统调用)概念;

    二、实验内容
    (1)在Unbantu或Fedora环境下裁剪和编译Linux内核,并启用新的内核。
    (2)在Unbantu或Fedora环境下为Linux内核增加1-3个新的系统调用,并启用新的内核,编写一个应用程序测试新增加的系统调用是否能正确工作。
    (3)在windows环境下,利用高级语言编程环境(限定为VS环境或VC环境)调用相关的系统调用(即系统API)实现一个包括“进程创建,文件读写”的应用程序。(特别注意:自己在寝室先做,老师在机房现场检查结果!):有一个文本文件CommandList.txt,第一行是说明性文字:本文件最后一次打开和运行日期是20190325。第二行开始每行是-一个可执行程序的名称(含路径)。编写一个应用程序能打开该文件,并顺序执行其中的每个程序,并更新文件第一行中的日期。
    (4)在windows环境下,编写一个批处理程序(算命大师.bat),程序运行后,输入:出生年月日(例如20000731)。系统输出你的属相和星座,例如:你属兔,狮子座。(特别注意:自己在寝室先做,老师在机房现场检查结果!)

    三、实验过程
    此处主要粘贴核心的关键源代码和程序流程,并分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
    实验过程要给出环境配置过程的说明,开发过程的文字说明,开发环境中关键代码的典型截图和文字说明。
    1)Ubuntu下编译新内核
    环境配置:下载安装VMware,并创建Ubuntu系统的新虚拟机。
    逐次执行以下命令:
    sudo apt-get install libncurses5-dev libssl-dev

    sudo apt-get install build-essential openssl

    sudo apt-get install zlibc minizip

    sudo apt-get install libidn11-dev libidn11

    从这里下载linux内核
    https://www.kernel.org/pub/linux/kernel/v4.x/下载后解压即可。
    然后依次执行如下命令:
    sudo make mrproper
    sudo make clean
    sudo make menuconfig
    其中mrproper为清除编译过程中产生的所有中间文件,clean为清除上一次产生的编译中间文件,在menuconfig中出现选择的图形化界面后,直接按右方向键选择到exit退出,退出提示中选择保存,实现内核的默认配置。

    • 设置的四核八线程,故使用指令make –j8

    2)Ubuntu中为linux添加系统调用
    *下载linux内核包并解压,在/kernel/sys.c文件末尾加入系统调用函数。

    *添加函数声明,添加系统调用号
    在linux-4.16.10/arch/x86/include/asm/vim syscalls.h
    (插入asmlinkage long sys_mycall(int number);)

    *添加函数调用id
    在linux-4.16.10/arch/x86/syscalls的syscall_64.tbl文件中做如下添加。

     配置编译安装内核
    依次执行make oldconfig
    make –j8
    sudo makemodules_install
    sudomake install
     重启虚拟机,选择最新的linux内核运行。
    验证系统调用是否成功
    a、编写测试代码
    b、打开终端
    c、输入下列指令:

    3)创建进程程序
    利用vc编写源代码
    获取时间写入文本,
    读取文本的第二到最后一行,每行存入字符数组
    依次读取每行地址,创建相应进程。
    部分代码截图

    Windows进程创建函数解读
    BOOL CreateProcess
    (
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes。
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
    );
    lpApplicationName:指向一个NULL结尾的、用来指定可执行模块的字符串。
    这个字符串可以使可执行模块的绝对路径,也可以是相对路径,在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径。
    这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于 lpCommandLine 参数的最前面并由空格符与后面的字符分开。
    lpCommandLine:指向一个NULL结尾的、用来指定要运行的命令行。
    这个参数可以为空,那么函数将使用参数指定的字符串当作要运行的程序的命令行。
    如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行
    。新运行的进程可以使用GetCommandLine函数获得整个命令行。
    lpProcessAttributes:指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpProcessAttributes参数为空(NULL)
    ,那么句柄不能被继承。
    lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpThreadAttributes参数为空(NULL),
    那么句柄不能被继承。
    bInheritHandles:指示新进程是否从调用进程处继承了句柄。如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原
    进程拥有完全相同的值和访问权限。
    dwCreationFlags:指定附加的、用来控制优先类和进程的创建的标志。
    lpEnvironment:指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
    一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。
    lpCurrentDirectory:指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数
    为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件。
    lpStartupInfo:指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
    lpProcessInformation:指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。

    4)算命大师
    代码如下
    主要是利用脚本语言输入参数,处理参数,比较参数输出相应信息。
    @Echo Off
    Set /p yearmonthday=输入: 出生年月日 (例如 2000-07-31)

    Set year=%yearmonthday:~0,4%
    Set monthday=%yearmonthday:5,2%%yearmonthday:8,2%
    Set /a mod=%year%%%12

    if %mod%==0 Echo 你属猴
    if %mod%==1 Echo 你属鸡
    if %mod%==2 Echo 你属狗
    if %mod%==3 Echo 你属猪
    if %mod%==4 Echo 你属鼠
    if %mod%==5 Echo 你属牛
    if %mod%==6 Echo 你属虎
    if %mod%==7 Echo 你属兔
    if %mod%==8 Echo 你属龙
    if %mod%==9 Echo 你属蛇
    if %mod%==10 Echo 你属马
    if %mod%==11 Echo 你属羊

    if “%monthday%” LEQ “0119” echo 你的星座是魔蝎座
    if “%monthday%” GEQ “0120” if “%monthday%” LEQ “0218” echo 你的星座是水瓶座
    if “%monthday%” GEQ “0219” if “%monthday%” LEQ “0320” echo 你的星座是双鱼座
    if “%monthday%” GEQ “0321” if “%monthday%” LEQ “0419” echo 你的星座是白羊座
    if “%monthday%” GEQ “0420” if “%monthday%” LEQ “0520” echo 你的星座是金牛座
    if “%monthday%” GEQ “0521” if “%monthday%” LEQ “0621” echo 你的星座是双子座
    if “%monthday%” GEQ “0622” if “%monthday%” LEQ “0722” echo 你的星座是巨蟹座
    if “%monthday%” GEQ “0723” if “%monthday%” LEQ “0822” echo 你的星座是狮子座
    if “%monthday%” GEQ “0823” if “%monthday%” LEQ “0922” echo 你的星座是处女座
    if “%monthday%” GEQ “0923” if “%monthday%” LEQ “1023” echo 你的星座是天秤座
    if “%monthday%” GEQ “1024” if “%monthday%” LEQ “1122” echo 你的星座是天蝎座
    if “%monthday%” GEQ “0321” if “%monthday%” LEQ “0419” echo 你的星座是白羊座
    if “%monthday%” GEQ “1222” echo 你的星座是魔蝎座
    四、实验结果
    此处主要粘贴运行截图和分析。把你做完了的全部编程题按1),2)…方式顺序编号,每个编程题目自己给取一个5-10字的名字作为标题。
    1)重启长按shift键,进入选择

    选择新内核运行之,编译安装新内核成功!!

    2)重启虚拟机,选择最新的linux内核运行。
    验证系统调用是否成功
    a、编写测试代码
    b、打开终端
    c、输入下列指令:

    3)成功启动了两个程序(护眼包和酷狗音乐)

    4)运行结果如下

    /*进程创建代码(第三个实验代码)-----------------
    ----------------author JaryJin*/
     
    #include <windows.h>
    #include <string>
    #pragma warning(disable:4996)
    int main()
    {
        char timeContent[100] = {0};
        char readBuffer[1000] = {0};
        char command[10][100] = {0};   		
        int commandNum = 0;		       
        DWORD dwRead = 0;
        DWORD dwWrite = 0;
        HANDLE hFile = CreateFile("./CommandList.txt", GENERIC_WRITE | GENERIC_READ, 0,
    	NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     
        if (hFile == INVALID_HANDLE_VALUE)
        {
            printf("CreateFile failed!");
            CloseHandle(hFile);
            return -1;
        }
     
    	//写入时间
    	FILETIME createTime,lastAccessTime,lastWriteTime;
    	GetFileTime(hFile,&createTime,&lastAccessTime,&lastWriteTime);
    	SYSTEMTIME st;
    	memset(&st,0x0,sizeof(st));
    	FileTimeToSystemTime(&lastWriteTime,&st);//将文件时间格式转换为系统时间格式(UTC格式),可以看到小时数比真实的小了8
    	TIME_ZONE_INFORMATION tz;
    	GetTimeZoneInformation(&tz);//获取当地时区信息
    	SYSTEMTIME localST;
    	SystemTimeToTzSpecificLocalTime(&tz,&st,&localST);//将UTC时间格式转换为当地时间格式,因为中国是东8区,所以转换时在小时上加了8
     
    	sprintf(timeContent,"本文件最后一次打开和运行日期是%4d年%02d月%02d日%02d时%02d分%02d秒\r\n",localST.wYear,localST.wMonth,localST.wDay,localST.wHour,localST.wMinute,localST.wSecond);
    	WriteFile(hFile, timeContent, strlen(timeContent), &dwWrite, NULL);
     
    	//读取文本
            DWORD fileSize = GetFileSize(hFile, NULL);
            ReadFile(hFile, readBuffer, fileSize, &dwRead, NULL);
            readBuffer[fileSize] = '\0';
    //	printf("%s",readBuffer);
    	CloseHandle(hFile);
     
    	//循环将读取的命令分隔开来
    	char tmpBuffer[1000] = {0};
    	for(int i=0;i<fileSize;i++)
    	{
    		if('\r' == readBuffer[i])
    		{
    			strncpy(command[commandNum],readBuffer,i);
    			i=i+2;
    			commandNum++;
    			strcpy(tmpBuffer,&readBuffer[i]);
    			strcpy(readBuffer,tmpBuffer);
    			i=0;
    		}else if('\0' == readBuffer[i])
    		{
    			strncpy(command[commandNum],readBuffer,i);
    			commandNum++;
    			break;
    		}
    	}
    	//循环创建进程
    	for(int j=0;j<commandNum;j++)
    	{
    		STARTUPINFO si = { sizeof(si) };
    		PROCESS_INFORMATION pi;
    		si.dwFlags = STARTF_USESHOWWINDOW; 
    		si.wShowWindow = TRUE; 
    		BOOL bRet = CreateProcess ( NULL,	
    			command[j],
    			NULL,	
    			NULL,	
    			FALSE,	
    			CREATE_NEW_CONSOLE,	
    			NULL,	
    			NULL,	
    			&si, &pi);
    		if(bRet)
    		{ // 不使用的句柄最好关掉
    			CloseHandle(pi.hThread);
    			CloseHandle(pi.hProcess);
    			printf("新进程的ID号:%d\n",pi.dwProcessId);
    			printf("新进程的主线程ID号:%d\n",pi.dwThreadId);
    		}
    		Sleep(1000);
    	}
     
    	system("pause");
        return 0;
    }
    

    五、体会
    作为一个linux小白实现上述过程是很痛苦的,但是真的很想去把这件事做好,一步一步,从什么都不知道到好像懂了一点点,再到可以看懂每行代码的含义,到最后做出成果,刚开始做的时候比较浮躁,到了后面就冷静了。看了20多篇博客,解决各种问题;之前做的时候犯的错误多的数不胜数,后面弄的时候一点点规避和解决,功夫不负有心人,虽然为了这个作业花了我不少精力(本人理解能力较差,周围有的大佬很快就弄完了),这次的成功相信可以给我后面进一步学好操作系统的信心,也培养了自己的耐心和攻坚精神。相信自己会继续加油的!

    展开全文
  • 1.实验目的 学会通过基本Linux进程控制函数创建进程,并实现协同工作。创建两个进程,让子进程读取输入,父进程等待子进程读完文件后继续执行。2.实验软硬件环境安装Windows7计算机VMware软件,在其上安装有...

    供大家交流学习,最好自己动手做,这样才有最深切的体会。


    1.实验目的

        学会通过基本的Linux进程控制函数创建子进程,并实现协同工作。创建两个进程,让子进程读取输入,父进程等待子进程读完文件后继续执行。


    2.实验软硬件环境
    • 安装Windows7的计算机
    • VMware软件,在其上安装有Ubuntu虚拟机

    3.实验内容

    1.掌握vfork()、fork()、waitpid()等函数的使用。

    2.用以上函数在Linux操作系统创建子进程,子进程创建文件,父进程等待子进程完成后读取文件,并使父子进程分别修改资源,了解vfork()和fork()的区别。

    相关函数介绍:

    • pid_t  vfork(void) 
      创建进程但子进程共享父进程的地址空间,即子进程对资源的改变会影响父进程,且强制优先子进程运行,父进程被阻塞,只有当子进程运行完后父进程才能运行。返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。
    • pid_t  fork(void) 
      子进程只是父进程的简单拷贝,子进程对资源的改变不会影响父进程,且父子进程可以并发运行。返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。
    • pid_t waitpid(pid_t pid,int * status,int options)                                                                                                  waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码,其他数值意义如下:                                                                                                                                              pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。                                                                                   pid=-1 等待任何子进程,相当于 wait()。                                                                                                               pid=0 等待进程组识别码与目前进程相同的任何子进程。                                                                                           pid>0 等待任何子进程识别码为 pid 的子进程。                                                                                                         参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);如果我们不想使用它们,也可以把options设为0,如:ret=waitpid(-1,NULL,0);WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。
    • pid_t  getpid(void) 
      返回子进程的pid。
    • pid_t  getppid(void) 
      返回父进程的pid,因为系统分配pid的时候遵循相邻递增的原则,所以一般子进程的pid比父进程的pid大1

    4.实验程序及分析

    实验程序:

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<unistd.h>
    //创建子进程fork()是简单的拷贝,而vfork()类似于线程,强制执行子进程让父进程阻塞,在子进程执行exit前子进程共享父进程的数据,子进程结束后在运行父进程。
    int main()
    {
    	pid_t child;
    	int i=0;
    	printf("i = %d\n",i);
    	char str[50];
    	puts("1.excute");
    	child = vfork();
    	//child = fork();
    //无论父子进程都是从fork或vfork后运行的,fork创建了子进程后,在子进程中返回0,在父进程中返回子进程的pid。
    
    	puts("2.excute");
    	
    	if(child== -1)
    	{
    		perror("fork");
    		puts("fail to create a sup process");
    		exit(1);
    	}
    	else if(child == 0)
    	{
    		puts("\nin child:");
    		printf("\tchild pid = %d\n",getpid());
    		printf("\tchild ppid = %d\n",getppid());
    		puts("writing in file :\n This is my work\n");
    		FILE  *fp = fopen("123.txt","w");
    		fprintf(fp,"%s","This is my work");		
    		fclose(fp);
    		i++;
    		printf("i = %d\n",i);
    		exit(0);
    	}
    	else
    	{
    		waitpid(child,NULL,0);
    //等待pid为child的子进程运行,当子进程运行完后父进程运行。
    		puts("\nin parent:");
    		puts("reading form file ...");
    		FILE *fp = fopen("123.txt","r");
    		fgets(str,50,fp);
    		puts(str);
    		fclose(fp);
    		i++;
    		printf("i = %d\n",i);
    		exit(0);
    	}
    	return 0;
    }

    终端结果:

    vfork():

    fork():


    分析:

    由以上代码,分别运行vfork()和fork()函产生子进程的结果有所不同,通过这些可以知道:

    • vfork()创建子进程后强制优先执行子进程,将父进程阻塞,只有当子进程运行完之后才运行父进程的代码。所以vfork()的运行结果中子进程的2.excute和父进程的2.excute没有连在一起。而是分开了。又因为父子进程是共享地址空间的,故子进程对i++的操作会影响父进程,父进程中输出i=2.
    • fork()运行结果就与vfork()不同了。因为fork()是对父进程的简单拷贝,且可以与父进程并发执行。故子进程的2.excute和父进程的2.excute连在一起。且子进程中i++对父进程没有影响,故父进程中的i=1.父进程中waitpid()函数使得父进程在运行else分支代码前要等待子进程。

    5.实验截图

    fork()

    vfork()



    6.实验心得体会
    这次实验让我掌握了如何Linux在创建子进程,并且较为深入的了解了fork()和vfork()的区别。fork()创建的子进程只是对父进程的简单拷贝,并且父子进程并发执行。vfork()创建的子进程共享父进程的地址空间,且父进程被阻塞,只有当子进程运行完成后才能执行。



    展开全文
  • 实验一 Linux系统安装及用户界面使用 一.实验目的 1. 了解Linux系统安装、熟悉系统启动过程和使用环境。 2. 掌握Linux环境下vi编辑器使用方法。 3. 掌握Linux系统中编辑、编译、调试...实验报告 内含源代码
  • 1、在WINDOWS 2000环境下,创建一个控制台进程,此进程包括4个线程:2个生产者线程和2个消费者线程。 2、用信号量机制解决进程(线程)同步与互斥问题。 二、实验目的 1.掌握基本同步互斥算法,理解生产者和消费者...
  • TCP聊天室 VC版 含实验报告

    热门讨论 2010-01-04 15:08:51
    一、 系统概要 1、 开发平台: Windows XP, Visual C++ 6.0 ...二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。
  • Windows2000环境下 创建一个控制台进程进程包含n个线程 用这n个线程来表示n个读者或写者 每个线程按相应测试数据文件 后面有介绍 要求进行读写操作 用信号量机制分别实现读者优先和写者优先读者 写者问题 ...
  • 理解和掌握Linux和Windows进程通信系统调用功能,通过实验和学习,提高对进程痛惜系统调用编程能力。 掌握使用信号量机制完成进程间同步和互斥方法。 二、实验内容 设置一个大小为缓冲区,且初始为空。 创建...

    操作系统课程设计 生产者消费者实验报告

    一、实验目的

    加深对进程概念的理解,明确进程与程序的区别。
    认识并发执行的本质。
    理解和掌握Linux和Windows进程通信系统调用的功能,通过实验和学习,提高对进程痛惜系统调用的编程能力。
    掌握使用信号量机制完成进程间同步和互斥的方法。

    二、实验内容

    设置一个大小为的缓冲区,且初始为空。
    创建两个生产者和三个消费者。
    生产者和消费者都必须用进程模拟。
    对于生产者而言,随机等待一段时间后,往缓冲区中存数据,若缓冲区满,则等待消费者取走数据后再存放。
    对于消费者而言,随机等待一段时间后,从缓冲区中取数据,若缓冲区空,则等待生产者存放数据后再取走。
    每个生产者进程需要执行6次存数据的操作,每个消费者进程需要执行4次取数据的操作。
    显示每次向缓冲区添加或取走的数据以及时间,每次操作后都要显示缓冲区中的全部数据,生产者、消费者进程号。

    三、实验环境

    虚拟机: Ubuntu 18.04
    Linux操作系统计:Linux-5.9.10
    操作系统:Windows10
    Win10下的IDE:Visual Studio2019

    四、程序设计与实现

    4.1 设计思路

    定义大小为3的共享内存区:
    struct BUF {
    char array[BufLength];
    int head;
    int tail;
    int IsEmpty;};
    其中array表示生产者和消费者存取数据的环形缓冲区,设置其大小BufLength为3。头指针head用于指向消费者下一次待取的数据,尾指针tail指向生产者下一次需要存放数据的缓冲,IsEmpty用来表示环形缓冲区是否为空,取0为不空,取1为空。
    在实验中,设置三个信号量如下:
    互斥信号量MUTEX:用于生产者进程与生产者进程、消费者进程与生产者进程、消费者进程与消费者进程间互斥使用缓冲区,初始值为1。
    同步信号量EMPTY:用于指示当前空缓冲区的可用数量,用于制约生产者进程向缓冲区存数据,初始值为3。
    同步信号量FULL:用于指示当前有数据的缓冲区的数量,用于制约消费者进程取数据,初始值为0。
    假设P(MUTEX)操作是申请MUTEX资源,V(MUTEX)操作是释放MUTEX资源,给出生产者消费者问题的流程如下。

    生产者存数据的流程:

    (1) 生产数据
    (2) P(EMPTY) //消费者进程申请一个空缓冲单元,EMPTY-1
    (3) P(MUTEX) //若申请到空缓冲单元,则申请这个缓冲单元的使用权,保证
    //这段时间内没有其他进程在使用环形缓冲区,MUTEX-1
    (4) 生产者进程向缓冲区中存放数据
    (5) 修改尾指针,指向下一个缓冲单元(此缓冲单元不一定为空缓冲),修改缓冲区状态IsEmpty。
    (6) V(MUTEX) //生产者进程释放对环形缓冲区的使用权,MUTEX+1
    (7) V(FULL) //FULL+1,表示环形缓冲区中可取数据的总量,唤醒消费者进程
    //取数据

    消费者取数据的流程:

    (1) P(FULL) //申请一个放有数据的缓冲单元(即申请一个产品),FULL-1
    (2) P(MUTEX) //若申请到产品,保证这段时间没有其他进程在使用环形缓冲
    //区,MUTEX-1
    (3)取得当前头指针指向的数据,清除存储这一数据的缓冲单元的内容,修改缓冲区状态,并修改尾指针指向下一缓冲单元。
    (4) V(MUTEX) //消费者进程释放对环形缓冲区的使用权
    (5) V(EMPTY) //Empty+1,表示环形缓冲区中空缓冲单元的数量,用于唤醒生
    //产者进程向缓冲区中存数据。
    由于在生产与消费的过程中,有同步信号量FULL和EMPTY控制各进程(消费者或生产者)是否能够申请缓冲单元,所以不会出现缓冲区满而生产者仍然往里放数据,缓冲区空而消费者仍然在取数据的情况。此外,由于MUTEX的存在,保证各进程能够互斥使用环形缓冲区,MUTEX、FULL与EMPTY的合理搭配保证不会有死锁的产生。

    4.2 Windows下的消费者生产者问题(详细见代码)

    函数功能:

    (1) 创建信号量:

    HANDLE CreateSemaphore(  //创建信号量
    	  lpSemaphoreAttributes, //NULL表示默认属性
    	  lInitialCount,         //信号量的初值
    	  lMaximumCount,  //信号量的最大值
    	  lpName);         //信号量的名称
    

    (2)释放信号量:

    BOOL ReleaseSemaphore(
    	  hSemaphore,   //信号量的句柄
    	  lReleaseCount,     //信号量计数增加值
    	  lpPreviousCount);  //返回信号量原来值
    

    (3)打开信号量:

    HANDLE  OpenSemaphore(dwDesiredAccess,  bInheritHandle,  lpName)
    

    (4)关闭信号量(hSemphore为信号量的句柄):

    CloseHandle(hSemphore)
    

    (5)创建共享内存区:

    HANDLE CreateFileMapping(
      HANDLE hFile,// 文件句柄,填写 INVALID_HANDLE_VALUE
      LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//安全描述符 
      DWORD flProtect,    // 映射对象保护属性
      DWORD dwMaximumSizeHigh,		// 文件映射的最大长度的高32位
      DWORD dwMaximumSizeLow,		// 文件映射的最大长度的低32位
      LPCTSTR lpName);         // 文件映射对象名称
    

    (6)将共享内存区映射到进程虚拟地址:

    LPVOID MapViewOfFile(
      HANDLE hFileMappingObject,  //文件映像对象句柄
      DWORD dwDesiredAccess,      // 数据的访问方式
      DWORD dwFileOffsetHigh,     // 文件映射起始偏移的高32位
      DWORD dwFileOffsetLow,      // 文件映射起始偏移的低32位
      DWORD dwNumberOfBytesToMap);  // 文件中要映射的字节数,0表示映射整个文件映射对象
    

    (7)打开进程中对应的内存映射对象(共享内存区):

    HANDLE OpenFileMapping(
      DWORD dwDesiredAccess,  // 数据的访问方式
      BOOL bInheritHandle,    // 是否继承句柄
      LPCTSTR lpName );         // 要打开的文件映射对象名称
    

    (8)取消进程地址空间的映射,关闭共享内存区:

    UnmapViewOfFile(pLocalMem);  
    pLocalMem=NULL;   
    //关闭内核映射文件,hFileMapping 为内存映射文件句柄  
    CloseHandle(hFileMapping);
    

    父进程的流程:

    (1) 创建信号量,创建共享内存区。使用CreateSemaphore()接口创建信号量MUTEX、FULL、EMPTY,并赋予初值分别为1、0、3,使用CreateFileMapping()接口创建共享内存区,内存区大小即为BUF的大小,并将共享内存区映射到父进程地址空间,得到共享内存区的入口地址pfile。
    (2) 通过pfile对共享内存区进行初始化,在之后的各进程中,几乎都采用如下的方式对共享内存区进行读写操作。

    if (pfile != NULL) {
    	ZeroMemory(pfile, sizeof(struct BUF));
    }//得到共享内存区的数据结构
    struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile);
    //初始化
    pbuf->head = 0;
    pbuf->tail = 0;
    pbuf->IsEmpty = 0;
    memset(pbuf->array, 0, sizeof(pbuf->array));
    

    (3)调用UnmapViewOfFile(pfile)接口解除共享内存在父进程地址空间的映射,使用CloseHandle(pfile)关闭共享内存区
    (4)创建三个生产者进程,两个消费者进程。在创建进程时使用了自己写的StartClone(int i)函数,详见代码。该函数在每个进程的argv[]参数中依次存放文件名、给子进程的编号(编号0、1、2表示该进程为生产者进程,编号3、4表示该进程为消费者进程)。
    (5)等待所有子进程运行完毕,此处使用到WaitForMultipleObjects()接口
    (6)调用CLoseHandle()接口关闭信号量

    生产者进程流程:

    (1) 调用OpenSemaphore()接口打开MUTEX、FULL、EMPTY信号量。
    (2) 调用OpenFileMapping()接口打开共享内存区,调用MapViewOfFile()接口把该内存区映射到生产者进程的虚拟地址空间。
    (3) 语句struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile)将pbuf指向共享内存区,且其结构为BUF,这使得之后在读取共享内存区中的数据时,可以照BUF的结构进行读取。
    (4) 每个生产者进程进行6次存数据操作,存数据对应于4.1中的生产者存数据流程,只是增加了少许步骤。
    生产者进程先通过GetRandomChar()得到数据,在申请空缓冲单元后,调用Sleep(GetRandomSleep())函数使其随机等待一段时间,在得到环形缓冲区的使用权后,存放数据。这两个函数为自己编写的函数,具体见代码。
    在做完存数据的一系列操作之后,调用localtime()函数获取系统时间,并以时分秒的形式打印,除此,在每次存数据后,应打印出该该进程标识、存放的数据、环形缓冲区的所有数据以供观察,并在打印完后清空输出缓冲区。
    (5) 调用UnmapViewOfFile()解除共享内存区在该进程地址空间的映射,并调用CloseHandle()关闭共享内存区句柄,关闭MUTEX、EMPTY、FULL信号量句柄。

    消费者进程流程:

    (1) 调用OpenSemaphore()接口打开MUTEX、FULL、EMPTY信号量。
    (2) 调用OpenFileMapping()接口打开共享内存区,调用MapViewOfFile()接口把该内存区映射到生产者进程的虚拟地址空间,并得到pfile,可将其看做共享内存区的入口。
    (3) 语句struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile)将pbuf指向共享内存区,且其结构为BUF,这使得之后在读取共享内存区中的数据时,可以照BUF的结构进行读取。
    (4) 每个消费者进程进行4次取数据操作,取数据操作对应于4.1中消费者取数据流程,但步骤有所增加:
    消费者进程在申请产品后调用Sleep(GetRaondomSleep())函数等待随机时间,在得到环形缓冲区的使用权后,后开始取数据,在完成取数据的一系列操作后,调用localtime()函数得到当前系统时间,并以时分秒的形式打印,除此,还需打印出消费者进程标识、取到的数据、当前环形缓冲区的全部数据等,并在打印完后清除输出缓冲区。
    (5) 调用UnmapViewOfFile()解除共享内存区在该进程地址空间的映射,并调用CloseHandle()关闭共享内存句柄,关闭MUTEX、EMPTY、FULL信号量句柄。
    在main()函数中,根据传过来的argv[1]参数(即我们给子进程的编号,令此编号为id)进行不同的操作,具体如下:

    if argc>1:
    	int id = atoi(argv[1]);
    	if id < 0:
    		exit(-1);//编号小于0说明出现了问题
    	else if id < 2:
    		生产者进程流程
    	else if id <5:
    		消费者进程流程
    else:
    	父进程流程
    

    4.3 Linux下的生产者消费者问题(详见代码):

    在Linux下,定义存取数据所需要的P、V操作如下:

    void P(int sem_id, int sem_num){
    	struct sembuf xx;
    	xx.sem_num = sem_num;
    	xx.sem_op = -1;
    	xx.sem_flg = 0;
    	semop(sem_id, &xx, 1);}
    void V(int sem_id, int sem_num){
    	struct sembuf xx;
    	xx.sem_num = sem_num;
    	xx.sem_op = 1;
    	xx.sem_flg = 0;
    	semop(sem_id, &xx, 1);}
    

    在Linux下,共享内存区的定义与之前一致,只是将名字BUF改为了ShrBuf。

    使用到的函数:

    (1) 创建信号量或取得信号量

    int semget(key_t key, int num_sems, int sem_flags);
    

    参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget()函数并提供一个键,再由系统生成一个相应的信号标识符(semget()函数的返回值),只有semget()函数才直接使用信号量键,所有其他的信号量函数使用由semget()函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
    参数num_sems指定需要的信号量数目。
    参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
    semget()函数成功返回一个相应信号标识符(非零),失败返回-1。
    (2)修改信号量的值

    int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
    

    (3) 对信号量执行控制操作。如读取或修改信号量集合的有关状态信息,撤销信号量集合等等。

    int semctl(int sem_id,int sem_num,int cmd,union semun arg);
    

    参数分别为信号量集合标识、信号量索引、要执行的命令、设置信号量信息的参数。
    (4) 申请(创建)一个共享内存区

    shmget(key,size,shmflg)
    

    key为共享内存区的关键字,shmflg为创建或打开标志,size是共享内存区字节长度。
    (5) 将共享内存区加到申请通信的进程地址空间

    shmat(shmid,shmadd,shmflg)
    

    shmid是共享内存区的标识,shmadd是给出的附加到进程虚空间的地址,通常 shmflg和shmadd为0。
    (6) 解除共享内存区与进程之间的连接

    shmdt(shamaddr)
    

    shmaddr是共享内存区在进程地址空间的虚地址
    (7) 对共享内存区执行控制操作

    int shmctl(int shmid,int cmd,struct shmid_ds *buf)
    

    cmd参数可以指定以下值:
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段
    SHM_LOCK:将共享段锁在内存
    SHM_UNLOCK:解锁共享段

    父进程流程:

    (1) 调用semget()接口创建大小为3的信号量集,并返回信号量集的标识,若创建失败,应返回错误信息。
    (2) 调用semctl()接口初始化信号量集中各信号的信息。在信号集中,索引0表示MUTEX,1表示EMPTY,2表示FULL。
    (3) 调用shmget()接口申请共享内存区,且大小为ShrBuf的大小,若申请失败,应返回错误信息,若申请成功,调用shmat()接口将该共享内存区附加到父进程地址空间,结构体指针MyBuf指向这一地址空间,大致语句如下:

    shm_id = shmget(IPC_PRIVATE,sizeof(ShrBuf),0600);
    MyBuf = shmat(shm_id, 0, 0);
    

    如此,父进程中便可借助MyBuf存取共享内存区,MyBuf是ShrBuf结构体类型的。子进程附加共享内存区的方式以及让MyBuf指向这一内存区的方式与父进程相同。
    (4) 借助MyBuf指针初始化共享内存区
    (5) 创建两个生产者进程,三个消费者进程,并让其做相关操作。
    (6) 调用wait()接口等待子进程运行结束,因为创建了5个子进程,所有用while(wait(0)!=-1)语句来保证所有子进程都运行结束。
    (7) 调用shmdt()接口解除父进程与共享内存区之间的连接,调用shmctl()接口释放共享内存区(指定cmd为IPC_RMID),调用shmctl()接口删除信号量集合(指定cmd为IPC_RMID)。

    生产者进程流程:

    (1) 调用shmat()接口将共享内存区映射到该进程的地址空间,若映射出错,返会错误信息,若成功映射则执行(2)
    (2) 生产者进程要进行6次数据存储,每次存数据的流程与4.1中生产者存数据的流程大体一致,但是增加了几个步骤,如下:生产者进程在申请空缓冲单元和环形缓冲区的使用权后,调用sleep(GetRandomWait())函数使其随机等待一段时间,后将GetRandomInput()得到的随机数据存入缓冲单元。这两个函数为自己编写的函数,具体见代码。
    在做完存数据的一系列操作之后,调用localtime()函数获取系统时间,并以时分秒的形式打印,除此,在每次存数据后,应打印出该该进程标识、存放的数据、环形缓冲区的所有数据以供观察,并在打印完后清空输出缓冲区。
    (3) 在6次存储数据后,调用shmdt(MyBuf)接口取消共享内存区与生产者进程的连接。
    (4) 调用exit(0)接口结束进程。

    消费者进程流程:
    

    (1) 调用shmat()接口将共享内存区映射到该进程的地址空间,若映射出错,返会错误信息,若成功映射则执行(2)
    (2) 消费者进程要进行4次取数据操作,每次取数据的流程与4.1中消费者取数据的流程大体一致,但是增加了几个步骤,如下:
    消费者进程在申请空缓冲单元和环形缓冲区的使用权后,调用sleep(GetRandomWait())函数使其随机等待一段时间。
    在做完取数据的一系列操作之后,调用localtime()函数获取系统时间,并以时分秒的形式打印,除此,在每次存数据后,应打印出该该进程标识、存放的数据、环形缓冲区的所有数据以供观察,并在打印完后清空输出缓冲区。
    (3) 在4次取数据后,调用shmdt(MyBuf)接口取消共享内存区与生产者进程的连接。
    (4) 调用exit(0)接口结束进程。
    在程序中,为了更好地区分父进程、消费者进程、生产者进程的执行关系,给出以下控制流:

    main(){
    	信号量的创建、初始化;
    	共享内存的创建,共享内存区附加到父进程地址空间及初始化;
    	for(i=0;i<2;i++):
    		创建生产者进程:
    			如果创建失败,返回错误信息;
    		如果进程标识号为0:
    			生产者进程流程;
    	for(i=0;i<3;i++):
    		创建消费者进程:
    			如果创建失败,返回错误信息;
    		如果进程标识号为0:
    			消费者进程流程;
    		等待子进程结束;
    		解除映射、删除共享内存区、删除信号量集;
    }
    

    4.4 实验结果

    4.4.1 Linux下的实验结果

    在这里插入图片描述

    查看共享内存、信号量是否被释放:
    在这里插入图片描述

    前后执行多次发现,结果都如上图,可见,在程序执行完后,信号量数组为空,这足以说明共享内存的和信号量在使用完后被释放掉。
    4.4.2 Windows下的实验结果
    在这里插入图片描述

    五、实验收获与体会

    本次实验主要是针对操作系统中的进程间同步和互斥等知识的运用。
    在实验中,掌握了Linux和Windows下共享内存的创建、使用和销毁,掌握了信号量的申请、使用记忆销毁,对Linux和Windows下关于共享内存、信号量创建、使用、销毁以及进程创建、销毁的系统调用有了更深刻的认识和理解。
    在实验中,掌握了如何通过信号量实现进程间的同步和互斥。
    在实验中,要注意释放已申请的共享内存和信号量。在Linux下,需要调用shmctl来销毁共享内存区,调用semctl来销毁信号量;在Windows下,应记得调用CloseHandle()关闭句柄。
    Windows下用WaitForSingleObject()来实现信号量的P操作。

    六、实验代码

    Windows下的实验代码

    #include<windows.h>
    #include<iostream>
    #include<string>
    #include<time.h>
    #define BufLength 3
    #define P(S) WaitForSingleObject(S,INFINITE);
    #define V(S) ReleaseSemaphore(S,1,NULL);
    static LPCTSTR filemapping_name = "FileMapping";
    HANDLE ProcessHandle[5];
    using namespace std;
    
    struct BUF {
    	char array[BufLength];
    	int head;
    	int tail;
    	int IsEmpty;
    };
    
    int GetRandomSleep() {
    	return (rand() + GetCurrentProcessId())%100 + 1000;
    }
    
    char GetRandomChar() {
    	return ((rand() + GetCurrentProcessId()) % 26 + 'A');
    }
    
    ///创建子进程 
    void StartClone(const int id) {
    	TCHAR szFilename[MAX_PATH];
    	GetModuleFileName(NULL, szFilename, MAX_PATH);
    
    	TCHAR szcmdLine[MAX_PATH];
    	sprintf(szcmdLine, "\"%s\" %d", szFilename, id);
    
    	STARTUPINFO si;
    	ZeroMemory(reinterpret_cast<void*>(&si), sizeof(si));
    	si.cb = sizeof(si);
    	PROCESS_INFORMATION pi;
    
    	BOOL bCreateOK = CreateProcess(
    		szFilename,
    		szcmdLine,
    		NULL,
    		NULL,
    		FALSE,
    		CREATE_DEFAULT_ERROR_MODE,
    		NULL,
    		NULL,
    		&si,
    		&pi);
    	//通过返回的hProcess来关闭进程
    	if (bCreateOK) {
    		CloseHandle(pi.hProcess);
    		CloseHandle(pi.hThread);
    		ProcessHandle[id] = pi.hProcess;
    	}
    	else {
    		printf("child process error!\n");
    		exit(0);
    	}
    }
    
    ///父进程程序 
    void ParentProc() {
    	//创建信号量 
    	HANDLE MUTEX = CreateSemaphore(NULL, 1, 1, "mymutex");
    	HANDLE EMPTY = CreateSemaphore(NULL, 3, 3, "myempty");
    	HANDLE FULL = CreateSemaphore(NULL, 0, 3, "myfull");
    	
    	//创建内存映射
    	HANDLE hMapping = CreateFileMapping(
    		NULL, 
    		NULL, 
    		PAGE_READWRITE, 
    		0, 
    		sizeof(struct BUF), 
    		filemapping_name);
    	if (hMapping != INVALID_HANDLE_VALUE) {
    		LPVOID pfile = MapViewOfFile(
    			hMapping, 
    			FILE_MAP_ALL_ACCESS, 
    			0, 
    			0, 
    			0);
    		if (pfile != NULL) {
    			ZeroMemory(pfile, sizeof(struct BUF));
    		}
    		//初始化
    		struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile);
    		pbuf->head = 0;
    		pbuf->tail = 0;
    		pbuf->IsEmpty = 0;
    		memset(pbuf->array, 0, sizeof(pbuf->array));
    		//解除映射
    		UnmapViewOfFile(pfile);
    		pfile = NULL;
    		CloseHandle(pfile);
    	}
    	//生产者
    	for (int i = 0; i < 2; i++) {
    		StartClone(i);
    	}
    	//消费者
    	for (int i = 2; i < 5; i++) {
    		StartClone(i);
    	}
    	WaitForMultipleObjects(5, ProcessHandle, TRUE, INFINITE);
    	//回收信号量 
    	CloseHandle(EMPTY);
    	CloseHandle(FULL);
    	CloseHandle(MUTEX);
    }
    ///生产者 
    void Producer() {
    	//打开信号量
    	//HANDLE MUTEX = CreateMutex(NULL,FALSE,"mymutex");
    	HANDLE MUTEX = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "mymutex");
    	HANDLE EMPTY = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "myempty");
    	HANDLE FULL = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "myfull");
    
    	//打开共享内存区,并加载到当前进程地址空间
    	HANDLE hmap = OpenFileMapping(
    		FILE_MAP_ALL_ACCESS, 
    		FALSE, 
    		filemapping_name);
    	LPVOID pfile = MapViewOfFile(
    		hmap,
    		FILE_MAP_ALL_ACCESS,
    		0,
    		0,
    		0);
    	struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile);
    	
    	for (int i = 0; i < 6; i++) {
    		char ch = GetRandomChar();
    		P(EMPTY);
    		//sleep
    		Sleep(GetRandomSleep());
    		P(MUTEX);
    		pbuf->array[pbuf->tail] = ch;//存数据 
    		pbuf->tail = (pbuf->tail + 1) % BufLength;//修改指针 
    		pbuf->IsEmpty = 1;//修改状态 
    		time_t t=time(NULL);
    		struct tm* ptm = localtime(&t);
    		//GetSystemTime(&tm);
    		printf("\nProducerID:%6d, puts data:%c\tbuffer:\t%c\t%c\t%c\t%d时%d分%d秒",
    			(int)GetCurrentProcessId(),
    			ch,
    			pbuf->array[0], 
    			pbuf->array[1], 
    			pbuf->array[2],
    			ptm->tm_hour,
    			ptm->tm_min,
    			ptm->tm_sec);
    		fflush(stdout);
    		V(MUTEX);//释放缓冲区使用权 
    		V(FULL);
    	}
    	//解除映射
    	UnmapViewOfFile(pfile);
    	pfile = NULL;
    	CloseHandle(pfile);
    	//关闭信号量
    	CloseHandle(MUTEX);
    	CloseHandle(EMPTY);
    	CloseHandle(FULL);
    }
    ///消费者 
    void Customer() {
    	//打开信号量
    	HANDLE MUTEX = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "mymutex");
    	//HANDLE MUTEX = CreateMutex(NULL,FALSE,"mymutex");
    	HANDLE EMPTY = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "myempty");
    	HANDLE FULL = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "myfull");
    
    	//打开共享内存区,并加载到当前进程地址空间
    	HANDLE hmap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, filemapping_name);
    	LPVOID pfile = MapViewOfFile(
    		hmap,
    		FILE_MAP_ALL_ACCESS,
    		0,
    		0,
    		0);
    	struct BUF* pbuf = reinterpret_cast<struct BUF*>(pfile);
        //读数据(取产品) 
    	for (int i = 0; i < 6; i++) {
    		P(FULL);
    		//sleep
    		Sleep(GetRandomSleep());
    		P(MUTEX);
    		char ch = pbuf->array[pbuf->head];
    		pbuf->array[pbuf->head] = ' ';//将缓冲置空 
    		pbuf->head = (pbuf->head + 1) % BufLength;//修改指针 
    		pbuf->IsEmpty = (pbuf->head == pbuf->tail);//修改状态 
    		time_t t=time(NULL);
    		struct tm* ptm = localtime(&t);
    		printf("\nCustomerID:%6d, gets data:%c\tbuffer:\t%c\t%c\t%c\t%d时%d分%d秒",
    			(int)GetCurrentProcessId(),
    			ch,
    			pbuf->array[0], 
    			pbuf->array[1], 
    			pbuf->array[2],
    			ptm->tm_hour,
    			ptm->tm_min,
    			ptm->tm_sec);
    		fflush(stdout);
    		V(EMPTY);
    		//ReleaseMutex(MUTEX);
    		V(MUTEX);//释放缓冲区使用权 
    	}
    	//解除映射
    	UnmapViewOfFile(pfile);
    	pfile = NULL;
    	CloseHandle(pfile);
    	//关闭信号量
    	CloseHandle(MUTEX);
    	CloseHandle(EMPTY);
    	CloseHandle(FULL);
    	
    }
    
    int main(int argc, char* argv[]) {
    	if (argc > 1) {
    		int id = atoi(argv[1]);
    		if (id < 0) {
    			printf("maybe child process error!\n");
    			exit(-1);
    		}
    		else if (id < 2) {
    			Producer();
    		}
    		else if (id < 5) {
    			Customer();
    		}
    	}
    
    	else {
    		ParentProc();
    	}
    	return 0;
    }
    

    Linux下的实验代码

    #include<sys/types.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<time.h>
    #include<sys/wait.h>
    #include<sys/shm.h>
    #include<sys/sem.h>
    #include<string.h>
    #define MUTEX 0
    #define EMPTY 1
    #define FULL 2
    #define Length 3
    #define WriteProc 2
    #define ReadProc 3
    #define W_P_Redo 6
    #define R_P_Redo 4
    #define SEM_KEY 300
    
    void P(int sem_id, int sem_num) {
    	struct sembuf xx;
    	xx.sem_num = sem_num;
    	xx.sem_op = -1;
    	xx.sem_flg = 0;
    	semop(sem_id, &xx, 1);
    }
    
    void V(int sem_id, int sem_num) {
    	struct sembuf xx;
    	xx.sem_num = sem_num;
    	xx.sem_op = 1;
    	xx.sem_flg = 0;
    	semop(sem_id, &xx, 1);
    }
    
    struct ShrBuf {
    	int array[3];
    	int head;
    	int tail;
    	int IsEmpty;
    };
    
    //随机停留一定时间
    int GetRandomWait() {
    	//srand((unsigned)(getpid() + time(NULL)));
    	return rand() % 5;
    }
    
    //随机取10以内的整数存入
    int GetRandomInput() {
    	srand((unsigned)(getpid() + time(NULL)));
    	return (rand() % 100+1);
    }
    
    int main() {
    	int sem_id, shm_id, pid, pid2;//信号量id 
    	int i, rc;
    	struct ShrBuf *MyBuf;
    	time_t cur;
    	struct tm *p;
       //init semget
    	if ((sem_id = semget(SEM_KEY, 3, IPC_CREAT | 0600)) < 0) {
    		printf("main's semget is error\n");
    		exit(-1);
    	}
    	semctl(sem_id, MUTEX, SETVAL, 1);
    	semctl(sem_id, EMPTY, SETVAL, Length);
    	semctl(sem_id, FULL, SETVAL, 0);
    
    	//创建共享内存
    	if ((shm_id = shmget(IPC_PRIVATE, 24, 0600)) < 0) {
    			printf("error on shget\n");
    			exit(-1);
    	}
    	if ((MyBuf = shmat(shm_id, 0, 0)) == (void*)-1) {
    		printf("error on shmat\n");
    		exit(-1);
    	}
    	MyBuf->head = 0;
    	MyBuf->tail = 0;
    	MyBuf->IsEmpty = 1;
    	memset(MyBuf->array,0,sizeof(MyBuf->array));
    
    	for (int k = 0; k < WriteProc; k++) {
    		if ((pid = fork()) < 0) {
    			printf("error on fork\n");
    			exit(-1);
    		}
    		if (pid == 0) {
    			//把共享内存加载到进程地址空间
    			if ((MyBuf = shmat(shm_id, 0, 0)) == (void*)-1) {
    				printf("error on shmat\n");
    				exit(-1);
    			}
    			//6次存操作
    			for (i = 0; i < W_P_Redo; i++) {
    				P(sem_id, EMPTY);
    				P(sem_id, MUTEX);
    				sleep(GetRandomWait());
    				MyBuf->array[MyBuf->tail] = GetRandomInput();
    				int num = MyBuf->array[MyBuf->tail];
    				MyBuf->tail = (MyBuf->tail + 1) % Length;
    				MyBuf->IsEmpty = 0;
    				cur = time(NULL);
    				p = localtime(&cur);
    				printf("%d时%d分%d秒\t", p->tm_hour, p->tm_min, p->tm_sec);
    				printf("producerID:%d    puts data:%d \tbuffer:\t%d\t%d\t%d\n", getpid(),num,MyBuf->array[0],MyBuf->array[1],MyBuf->array[2]);
    				V(sem_id, MUTEX);
    				V(sem_id, FULL);
    			}
    			shmdt(MyBuf);
    			exit(0);
    		}
    	}
    
    	for (int k = 0; k < ReadProc; k++) {
    		if ((pid = fork()) < 0) {
    			printf("error on fork\n");
    			exit(-1);
    		}
    		if (pid == 0) {
    			//把共享内存加载到进程地址空间
    			if ((MyBuf = shmat(shm_id, 0, 0)) == (void*)-1) {
    				printf("error on shmat\n");
    				exit(-1);
    			}
    			//4次读操做
    			for (i = 0; i < R_P_Redo; i++) {
    				P(sem_id, FULL);
    				P(sem_id, MUTEX);
    				sleep(GetRandomWait());				
    				int num = MyBuf->array[MyBuf->head];
    				MyBuf->array[MyBuf->head] = 0;
    				MyBuf->head = (MyBuf->head + 1) % Length;
    				MyBuf->IsEmpty = (MyBuf->head == MyBuf->tail);
    				cur = time(NULL);
    				p = localtime(&cur);
    				printf("%d时%d分%d秒\t", p->tm_hour, p->tm_min, p->tm_sec);
    				printf("customerID:%d    gets data:%d \tbuffer:\t%d\t%d\t%d\n", getpid(), num, MyBuf->array[0], MyBuf->array[1], MyBuf->array[2]);
    				V(sem_id, MUTEX);
    				V(sem_id, EMPTY);
    			}
    			shmdt(MyBuf);
    			exit(0);
    		}
    	}
    
    	while (wait(0) != -1);
    	shmdt(MyBuf);
    	semctl(sem_id, IPC_RMID, 0);
    	//semctl(sem_id, IPC_RMID, 0);
    	//semctl(sem_id, IPC_RMID, 0);
    	shmctl(shm_id, IPC_RMID, 0);
    	exit(0);
    }
    
    展开全文
  • 每个进程需要有访问SQL Server相关文件和系统注册表权限,为了能让SQL Server服务在操作系统中正常启动和运行,就需要指定SQL Server服务帐户,所以服务帐户指Windows操作系统中帐户。 图3-9 ...
  • //用MPI_Comm_rank 获得进程的rank,该rank值为到p-1间的整数,相当于进程的ID MPI_Comm_rank(MPI_COMM_WORLD, &myid); //用MPI_Comm_size 获得进程个数 int MPI_Comm_size(MPI_Comm comm, int *size); MPI_Comm_...
  • 6.1 进程的内部机理 289 数据结构 289 内核变量 297 性能计数器 297 有关的函数 298 6.2 CreateProcess的流程 300 阶段1:打开将要被执行的映像 302 阶段2:创建Windows执行体进程对象 304 阶段3:创建初始线程,...
  • 用户模式转储堆(UMDH)工具Umdh.exe分析了给定进程的Microsoft Windows堆内存分配。 特征: 快照创建 报告制作 报告检视 过滤以查看报告 排序以查看报告 摘要信息 不同的数据表示形式(过滤的纯文本,列表,通话...
  • //用MPI_Comm_rank 获得进程的rank,该rank值为到p-1间的整数,相当于进程的ID MPI_Comm_rank(MPI_COMM_WORLD, &myid); //用MPI_Comm_size 获得进程个数 int MPI_Comm_size(MPI_Comm comm, int *size); MPI_Comm_...
  • LINGO软件学习

    2009-08-08 22:36:50
    当你在windows下开始运行LINGO系统时,会得到类似下面一个窗口: 外层是主框架窗口,包含了所有菜单命令和工具条,其它所有窗口将被包含在主窗口之下。在主窗口内标题为LINGO ...
  •  第3章 有米之炊——数据库的创建与删除  3.1 创建数据库  3.1.1 数据库创建概述  3.1.2 solaris下用dbca创建数据库  3.1.3 diy——solaris下手工创建数据库  3.1.4 数据库创建疑难解析  3.2 删除...
  •  第3章 有米之炊——数据库的创建与删除  3.1 创建数据库  3.1.1 数据库创建概述  3.1.2 solaris下用dbca创建数据库  3.1.3 diy——solaris下手工创建数据库  3.1.4 数据库创建疑难解析  3.2 删除...
  • 在分析了成千上万计算机和用户报告的基础上,创建了这个对计算机和系统无任何副作用配置方案,将为每台计算机提供大幅性能提升。最大性能:此配置方案以世界各地配置反馈为基础,并适当添加了一些实验性优化...
  • copy …: 拷贝文件,除支持模拟Linux文件系统内部文件拷贝外,还支持host文件系统与模拟Linux文件系统间文件拷贝,host文件系统文件命名为…,如:将windows下D:盘文件\data\sample\test.txt文件拷贝到模拟...
  • 3.5 一个创建并发进程的例子 20 3.5.1 一个顺序执行的C实例 20 3.5.2 程序的开发版本 21 3.5.3 时间分片 22 3.5.4 单线程的进程 23 3.5.5 使各进程分离 23 3.6 执行新的代码 24 3.7 上下文切换和协议软件...
  • 4.6 格式化SQL*Plus输出以及创建报告的命令 93 4.6.1 BREAK命令 93 4.6.2 COLUMN命令 94 4.6.3 COMPUTE命令 94 4.6.4 REPFOOTER命令 94 4.6.5 REPHEADER命令 94 4.6.6 BTITLE和TTITLE命令 94 4.7 ...
  • WinstarNssmMiner首先启动svchost.exe进程并向其植入代码,然后将该进程的属性设置为CriticalProcess。由于计算机将其视为关键进程,因此一旦强制结束该进程,计算机就会蓝屏。 如何预防挖矿劫持? 如果遵循这些...

空空如也

空空如也

1 2
收藏数 22
精华内容 8
关键字:

windows进程的创建实验报告