操作系统实验报告 并发程序设计
- 格式:doc
- 大小:246.00 KB
- 文档页数:6
实验4 操作系统综合实验----Linux并发程序设计(实验时间:90分钟)一、背景知识:管道是Linux中最常用的进程间通信IPC(inter process communication)机制。
利用管道时,一个进程的输出可成为另外一个进程的输入。
当输入输出的数据量特别大时,这种IPC机制非常有用。
可以想象,如果没有管道机制,而必须利用文件传递大量数据时,会造成许多空间和时间上的浪费。
在Linux中,通过将两个file结构指向同一个临时的索引节点,而索引节点又指向同一个物理页而实现管道。
管道被定义为一对分开的文件描述符:一个输入和一个输出。
我们可以从管道的一端写入然后从管道的另一端读取。
如果定义管道的函数pipe使管道成功建立的话,pipe就会返回0,同时生成两个文件描述符。
例如,如果两个文件描述符是数组myPipe[2],则myPipe[2]将会包括两个新的代表管道的输入(myPipe[1])和对管道的输出(myPipe[0])的文件描述符。
函数dup允许我们复制一个文件描述符。
我们向函数传递一个已经存在了的描述符,然后它返回了一个和前面那个一模一样的新的描述符。
这意味着两个描述符共享相同的结构。
程序调用fork创建一个进程,称为子进程。
调用的进程称为父进程。
子进程继承了父进程的几乎所有的属性,当然也具有跟父进程不同的属性。
调用fork会返回两次。
返回值= = 0,代表子进程;返回值> 0 ,代表父进程。
返回值< 0 ,代表error。
fork正确返回后,在父、子进程中可以直接写代码或者用execl系列函数执行应用程序。
如果是用execl函数执行应用程序,且调用execl成功,那么进程会一直阻塞在execl处,执行完后直接退出进程,而不会再执行execl以后的代码。
二、实验目的1) 通过在Linux进程之间进行的通信实例来学习并发程序设计的方法。
2) 通过Linux进程通信的程序设计与实现,进一步熟悉操作系统的进程概念,理解Linux 进程管理概念。
操作系统实验报告线程并发拷贝程序文档编制序号:[KKIDT-LLE0828-LLETD298-POI08]操作系统:线程(进程)并发拷贝程序附录一:程序代码#include<>#include<sys/>#include<sys/>#include<>#include<>#include<>#include<>#define PSIZE 4096 /*管道文件的大小*/#define BSIZE 128 /*默认缓冲区的大小*/#define NOFILE 20 /*u_ofile3575表可分配的个数*/#define NFILE 20 /*file表可分配的个数*/#define NPIPE 20 /*pipecb3575可分配的个数*//*进程的u_file表*/int u_ofile3575[NOFILE];/*模拟file表*/struct{char f_flag;/*读写标志,'w'表示写,'r'表示读*/int f_count;/*表示此表项的状态,=0表示此表项没被使用,可分配;=1表示此表项在被使用,不可再分配*/int f_inode;/*对应的pipecb3575表下标*/long f_offset;/*读写指针,当前已读或已写个数*/}file[NFILE];/*管道控制块*/struct{char *p_addr;/*管道文件基地址*/int p_size;/*管道文件大小,PSIZE*/int p_count;/*=2表示读写都在被进行,=1表示在被读或被写,=0表示管道没被使用,可分配*/}pipecb3575[NPIPE];/*模拟管道文件*/char *pfile;/*管道的写入写出端*/int fd[2];/*锁机制,实现互斥*/pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;/*进程间通信,实现同步*/pthread_cond_t rflag = PTHREAD_COND_INITIALIZER;/*读信号量*/pthread_cond_t wflag = PTHREAD_COND_INITIALIZER;/*写信号量*//*线程创建函数只能传一个参数,用结构体来封装所有参数*/struct arg_set{char *fname; /*文件名*/int f; /*传递fdp[]*/};/*u_ofile3575表初始化*/int u_ofile_init3575(){printf("init the u_ofile3575\n");int i;for(i=0;i<NOFILE;i++)u_ofile3575[i] = -1;u_ofile3575[0]=0;u_ofile3575[1]=0;u_ofile3575[2]=0;return 0;}/*创建管道*/int pipe_simulate3575(int a[]){printf("start to create a pipe\n");int i;for(i=0;i<NOFILE;i++){if(u_ofile3575[i]==-1){a[0] = i;/*读*/u_ofile3575[i] = 0;/*读端*/break;}}for(i;i<NOFILE;i++){if(u_ofile3575[i]==-1){a[1] = i;/*写*/u_ofile3575[i] = 1;/*写端*/break;}}if(i>=NOFILE){printf("u_ofile3575分配失败,failure to create a pipe\n");return -2;}pfile = (char *)malloc(PSIZE*sizeof(char));/*申请模拟管道用的内存空间*/ if(pfile==NULL)/*申请可能不成功*/{printf("failure to create a pipe\n");return -1;}for(i=0;i<NFILE;i++){if(file[i].f_count!=1){file[i].f_flag = 'r';/*读标志*/_inode = 0;/*读对应pipecb3575表下标*/file[i].f_count = 1;/*file[0]这个表项在被使用,不可再分配*/ file[i].f_offset = 0;/*读指针*/u_ofile3575[a[0]] = i;/*读端*/break;}}for(i=0;i<NFILE;i++){if(file[i].f_count!=1){file[i].f_flag = 'w';/*写标志*/_inode = 0;/*写对应pipecb3575控制块下标*/file[i].f_count = 1;/*file[1]这个表项在被使用,不可再分配*/ file[i].f_offset = 0;/*写指针*/u_ofile3575[a[1]] = i;/*写端*/break;}}if(i>=NFILE){printf("failure to create a pipe\n");return -1;}for(i=0;i<NPIPE;i++){if(pipecb3575[i].p_count==0){pipecb3575[i].p_addr = pfile;/*给管道文件基地址赋值*/pipecb3575[i].p_size = PSIZE;/*管道文件大小*/pipecb3575[i].p_count = 2;/*读写都在进行,此pipecb3575表项不可再分*/file[u_ofile3575[a[0]]].f_inode = i;file[u_ofile3575[a[1]]].f_inode = i;break;}}if(i>=NPIPE){printf("failure to create a pipe\n");return -1;}printf("Secceed create the pipe\n");return 0;/*分配成功*/}/*关闭管道*/int close_simulate3575(int a[]){printf("start to close the pipe you have created\n");char *p;int i;for(i=0;i<2;i++){p=pipecb3575[file[u_ofile3575[a[i]]].f_inode].p_addr;/*if(p!=NULL)free(p); _inode].p_count = 0; /*管道控制块计数清零*/file[u_ofile3575[a[i]]].f_count = 0; /*file表项计数清零*/u_ofile3575[a[i]] = -1; /*u_ofile3575表项清空*/a[i] = -1; /*fdp[]清空*/}printf("secceed close the pipe\n");return 0;}/*写管道*/int numwrite_simulate3575;int write_simulate3575(int fd,char *ca,int n)_inode;/*读管道对应的pipecb3575表的下标*/int n1 = n;/*一次应该写入的字符个数*/int wstart = 0;/*计数器,写入字符个数*/int i = 0;for(i;i<NFILE;i++)/*寻找写管道对应的读管道的读端*/{if((file[i].f_flag=='r')&&(file[i].f_inode==pf)){r = i;break;}else{continue;}}printf("----add the lock\n");pthread_mutex_lock(&lock);/*互斥锁,相当于进入临界区*/offr = file[r].f_offset;/*赋值读指针*/offw = file[w].f_offset;/*赋值写指针*/if((offw+n1-PSIZE)>offr)/*不能一次写完*/{if(pipecb3575[pf].p_count==0)/*对文件的复制操作已进行结束,管道文件被释放*/ {printf("对文件的复制操作已进行结束,管道文件被释放\n");return 0;}else{m = PSIZE+offr-offw;/*最多可写入数*/for(wstart=0;wstart<m;wstart++){*(pipecb3575[pf].p_addr+offw%PSIZE) = *ca;ca++;offw++;}file[w].f_offset = offw;/*重定位写指针位置*/n1 = n1-m;/*剩余需要读的字符个数*/printf("weak up the read thread , pipe is readable\n");pthread_cond_signal(&rflag);/*唤醒读线程,管道可读*/printf("write thread is blocked and under the statement of waiting\n"); pthread_cond_wait(&wflag,&lock);/*写线程封锁等待*/}}/*一次性可将ca中内容全部写入管道*/offr = file[r].f_offset;offw = file[w].f_offset;for(wstart=0;wstart<n1;wstart++){/*printf("%d\n",pipecb3575[pf].p_addr);*/*(pipecb3575[pf].p_addr+offw%PSIZE) = *ca;/*printf("%d\n",wstart);*/ca++;offw++;}file[w].f_offset = offw;pthread_cond_signal(&rflag);printf("----release the lock\n");pthread_mutex_unlock(&lock);printf("Secceed (memory---->pipe)input data in memory *ca into pipe\n"); return n;/*返回写入字符个数*/}/*读管道*/int numread_simulate3575;_inode;/*读管道对应的pipecb3575表的下标*/ int rstart = 0;/*计数器,读出字符个数*/int i = 0;for(i;i<NFILE;i++)/*寻找读管道对应的读管道的端*/{if((file[i].f_flag=='w')&&(file[i].f_inode==pf)){w = i;break;}else{continue;}}printf("----add the lock\n");pthread_mutex_lock(&lock);/*互斥锁,相当于进入临界区*/offr = file[r].f_offset;/*赋值读指针*/offw = file[w].f_offset;/*赋值写指针*/if(offr==offw)/*管道空,无内容可读*/{printf("pipe is empty , nothing to output\n");if(pipecb3575[pf].p_count==1)/*写端关闭*/{pipecb3575[pf].p_count--;/*文件的复制以完成,释放管道文件的空间*/ printf("the write point is closed,the copy of the file is finished\n");return 0;}else{printf("weak up the write thread , make the pipe writable\n");pthread_cond_signal(&wflag);/*唤醒写线程,管道可写*/printf("read thread is blocked and under the statement of waiting\n");pthread_cond_wait(&rflag,&lock);/*读线程封锁等待*/}}offr = file[r].f_offset;offw = file[w].f_offset;m = n<=(offw-offr)n:(offw-offr);/*得到可读字符个数*/for(rstart=0;rstart<m;rstart++){*ca = *(pipecb3575[pf].p_addr+offr%PSIZE);ca++;offr++;}file[r].f_offset = offr;/*重定位读指针位置*/printf("weak the write thread , make the pipe writable\n");pthread_cond_signal(&wflag);printf("----release the lock\n");pthread_mutex_unlock(&lock);printf("Secceed (pipe---->memory)output data from the pipe into memory *ca\n"); return m;}/*线程调用,读源文件,写管道*/void *pwrite3575(void *a)_inode].p_count--;/*文件已读完,关闭管道写端*/}else{perror(args->fname);/*打开源文件可能不成功*/return NULL;}printf("Secceed (file---->pipe)input data from the original file into pipe\n");return NULL;}/*线程调用,写目标文件,读管道*/void *pread3575(void *a)name=argv[1];/*源文件名*/args[0].f=fd[1];/*管道文件写端*/args[1].fname=argv[2];/*目标文件名*/args[1].f=fd[0];/*管道文件读端*/pthread_create(&t,NULL,pwrite3575,(void *)&args[0]);/*创建子线程,写管道*/ pread3575((void *)&args[1]);/*主线程调用,读管道*/pthread_join(t,NULL);/*等待写线程结束*/close_simulate3575(fd);printf("\nnum of read_simulate3575 = %d\n",numread_simulate3575);printf("num of write_simulate3575 = %d\n",numwrite_simulate3575);return 0;}附录二(实验结果):ls -l总用量 2379-rw-r--r-- 1 john john 50 2012-12-17 18:06 3575-rw-r--r-- 1 john john 36864 2012-07-15 07:53-rw-r--r-- 1 john john 95441 2012-08-27 22:16-rw-r--r-- 1 john john 2083969 2010-11-08 13:07-rw-r--r-- 1 john john 84835 2012-07-11 13:22-rw-r--r-- 1 john john 73728 2012-12-17 16:48-rw-r--r-- 1 john john 19924 2012-07-03 18:51-rw-r--r-- 1 john john 0 2012-12-17 22:49 sta1-rw-r--r-- 1 john john 11563 2012-12-17 22:46./tc 3575 a3575 > copy1./tc 3575./tc./tc./tc./tc > copy2./tcls -l总用量 5163-rw-r--r-- 1 john john 50 2012-12-17 18:06 3575-rw-r--r-- 1 john john 36864 2012-07-15 07:53-rw-r--r-- 1 john john 95441 2012-08-27 22:16-rw-r--r-- 1 john john 2083969 2010-11-08 13:07-rw-r--r-- 1 john john 84835 2012-07-11 13:22-rw-r--r-- 1 john john 73728 2012-12-17 16:48-rw-r--r-- 1 john john 19924 2012-07-03 18:51-rwxr-xr-x 1 john john 50 2012-12-17 22:51 a3575 -rwxr-xr-x 1 john john 36864 2012-12-17 22:51-rwxr-xr-x 1 john john 95441 2012-12-17 22:53-rwxr-xr-x 1 john john 2083969 2012-12-17 22:54-rwxr-xr-x 1 john john 84835 2012-12-17 22:56-rwxr-xr-x 1 john john 73728 2012-12-17 22:53-rwxr-xr-x 1 john john 19924 2012-12-17 22:55-rw-r--r-- 1 john john 1320 2012-12-17 22:51 copy1 -rw-r--r-- 1 john john 419294 2012-12-17 22:56 copy2 -rw-r--r-- 1 john john 690 2012-12-17 22:49 sta1-rw-r--r-- 1 john john 0 2012-12-17 22:57 sta2-rwxr-xr-x 1 john john 12256 2012-12-17 22:50 tc-rw-r--r-- 1 john john 11563 2012-12-17 22:46cat copy1init the u_ofilestart to create a pipeSecceed create the pipe(pipe---->file)output data from the pipe into new file(pipe---->memory)output data from pipe into memory *ca----add the lockpipe is empty , nothing to outputweak up the write thread , make the pipe writableread thread is blocked and under the statement of waiting(file---->pipe)input data from the original file into pipe(file---->memory)input data in original file into memory *ca Secceed (file---->memory)input data in original file into memory *ca (memory---->pipe)input data in memory *ca into pipe----add the lock----release the lockSecceed (memory---->pipe)input data in memory *ca into pipe Secceed (file---->pipe)input data from the original file into pipe weak the write thread , make the pipe writable----release the lockSecceed (pipe---->memory)output data from the pipe into memory *ca (memory---->file)output data in memory *ca into fileSecceed (memory---->file)output data from the pipe into new file (pipe---->memory)output data from pipe into memory *ca----add the lockpipe is empty , nothing to outputthe write point is closed,the copy of the file is finishedSecceed (pipe---->file)output data from the pipe into new filestart to close the pipe you have createdsecceed close the pipenum of readp = 2num of writep = 1。
篇一:操作系统实验报告完全版《计算机操作系统》实验报告班级:姓名:学号:实验一进程控制与描述一、实验目的通过对windows 2000编程,进一步熟悉操作系统的基本概念,较好地理解windows 2000的结构。
通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解windows 2000中进程的“一生”。
二、实验环境硬件环境:计算机一台,局域网环境;软件环境:windows 2000 professional、visual c++6.0企业版。
三、实验内容和步骤第一部分:程序1-1windows 2000 的gui 应用程序windows 2000 professional下的gui应用程序,使用visual c++编译器创建一个gui应用程序,代码中包括了winmain()方法,该方法gui类型的应用程序的标准入口点。
:: messagebox( null, “hello, windows 2000” , “greetings”,mb_ok) ;/* hinstance */ , /* hprevinstance */, /* lpcmdline */, /* ncmdshow */ )return(0) ; }在程序1-1的gui应用程序中,首先需要windows.h头文件,以便获得传送给winmain() 和messagebox() api函数的数据类型定义。
接着的pragma指令指示编译器/连接器找到user32.lib库文件并将其与产生的exe文件连接起来。
这样就可以运行简单的命令行命令cl msgbox.cpp来创建这一应用程序,如果没有pragma指令,则messagebox() api函数就成为未定义的了。
这一指令是visual studio c++ 编译器特有的。
接下来是winmain() 方法。
其中有四个由实际的低级入口点传递来的参数。
一、实验目的1. 理解线程的概念和并发编程的基本原理。
2. 掌握线程的创建、同步和通信方法。
3. 通过实验加深对线程并发编程的理解,提高编程能力。
二、实验环境1. 操作系统:Windows 102. 开发工具:Visual Studio 20193. 编程语言:C++三、实验内容本次实验主要涉及以下内容:1. 线程的创建与销毁2. 线程的同步与互斥3. 线程的通信4. 线程池的使用四、实验步骤1. 线程的创建与销毁(1)创建线程:使用C++11标准中的`std::thread`类创建线程。
```cpp#include <iostream>#include <thread>void threadFunction() {std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;}int main() {std::thread t1(threadFunction);std::thread t2(threadFunction);t1.join(); // 等待线程t1结束t2.join(); // 等待线程t2结束return 0;}```(2)销毁线程:线程会在任务执行完毕后自动销毁,无需手动销毁。
2. 线程的同步与互斥(1)互斥锁:使用`std::mutex`类实现线程间的互斥。
```cpp#include <iostream>#include <thread>#include <mutex>std::mutex mtx;void threadFunction() {mtx.lock();std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;mtx.unlock();}int main() {std::thread t1(threadFunction);t1.join();t2.join();return 0;}```(2)条件变量:使用`std::condition_variable`类实现线程间的条件同步。
一、实验目的1. 理解并发执行的概念和原理。
2. 掌握多线程编程的基本方法。
3. 学会使用同步机制解决并发编程中的竞争条件。
4. 分析并发程序的性能和效率。
二、实验环境1. 操作系统:Windows 102. 编程语言:Java3. 开发工具:Eclipse三、实验内容1. 创建一个简单的并发程序,实现两个线程同时执行。
2. 使用同步机制解决并发程序中的竞争条件。
3. 分析并发程序的性能和效率。
四、实验步骤1. 创建一个简单的并发程序(1)创建一个名为ConcurrentTest的类,该类继承自Thread类。
(2)在ConcurrentTest类的run方法中,打印出当前线程的名字。
(3)在主函数中,创建两个ConcurrentTest对象,分别命名为thread1和thread2。
(4)启动thread1和thread2线程。
(5)等待thread1和thread2线程执行完毕。
2. 使用同步机制解决并发程序中的竞争条件(1)创建一个名为Counter的类,该类包含一个私有变量count和一个静态同步方法add。
(2)在add方法中,增加count变量的值。
(3)在主函数中,创建一个Counter对象counter。
(4)创建两个线程,分别调用counter对象的add方法。
(5)启动两个线程,并等待它们执行完毕。
3. 分析并发程序的性能和效率(1)在主函数中,记录两个线程开始执行的时间。
(2)在主函数中,记录两个线程执行完毕的时间。
(3)计算两个线程执行所需的时间差。
五、实验结果与分析1. 实验结果(1)简单的并发程序在控制台中,可以看到thread1和thread2线程交替打印出它们的名字。
(2)使用同步机制解决竞争条件在控制台中,可以看到Counter对象的count变量值正确地增加了。
(3)分析并发程序的性能和效率thread1和thread2线程执行所需的时间差为0.01秒。
2. 实验分析(1)简单的并发程序通过创建两个线程,实现了两个任务同时执行。
并行程序设计实验报告实验报告:并行程序设计一、实验目的本实验旨在通过并行程序设计,提高程序的执行效率和性能,减少串行程序在处理大规模数据时出现的效率瓶颈。
二、实验原理1.任务划分:将大规模的任务划分成多个可并行执行的子任务。
2.任务分配:将各个子任务分配给不同的计算单元(如多线程、多进程、多核)进行处理。
3.任务合并:将各个子任务的计算结果进行合并,得到最终的结果。
三、实验内容本次实验主要涉及多线程编程和数据并行编程。
1.多线程编程多线程编程是指在一个单独的程序中同时运行多个线程,利用系统的多核资源来提高程序的执行效率。
多线程编程可以通过线程的创建、启动、运行和同步等操作来实现。
在本实验中,我们将使用C++编程语言,并利用其提供的多线程库来实现多线程编程。
具体步骤如下:(1)使用pthread库创建并启动多个线程。
(2)利用线程同步机制(如互斥锁、信号量等)保证线程的正确执行顺序和数据的正确性。
(3)通过编写并行程序,将大规模任务划分成多个子任务,并分配给不同的线程进行处理。
2.数据并行编程数据并行编程是指将大规模的数据划分成多个小块,并分配给多个计算单元(如GPU)进行并行处理。
每个计算单元都执行相同的计算操作,但操作的数据不同。
在本实验中,我们将使用CUDA平台进行数据并行编程。
(1)利用CUDA编程模型,将计算任务划分成多个线程块,并分配给不同的计算单元执行。
(2)通过编写并行程序,实现数据的划分和映射、任务的分配和协调。
四、实验结果经过多次实验,我们发现并行程序设计在处理大规模数据时能够显著提高程序的执行效率和性能。
相比于串行程序,多线程编程和数据并行编程分别提高了X%和Y%的执行速度。
同时,我们也发现在设计并行程序时,要考虑到数据的划分和映射、任务的分配和协调、线程的同步和通信等方面的问题。
这些问题对于程序的性能和正确性都有着重要的影响。
五、实验总结通过本次实验,我们了解到并行程序设计的基本原理和技术,以及它在优化程序性能和提高执行效率方面的重要作用。
《计算机操作系统》实验报告学号:姓名:学院:专业:计算机年级:班级:实验时间:2011-2012学年第一学期指导教师:实验一并发程序设计1.目的在单处理器环境下,实现多任务的核心是并发程序设计,进程的并发执行提高了CPU 的利用率,使得CPU与设备并行、设备与设备并行成为可能。
但并发执行也存在许多单任务中所没有的问题,其中之一是互斥和同步的控制。
2.要求在BACI环境下,对程序并发执行的实验:(1)没有控制时正确的程序执行的结果不正确;(2)BACI中PV操作的并发控制的实现。
要求:(1)熟悉教材中有关进程并发执行的内容。
(2)设计并实现:没有控制时正确的程序执行的结果不正确。
(3)BACI中PV操作的并发控制的实现。
(4)将课堂和习题中的同步、互斥的并发程序设计的习题的设计与实现。
3.题目堆栈内容为10 20 25 30 40,getspace为栈顶元素出栈,release(100)为100入栈。
getspace()和release(100)各执行一次的结果(堆栈)int stack[10];4.代码int top=4;void getspace(){int free;free=stack[top];top=top-1;cout<<"free="<<free<<endl;}void release(int ad){top=top+1;stack[top]=ad;}main(){int i;stack[0]=10;stack[1]=20;stack[2]=25;stack[3]=30;stack[4]=40;cobegin{getspace();release(100);}cout<<"top="<<top<<endl;for(i=0;i<=top;i++)cout<<"stack["<<i<<"]="<<stack[i]<<endl;}5.实验结果与分析图1 图2图1为正确结果,图2是错误结果。
《计算机操作系统》实验报告(一)学号:030702412 姓名:陈楠学院:数计学院专业:计算机类年级:2007级班级:4班实验时间:2009-2010学年第一学期指导教师:丁善镜、黄志华目录1、实验题目 (3)2、实验目的 (3)3、实验环境 (3)4、实验原理 (3)5、实验内容 (3)5.1、fork()函数机理探索 (3)5.1.1、原始代码段 (3)5.1.2、代码段修改一 (3)5.1.3、结论(1) (4)5.1.4、fock()函数原理 (4)5.1.5、代码段修改二 (4)5.2、并发执行测猜测 (5)5.2.1、并发执行改进 (5)5.2.2、代码段修改(3) (5)5.3、小结 (6)1、实验题目2、实验目的掌握在程序中创建新进程的方法,观察并理解多道程序并发执行的现象。
3、实验环境Red Hat Enterprise Linux 54、实验原理fork():建立子进程。
子进程得到父进程地址空间的一个复制。
返回值:成功时,该函数被调用一次,但返回两次,fork()对子进程返回0,对父进程返回子进程标识符(非0值)。
不成功时对父进程返回-1,没有子进程。
5、实验内容分析一下程序运行时其输出结果有哪几种可能性,然后实际调试该程序观察其实际输出情况,比较两者的差异,分析其中的原因。
5.1、fork()函数机理探索5.1.1、原始代码段#include<stdio.h>#include<unistd.h>void main (void){ int x=5;if( fork( ) ){x+=30;printf (“%d\n”,x);}elseprintf(“%d\n”,x);printf(“%d\n”,x);}为了了解fork()函数的机理,了解fork()是从哪里开始重复执行代码,特将代码做了第一次修改5.1.2、代码段修改一#include<stdio.h>#include<unistd.h>图1 验证fork()函数原理 图2 验证是否共享内存 void main (void){ int x=5;printf(“start \n”);if( fork( ) ){x+=30;printf (“father %d \n”,x);}elseprintf(“child %d \n”,x);printf(“out%d \n”,x);}右侧为第一次修改后代码的输出(图1)5.1.3、 结论(1)fock() 对于该语句之前的程序段只执行一次fock() 对于该语句返回两次值,并针对两次返回值依次执行之后的语句5.1.4、 fock()函数原理 被fork 创建的新进程叫做自进程。
一、实验背景与目的随着计算机技术的发展,多任务处理和多进程并发已经成为操作系统设计中的基本要求。
为了更好地理解进程并发的基本原理和实现方法,我们进行了本次实验。
实验的目的是通过实践操作,加深对进程并发执行的理解,掌握进程创建、同步、互斥等基本概念,并学会使用相关系统调用实现进程的并发控制。
二、实验环境与工具实验环境:Windows 10操作系统,Visual Studio 2019开发环境。
实验工具:C++编程语言,WinAPI系统调用。
三、实验内容与步骤本次实验主要分为以下几个部分:1. 进程创建与并发执行- 使用CreateProcess函数创建多个进程,并观察它们的并发执行情况。
- 使用GetTickCount函数获取每个进程的执行时间,分析并发执行的效果。
2. 进程同步- 使用互斥锁(mutex)实现进程间的同步,确保同一时刻只有一个进程访问共享资源。
- 使用条件变量(condition variable)实现进程间的条件同步,实现生产者-消费者模型。
3. 进程互斥- 使用信号量(semaphore)实现进程间的互斥,避免资源竞争。
- 使用临界区(critical section)保护共享资源,防止多个进程同时访问。
4. 实验分析- 分析实验结果,总结进程并发、同步和互斥的原理和方法。
- 讨论实验中遇到的问题和解决方法。
四、实验代码示例以下是一个简单的实验代码示例,演示了使用互斥锁实现进程同步的过程:```cpp#include <windows.h>#include <iostream>using namespace std;// 全局互斥锁mutex mtx;void processFunction(){unique_lock<mutex> lock(mtx);cout << "Process " << GetCurrentProcessId() << " is running." << endl;lock.unlock();}int main(){// 创建两个进程CreateProcess(NULL, "process.exe", NULL, NULL, FALSE, 0, NULL, NULL, NULL, NULL);// 主进程继续执行cout << "Main process is running." << endl;return 0;}```五、实验结果与分析1. 进程创建与并发执行通过实验,我们观察到多个进程可以并发执行,并且每个进程的执行时间都会有所不同。
一、实验目的1. 理解多线程并发编程的基本概念和原理;2. 掌握Java多线程编程的基本方法和技巧;3. 学习线程同步机制,解决线程安全问题;4. 熟悉线程调度策略,提高程序性能。
二、实验环境1. 操作系统:Windows 102. 开发工具:IntelliJ IDEA3. JDK版本:1.8三、实验内容1. 线程创建与启动2. 线程同步与互斥3. 线程通信与协作4. 线程池与线程调度5. 线程局部变量与共享变量四、实验步骤及结果分析1. 线程创建与启动实验步骤:(1)创建一个继承自Thread类的子类;(2)重写run()方法,定义线程的执行逻辑;(3)创建Thread对象,并调用start()方法启动线程。
实验结果:成功创建并启动两个线程,分别执行各自的run()方法。
2. 线程同步与互斥实验步骤:(1)创建一个共享资源;(2)使用synchronized关键字声明同步方法或同步代码块;(3)在同步方法或同步代码块中访问共享资源。
实验结果:线程在访问共享资源时,能够保证互斥,防止数据不一致。
3. 线程通信与协作实验步骤:(1)使用wait()和notify()方法实现线程间的通信;(2)创建共享对象,作为线程间通信的媒介;(3)在等待线程中调用wait()方法,在通知线程中调用notify()方法。
实验结果:线程能够通过wait()和notify()方法实现通信与协作,完成特定任务。
4. 线程池与线程调度实验步骤:(1)使用Executors工厂方法创建线程池;(2)提交任务到线程池;(3)关闭线程池。
实验结果:线程池能够有效地管理线程,提高程序性能。
5. 线程局部变量与共享变量实验步骤:(1)创建线程局部变量;(2)创建共享变量;(3)在各个线程中访问和修改线程局部变量与共享变量。
实验结果:线程局部变量在各个线程中独立存在,不会相互干扰;共享变量在各个线程中共享,需要使用同步机制保证数据一致性。
《计算机操作系统》
实验报告(一)
学号:030702412 姓名:陈楠学院:数计学院专业:计算机类年级:2007级班级:4班
实验时间:2009-2010学年第一学期指导教师:丁善镜、黄志华
目录
1、实验题目 (3)
2、实验目的 (3)
3、实验环境 (3)
4、实验原理 (3)
5、实验内容 (3)
5.1、fork()函数机理探索 (3)
5.1.1、原始代码段 (3)
5.1.2、代码段修改一 (3)
5.1.3、结论(1) (4)
5.1.4、fock()函数原理 (4)
5.1.5、代码段修改二 (4)
5.2、并发执行测猜测 (5)
5.2.1、并发执行改进 (5)
5.2.2、代码段修改(3) (5)
5.3、小结 (6)
1、实验题目
2、实验目的
掌握在程序中创建新进程的方法,观察并理解多道程序并发执行的现象。
3、实验环境
Red Hat Enterprise Linux 5
4、实验原理
fork():建立子进程。
子进程得到父进程地址空间的一个复制。
返回值:成功时,该函数被调用一次,但返回两次,fork()对子进程返回0,对父进程返回子进程标识符(非0值)。
不成功时对父进程返回-1,没有子进程。
5、实验内容
分析一下程序运行时其输出结果有哪几种可能性,然后实际调试该程序观察其实际输出情况,比较两者的差异,分析其中的原因。
5.1、fork()函数机理探索
5.1.1、原始代码段
#include<stdio.h>
#include<unistd.h>
void main (void)
{ int x=5;
if( fork( ) )
{
x+=30;
printf (“%d\n”,x);
}
else
printf(“%d\n”,x);
printf(“%d\n”,x);
}
为了了解fork()函数的机理,了解fork()是从哪里开始重复执行代码,特将代码做了第一次修改
5.1.2、代码段修改一
#include<stdio.h>
#include<unistd.h>
图1 验证fork()函数原理 图2 验证是否共享内存 void main (void)
{ int x=5;printf(“start \n”);
if( fork( ) )
{
x+=30;
printf (“father %d \n”,x);
}
else
printf(“child %d \n”,x);
printf(“out%d \n”,x);
}
右侧为第一次修改后代码的输出(图1)
5.1.3、 结论(1)
fock() 对于该语句之前的程序段只执行一次
fock() 对于该语句返回两次值,并针对两次返回值依
次执行之后的语句
5.1.4、 fock()函数原理 被fork 创建的新进程叫做自进程。
fork 函数被调用一
次,却两次返回。
返回值唯一的区别是在子进程中返回0,而在父进程中返回子进程的pid 。
子进程和父进程都执行在fork 函数调用之后的代码,子进程是父进程的一个拷贝。
父进程的数据空间、堆栈空间都会给子进程一个拷贝,而不是共享这些内存。
5.1.5、 代码段修改二
为了验证父进程的数据空间、堆栈空间都会给子进程一个拷贝,而不是共享这些内存,特意修改了代码如下,输出如右侧(图2)
#include<stdio.h>
#include<unistd.h>
void main (void)
{ int x=5;printf(“start \n”);
if( fork( ) )
{
x+=30;
printf (“father %d \n”,x);
}
else {x+=20;
printf(“child %d \n”,x);}
printf(“out%d \n”,x);
}
修改后的代码能够体现子进程的空间和父进程的空间是不在同一个区域中,即不是共享同一内存
5.2、并发执行测猜测
我的理解是程序刚好在执行中切换进程执行,在编译原始代码段后因为执行时间持续较短,发生这种现象的概率较小。
在之前的解释中能够说明父进程和子进程的空间是独立,存在进程间切换的可能性,发生这种现象证明了这种猜测。
既然进程间会进行切换,那么所有执行的可能就有如下几种
start father 35 35(子进程创建失败)
start child 5 5 father 35 35(无切换)
start child 5 father 35 35 5(切换1次)
start child 5 father 35 5 35(切换2次)
start father 35 35 child 5 5(切换1次)
start father 35 child 5 5 35(切换2次)
start father 35 child 5 35 5(切换3次)
不可能出现的情况是
start 5 child 5 35 father 35
start 5 35 father 35 child 5
start 5 35 child 5 father 35
start 35 father 35 5 child 5
start 35 5 child 5 father 35
start 35 5 father 35 child 5
5.2.1、并发执行改进
在对源程序进行近半小时的重复执行后还没发现其他情况,我尝试的增加了一些冗余的执行代码来增加切换的几率。
增加了很多空执行的延时
for(int i=0 ;i<0XFFFFF;i++);
5.2.2、代码段修改(3)
#include<stdio.h>
#include<unistd.h>
#define delay for(int i=0 ;i<0XFFFFF;i++);
void main (void)
{ int x=5;printf(“start\n”);
if( fork( ) )
{
x+=30;delay;
printf (“father %d\n”,x); delay;
}
else {30;delay; printf(“child %d\n”,x); delay;}
delay; printf(“out%d\n”,x);
}
5.3、小结
我总共尝试出6种结果中的5种,最后一种由于要3次切换才能出现,概率太小了,未能尝试出。
当执行操作很少的时候,一般是顺序就执行完
当执行操作很多的时候,进程间的切换概率就很大
由于并发执行对于单个程序来说还是顺序执行的,所以不可能出现5 child 5 35 father 35等6种情况
验证出来的结果充分说明并发执行中的进程切换是随机而且是无序的,在执行操作持续时间约长的时候,出现的几率越高;但对于同一个程序来说是顺序执行的。