基于Linux的进程同步与通信的模拟2
- 格式:docx
- 大小:631.64 KB
- 文档页数:11
《Linux操作系统设计实践》实验二:进程通信实验目的:进一步了解和熟悉 Linux 支持的多种 IPC 机制,包括信号,管道,消息队列,信号量,共享内存。
实验环境: redhat实验内容:(1)进程间命名管道通信机制的使用:使用命名管道机制编写程序实现两个进程间的发送接收信息。
(2)进程间消息队列通信机制的使用:使用消息队列机制自行编制有一定长度的消息(1k 左右)的发送和接收程序。
(3)进程间共享存储区通信机制的使用:使用共享内存机制编制一个与上述(2)功能相同的程序。
并比较分析与其运行的快慢。
实验代码验证:(1).使用命名管道机制编写程序实现两个进程间的发送接收信息。
#include <stdio.h>#include <stdlib.h>#define FIFO_FILE "MYFIFO"int main(int argc, char *argv[]){FILE *fp;int i;if (argc<=1){printf("usage: %s <pathname>\n",argv[0]); exit(1);}if ((fp = fopen(FIFO_FILE, "w")) == NULL) {printf("open fifo failed. \n");exit(1);}for (i = 1; i < argc; i++){if (fputs(argv[i],fp) == EOF){printf("write fifo error. \n");exit(1);}if (fputs(" ",fp) == EOF){printf("write fifo error. \n"); exit(1);}}fclose(fp);return 0;}#include <stdio.h>#include <stdlib.h>#include <sys/stat.h>#include <unistd.h>#include <linux/stat.h>#define FIFO_FILE "MYFIFO"int main(){FILE *fp;char readbuf[80];if ((fp = fopen(FIFO_FILE, "r")) == NULL) {umask(0);mknod(FIFO_FILE, S_IFIFO | 0666, 0);}else{fclose(fp);}while (1){if ((fp = fopen(FIFO_FILE, "r")) == NULL) {printf("open fifo failed. \n");exit(1);}if (fgets(readbuf, 80, fp) != NULL){printf("Received string :%s \n", readbuf); fclose(fp);}else{if (ferror(fp)){printf("read fifo failed.\n");exit(1);}}}return 0;}实验结果:Server.c将client.c写入的字符输出。
linux进程间同步机制一、进程间同步的概念在多进程系统中,进程间的通信是必要的,但同时也要防止进程间的相互干扰和数据污染。
进程间的同步机制就是用于解决这一问题的机制,它通过控制进程间的执行顺序、共享资源访问等方式,确保进程间的正确协作。
1. 互斥锁(Mutex)互斥锁是一种常用的进程同步机制,用于保护共享资源,防止多个进程同时访问和修改共享资源,导致数据错误或不一致。
使用互斥锁时,进程需要先获取锁才能访问共享资源,释放锁后才能进行其他操作。
示例代码:```cpthread_mutex_t mutex;pthread_mutex_lock(&mutex);// 访问共享资源pthread_mutex_unlock(&mutex);```2. 信号量(Semaphore)信号量是一种计数器,用于控制对共享资源的访问。
它可以实现进程间的同步和互斥,确保在任何时刻只有一个进程可以访问共享资源。
示例代码:```c#define MAX_COUNT 5sem_t sem;sem_wait(&sem); // 等待信号量释放// 访问共享资源sem_post(&sem); // 释放信号量```3. 屏障(Barrier)屏障是一种用于同步进程的机制,用于确保所有进程在执行完一定操作后才能继续执行。
在多线程或多进程编程中,屏障可以用于确保所有线程或进程完成了某个阶段的准备工作后,再继续执行后续的操作。
示例代码:```cpthread_barrier_t barrier;pthread_barrier_wait(&barrier); // 等待所有线程或进程到达屏障位置```4. 管道(Pipe)和消息队列(Message Queue)管道和消息队列是用于进程间通信的机制,它们允许一个进程向另一个进程发送消息或数据。
通过管道和消息队列,进程间可以异步地交换数据,从而实现同步。
操作系统课程实验二年级2012 级专业计算机科学与技术(应用型)指导教师黄玲学号姓名实验二、Linux进程同步与通信一、关键问题1、实验目的理解进程的高级通信;理解通过信号量实现进程的同步。
2、实验环境Ubuntu 8.0或者以上,Eclipse集成开发环境3、实验内容3.1 观察Linux进程通信下面代码实现两个进程之间通过共享内存通信。
3.2 Linux进程同步实验要求:一个生产者向一个缓冲区发消息,每当发出一个消息后,要等待三个消费者都接收这条消息后,生产者才能发送新消息。
用信号量和P、V操作,写出他们同步工作的程序。
(提示:可以把缓冲区看作是三个缓冲块组成的缓冲区,生产者等到这三个缓冲块为空时,才发送新消息到这个缓冲区。
每个消费者从一个缓冲块取走数据。
)二、设计修改思路这是一个典型的生产者消费者问题可以基于信号灯解决,由于有3个消费者,一个生产者,共用同一个缓冲区,所以我们需要设置4个信号灯来控制4个进程的互斥。
一个生产者进程不断向修改变量(写入10次),三个消费者进程从缓冲区中读取数据。
创建了empty、full1、full2、full3四个信号量,供进程间同步访问临界区,同时建立两个虚存区:a用于生产者、消费者之间的共享数据,get 记录当前生产者进程和消费者进程的读写次数。
实现的算法如下:生产者进程{p(empty);p(empty);p(empty);发送消息v(full1);v(full2);v(full3);}消费者A进程{p(full1);接受消息v(empty);}消费者B、C进程和A进程类似三、实现修改的关键代码//为信号灯赋初值arg.val = 3;if(semctl(empty , 0 , SETV AL , arg) == -1) perror("semctl setval error"); arg.val = 0;if(semctl(full1 , 0 ,SETV AL , arg) == -1) perror("semctl setval error"); arg.val = 0;if(semctl(full2 , 0 ,SETV AL , arg) == -1) perror("setctl setval error"); arg.val = 0;if(semctl(full3 , 0 ,SETV AL , arg) == -1) perror("setctl setval error");//生产者进程if(fork() == 0 ) {int i=1;while (i<11){semop(empty , &P , 1); //申请对缓冲区的互斥操作semop(empty , &P , 1); //申请对缓冲区的互斥操作semop(empty , &P , 1); //申请对缓冲区的互斥操作*a = i; //放入数据到缓冲区printf("Producer %d\n", *a);semop(full1 , &V , 1); //释放信号量semop(full2 , &V , 1);semop(full3 , &V , 1);i++;}sleep(10);printf("Producer is over");exit(0);}else {//ConsumerA 进程if(fork()==0) {while(1){if(*get==30) break;semop(full1 , &P , 1); //申请数据printf("The ComsumerA Get Number %d\n", *a );(*get)++;semop(empty , &V , 1); //释放信号量sleep(1);}printf("ConsumerA is over");exit(0);}四、程序运行结果截图五、实验总结及进一步改善建议1、通过今天的实验操作,自己初步熟悉理解进程的高级通信;理解通过信号量实现进程的同步。
操作系统的进程间通信与同步机制操作系统是计算机系统中最核心的软件之一,负责管理和协调计算机硬件和软件资源的使用。
在多道程序设计环境下,操作系统需要管理多个同时运行的进程,而进程间的通信和同步是操作系统中的重要任务之一。
本文将探讨操作系统中的进程间通信和同步机制的基本概念、原理和应用。
一、进程间通信(Inter-process Communication,IPC)进程间通信是指不同进程之间交换信息的机制。
在操作系统中,进程间通信是实现进程协作和数据共享的基础。
常见的进程间通信方式包括管道、消息队列、信号量、共享内存等。
1. 管道(Pipe)管道是一种半双工的通信方式,将一个进程的输出连接到另一个进程的输入,实现进程之间的单向通信。
管道分为有名管道和无名管道,有名管道可以在不同进程之间共享,而无名管道只能在具有父子关系的进程之间使用。
2. 消息队列(Message Queue)消息队列是一种可以在不相关进程之间传递数据的通信机制,它通过将消息放入队列中,实现进程之间的异步通信。
消息队列可以实现进程之间的解耦,提高系统的可维护性和灵活性。
3. 信号量(Semaphore)信号量是一种用于进程间同步与互斥的机制,它可以用来解决临界区问题和进程同步问题。
信号量可以用来控制资源的访问顺序,避免竞争条件和死锁等问题。
4. 共享内存(Shared Memory)共享内存是一种高效的进程间通信方式,它允许多个进程直接访问同一块内存区域,从而实现数据的共享。
共享内存可以提高进程间通信的性能,但需要合理控制访问权限,防止数据的冲突和错误。
二、进程间同步(Process Synchronization)进程间同步是指多个进程之间按照特定的顺序执行,保证数据的一致性和正确性。
在多进程环境下,进程之间的并发执行可能导致竞争条件、死锁和资源冲突等问题,进程间同步机制可以解决这些问题。
1. 互斥锁(Mutex)互斥锁是一种二进制的同步机制,用于保护临界区,避免多个进程同时访问共享资源。
实验二 Linux进程通信一、实验目的1)了解有关Linux系统调用;2)学习有关Linux的进程创建,理解进程创建后两个并发进程的执行。
二、实验内容在Linux环境下,用C/C++语言编程,使用系统调用fork创建进程多个子进程。
(1) 调试并完成下列程序,完成实验要求:#include "stdio.h"#include "sys/types.h"#include "unistd.h"int main(){pid_t pid1;pid_t pid2;pid1 = fork();pid2 = fork();printf("pid1:%d, pid2:%d\n", pid1, pid2);}要求:A.请说出执行这个程序后,将一共运行几个进程。
B.观察运行结果,并给出分析与解释。
答:A.执行这个程序后,将一共运行4个进程。
A.运行结果如下:分析与解释:fork()函数能够建立子进程,且使得子进程得到父进程地址空的一个复制;而且,当创建成功时,如果是父进程则返回0,子进程则返回子进程的I D,但是,fork()创建的进程并不是从该程序开头开始执行,它只是和父进程一样继续执行后续代码,因此在之后的语句中,父子进程同时创建一个进程,就形成了四个进程,如图上所示。
所以,,在上面的截图中,第一次fork()函数时成功产生了父子进程pid分别为2775和0,第二次使用fork()函数时父子进程又各产生了一对父子进程父进程产生的父子进程的pid分别为2776和0,子进程产生的父子进程的pid分别为0和2777。
因为进程是并发的,他的调度我们无法干预,所以出现的结果并非都是一成不变的,执行多次后,输出的顺序有可能不一样。
(2)参考下面的相关程序实例,编写一个管道实验程序,实现两个进程之间的数据通信。
要求:父进程顺序写入若干个字符串,子进程顺序读出内容,并写入文件piple.txt,并显示出来。
实验报告学号_____ 姓名____ ___ 成绩实验二进程通信【实验目的和要求】1、了解进程通信的概念及方法;2、了解信号量、管道;3、掌握信号量、管道和命名管道编程方法。
【实验内容】1、利用命名管道实现单机QQ聊天;2、撰写实验报告;【实验原理】1、信号量(semaphore)是为那些访问相同资源的进程以及同一进程不同线程之间提供的一个同步机制。
它不是用于传输数据,而只是简单地协调对共享资源的访问。
信号量包含一个计数器,表示某个资源正在被访问和访问的次数,用来控制多进程对共享数据的访问。
一旦成功拥有了一个信号量,对它所能做的操作只有两种:请求和释放。
当执行释放操作时,系统将该信号值减1(如果小于零,则设置为零);当执行请求操作时,系统将该信号值加1,如果加1后的值大于设定的最大值,那么系统将会挂起处理进程,直到信号值小于最大值为止。
Tuxedo 用信号量来确保在某一时刻只有一个进程对某一块共享内存进程访问。
信号量配置太低会导致Tuxedo系统应用程序无法启动。
2、管道分为两种:管道和命名管道。
管道是UNIX系统IPC的最古老形式,并且所有的UNIX系统都提供这种通信机制。
可以在有亲缘关系(父子进程或者是兄弟进程之间)进行通信,管道的数据只能单向流动,如果想双向流动,必须创建两个管道。
管道应用的一个重大缺陷就是没有名字,因此只能用于亲缘进程之间的通信。
后来以管道为基础提出命名管道(named pipe,FIFO)的概念,该限制得到了克服。
FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。
这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。
值得注意的是,FIFO严格遵循先进先出(first in first out)规则,对管道及FIFO的读总是从开始处返回数据,对它们的写则是把数据添加到末尾。
实验三2_Linux进程间通信实验三 Linux进程间通信一、实验目的熟悉Linux下进程间通信机制,能够使用系统提供的各种通信机制实现并发进程间的数据交换。
二、实验题目分别使用Linux下的共享存储区、消息、管道等通信机制,编程实现并发进程之间的相互通信。
三、背景材料(一)需要用到的系统调用实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。
fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行;wait() 等待子进程执行完成;getpid() 获取当前进程id;shmget() 建立一个共享存储区;shmctl() 操纵一个共享存储区;shmat() 把一个共享存储区附接到进程内存空间;shmdt() 把一个已经附接的共享存储区从进程内存空间断开;msgget() 建立一个消息队列;msgctl() 操纵一个消息队列;msgsnd() 发送消息;msgrcv() 接收消息;signal() 设置对信号的处理方式或处理过程;pipe() 打开管道;lockf() 锁定一个文件。
(二)使用共享存储区的示例程序下面程序主要用来演示共享存储区的使用方法:首先要使用shmget得到共享存储区句柄(可以新建或连接已有的共享存储区,以关键字标识),然后使用shmat挂接到进程的存储空间(这样才能够访问),当使用完后,使用shmctl释放(shmctl 还可以完成一些其他功能)。
这种使用逻辑也适用于消息和信号量。
示例程序代码如下:#include <sys/types.h>#include <unistd.h>#include <stdio.h>#include <sys/ipc.h>#include <sys/shm.h>int main(void){int x, shmid;int *shmptr;if((shmid=shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT|0666)) < 0) printf("shmget error"), exit(1);//函数原型int shmget(key_t key,int size,int shmflg); 函数用于创建(或者获取)一key键值指定的共享内存对象,返回该对象的系统标识符:shmid;size 是创个由IPC_CREAT|0666,说明新建一个权限为0666的消息队列,建的共享内存的大小,id,若其中组用户、当前用户以及其他用户拥有读写的权限若成功则返回共享内存-1 出错则为if((shmptr=(int *)shmat(shmid, 0, 0)) == (int *)-1)printf("shmat error"), exit(1);// void *shmat(int shm_id,void *shm_addr,int shmflg); shm_id标识码,shm_addr连,shmflg标志位接到的地址printf("Input a initial value for *shmptr: ");scanf("%d", shmptr);while((x=fork())==-1);if(x==0) /* child run */{printf("When child runs, *shmptr=%d\n", *shmptr);printf("Input a value in child: ");scanf("%d", shmptr);printf("*shmptr=%d\n", *shmptr);}else /* parent run */{wait();printf("After child runs, in parent, *shmptr=%d\n", *shmptr);if ( shmctl(shmid, IPC_RMID, 0) < 0 )// shmctl()函数声明:int shmctl(int shmqid, int cmd, struct shmid_ds *buf);返回值:0 on success函数用于对已创建的共享内存对象进行查询、设值、删除等操作;这个函数-1 on error:和 msgget()函数十分相似,用法也相同。
实验六:Linux进程间通信(2)(4课时)实验目的:理解进程通信原理;掌握进程中信号量、共享内存、消息队列相关的函数的使用。
实验原理:Linux下进程通信相关函数除上次实验所用的几个还有:信号量信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。
要调用的第一个函数是semget,用以获得一个信号量ID。
int semget(key_t key, int nsems, int flag);key是IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。
nsems是该集合中的信号量数。
如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。
semctl函数用来对信号量进行操作。
int semctl(int semid, int semnum, int cmd, union semun arg);不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。
semop函数自动执行信号量集合上的操作数组。
int semop(int semid, struct sembuf semoparray[], size_t nops);semoparray是一个指针,它指向一个信号量操作数组。
nops规定该数组中操作的数量。
ftok原型如下:key_t ftok( char * fname, int id )fname就是指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。
共享内存共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。
通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。
中北大学
操作系统课程设计
说明书
学院、系:软件学院
专业:软件工程
学生姓名:高岩学号:0921010248 设计题目:基于Linux的进程同步与通信的模拟实现
起迄日期: 2011年12月22日- 2012年1月7日指导教师:贾美丽
2012 年 1月 7 日
1需求分析
该软件需要利用银行家算法判断系统是否处于安全状态,同时根据银行家算法得出的安全序列进行临界资源的分配。
软件设计的目的是要对输入的进程进行安全性判断模拟分配资源。
首先先创建进程:手动创建几个进程,都在界面上完成;其中包括每个进程已分配的资源、最大需求资源、系统剩余资源以及每个进程的执行时间。
实例中创建5个进程。
其次采用银行家算法,判断是否可以进行资源的分配。
最后,按照银行家算法得出的安全序列,对临界资源进行分配。
2总体设计
软件由两个模块组成,分别是银行家算法判断安全序列模块以及临界资源分配模块。
2.1银行家算法模块
本模块中有如下功能:初始化allocation矩阵,初始化max矩阵,初始化available
矩阵,初始化need矩阵,初始化finished矩阵,初始化进程执行时间,用银行家算法判断
是否安全,计算系统总资源数目,输出系统总资源数目。
其中设置了多个线性单链表结构存储矩阵中的数据,包括已分配allocation矩阵、最
大需求量max矩阵、可用资源数available矩阵、需要资源数need矩阵、安全路径path
矩阵、进程安全标示finish矩阵、进程运行时间ti矩阵。
银行家算法:
设进程i提出请求Request[n],则银行家算法按如下规则进行判断。
(1)如果Request[n]>Need[i,n],则报错返回。
(2)如果Request[n]>Available,则进程i进入等待资源状态,返回。
(3)假设进程i的申请已获批准,于是修改系统状态:
Available=Available-Request
Allocation=Allocation+Request
Need=Need-Request
(4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,
进程等待。
安全性检查
(1)设置两个工作向量Work=Available;Finish[M]=False
(2)从进程集合中找到一个满足下述条件的进程,
Finish [i]=False
Need<=Work
如找到,执行(3);否则,执行(4)
(3)设进程获得资源,可顺利执行,直至完成,从而释放资源。
Work=Work+Allocation
Finish=True
GO TO 2
(4)如所有的进程Finish[M]=true,则表示安全;否则系统不安全。
2.2临界资源分配模块
这个模块有如下功能:创建线程、模拟为线程分配资源。
在原来的基础上设置了两个可变长数组altemp、ntemp,分别存放当前进程的已分配资源和需要的资源量。
通过比较每类资源的需要资源量和系统可用的资源量之间大小关系,判断系统是否能给进程分配资源。
如果分配成功,则利用sleep函数模拟线程执行,然后释放线程利用的系统资源,执行完毕。
如果分配失败,则调用sleep函数等待一段时间,再进行循环判断,直到判断出资源量能够满足进程的要求。
运行时,首先先新建一个终端,然后用gcc命令编译和链接,运行程序,
编译命令为:gcc–o process process.c–lpthread如下
图1
创建三类资源,五个进程,并写出每个进程的已分配资源、最大需求量、系统现在可用的资源、进程执行时间。
如下。
图2(已分配资源)
图3(最大需求量)
图4(系统可用资源)
图5(进程执行时间)
输出系统所有资源量,用银行家算法判断是否安全,如果安全得出安全序列,分配资源如下。
图6(执行过程)
图7(执行过程)
程序流程图
图 8
3.详细设计
我负责的是银行家算法判断是否安全,以下是我做的代码。
int main()
{
flag=0;
processtest=0;
for(temp=0;temp<proNum;temp++)
{
alloctemp=allochead;
needtemp=needhead;
finishtemp=finihead;
worktemp=workhead;
for(i=0;i<proNum;i++)
{
worktemp1=worktemp;
if(finishtemp->stat==0)
{
for(j=0;j<sourceTyNum;j++,needtemp=needtemp->next,worktemp=worktemp->next)
if(needtemp->value<=worktemp->value)
processtest++;
if(processtest==sourceTyNum)
{
for(j=0;j<sourceTyNum;j++)
{
worktemp1->value+=alloctemp->value;
worktemp1=worktemp1->next;
alloctemp=alloctemp->next;
}
if(flag==0)
{
pathhead=path1=path2=(struct
path*)malloc(pathlen);
path1->next=path2->next=NULL;
path1->value=i;
flag++;
}
else
{
path2=(struct path*)malloc(pathlen);
path2->value=i;
if(flag==1)
{
pathhead->next=path2;
flag++;
}
path1->next=path2;
path1=path2;
}
finishtemp->stat=1;
}
else
{
for(t=0;t<sourceTyNum;t++)
alloctemp=alloctemp->next;
finishtemp->stat=0;
}
}
else
for(t=0;t<sourceTyNum;t++)
{
needtemp=needtemp->next;
alloctemp=alloctemp->next;
}
processtest=0;
worktemp=workhead;
finishtemp=finishtemp->next;
}
}
path2->next=NULL;
finishtemp=finihead;
for(temp=0;temp<proNum;temp++)
{
if(finishtemp->stat==0)
{
printf("\n not safe\n");
exit(0);
}
finishtemp=finishtemp->next;
}
pathtemp=pathhead;
printf("\n safe \n");
printf("\n path: \n");
do
{
printf("p%d ",pathtemp->value);
}
while(pathtemp=pathtemp->next);
printf("\n");
printf("----------------------------------------\n");
}
4.心得体会
银行家算法是避免死锁的一种重要方法,通过编写一个简单的银行家算法程序,加深了解有关资源申请、避免死锁等概念,并体会和了解死锁和避免死锁的具体实施方法。
死锁的产生,必须同时满足四个条件,即一个资源每次只能由一个进程;第二个为等待条件,即一个进程请求资源不能满足时,它必须等待,但它仍继续保持已得到的所有其他资源;第三个为非剥夺条件,即在出现死锁的系统中一定有不可剥夺使用的资源;第四个为循环等待条件,系统中存在若干个循环等待的进程,即其中每一个进程分别等待它前一个进程所持有的资源。
防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。
在编程的过程中,我们尝试着用if条件语句编写可变长的创建线程语句,可是不能成功,所以我们改用只能创建五个线程的语句编写,调用系统创建线程函数pthread_create。
利用sleep函数休眠30秒钟后结束。
在linux中sleep函数中的参数是以秒为单位的,而不是像在windows中以毫秒为单位的。