实验二_进程间通信

合集下载

《Linux操作系统设计实践》实验二:进程通信

《Linux操作系统设计实践》实验二:进程通信

《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写入的字符输出。

实验二.进程间通信

实验二.进程间通信

实验题目:进程间通信实验目的:理解进程间通信、进程同步与互斥实验内容:有一个名为DATA的文本文件,其中只含有一行文本,该文本内容为一个ASCII码格式的正整数。

现在有多个进程,它们都要访问DATA文件,具体操作是读出整数、对其加1、然后将整数存回到文件之中。

现在要求编写程序,能够正确地实现这一操作。

显然,如果对上述的多个进程同时操作一个整数的行为不加以控制,则会产生竞争条件,导致错误的运行结果。

(一)请使用文件上锁的办法,编写C语言程序,解决竞争条件问题。

(二)请使用信号量解决上述问题作为参考,下面给出了一个不上锁的程序#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <sys/types.h>#include <unistd.h>#include <stdarg.h>#include <string.h>int main(void){int num, fd,i;FILE *f;char buff[100];fd = open("data", O_RDWR);f = (FILE *)fdopen(fd, "r+");for(i=1;i<=100000;i++){lseek(fd, 0, SEEK_SET);fscanf(f, "%d", &num);sprintf(buff, "%d\n", num + 1);lseek(fd, 0, SEEK_SET);write(fd, buff,strlen(buff));}fclose(f);return 0;}信号量请使用信号量机制代替文件锁,实现文件的互斥访问。

作为参考,给同学们提供一个演示程序,这个程序中,父进程创建子进程之后,父子进程并发执行。

操作系统实验2 进程通信

操作系统实验2 进程通信

仲恺农业工程学院实验报告纸
计算机科学与工程(院、系)网络工程专业班组《操作系统》
学号姓名实验日期2011-5-24 教师评定
实验二进程通信
一.实验目的:
通过实验使学生进一步了解进程之间的各种通信方式、基本原理和不同操作系统中具体的实现。

基本能达到下列具体的目标:
1、理解进程消息通信的概念,如何实现两个创建进程之间的数据传递。

2、理解进程共享变量的进程通信。

二.实验内容:
1.选择Window或Linux,并选择该操作系统中一种进程通信的方式。

2.查找该进程通信的API使用方式,设计出一个合适的应用程序。

3.采用java语言实现该应用程序。

三.实验步骤:
这里可以实现两个人在同一局域网的聊天,程序可以自动扫描上线的用户。

如果需要与其中的用户进行交谈,则只需双击用户列表,输入对方IP,便可在下面的输入框内输入信息进行发送。

这个是三个人之间进行交谈,但是只能够实现相互两个人之间进行通信,方式跟上面的差不多。

但是三个人都可以看到发送的信息。

本机上的交谈信息
其他用户上的信息
四.实验心得:
这次的实验一开始是在课上有简单的弄了下,后来跟计算机网络的课程设计题目——聊天软件设计差不多一致,于是便这个当做了课题来进行课程设计,通过实现简单的聊天程序来完成进程间的通信。

这次的实验采用基于Java的程序设计技术,要用到很多Java socket的知识。

刚开始也得从网上找一些代码来看和了解一些新的知识。

操作系统实验 进程与进程通信

操作系统实验 进程与进程通信

计算机工程学院实验报告课程名称:操作系统实验班级实验成绩:指导教师:姓名:实验项目名称:进程与进程通信学号:上机实践日期:2009-11-13实验项目编号:实验二组号:上机实践时间:2学时一、目的1、深刻理解进程和线程的概念;2、掌握线程和进程的差别以及与之相适应的通信方式;3、掌握在Linux环境下创建进程: fork()的应用;4、了解用fork()创建进程、以及整个程序的运行过程;5、掌握多进程的程序设计与进程之间通信的方法;6、掌握共享内存、信号灯集实现进程通信的方法;7、理解、掌握Linux下文件系统,以及其安装与卸载过程。

二、实验内容1、在Linux环境下,用fork()创建多个进程,分别运行不同的函数;2、一部分进程代表读者,一部分进程代表写者;用共享内存、信号灯集机制实现各个读者、写者进程之间的通信;3、掌握shmget()、shmat()、shmctl()以及semget()、semctl()、semop()等函数在进程通信中的使用方法;4、用信号灯加PV操作实现进程间的互斥与同步。

三、实验环境1、操作系统:Red Hat Linux四、实验原理1、枚举数据类型,在信号灯集初始化时使用。

该结构在sys/sem.h中没有定义,必须程序设计者自行定义。

其中:semid—已经创建的信号灯集ID,sn—操作的元素的IDunion semun{int val;struct semid_ds *buf;ushort *array;};2、对信号灯集中的某个元素进行P操作。

首先要定义一个sembuf 类型的变量(该类型已经在sys/sem.h中预定义,可以直接引用),然后对该变量的各个元素进行赋值,注意进行P操作,主要是sem_op元素赋值为 -1。

其中:semid—已经创建的信号灯集ID,sn—操作的元素的IDvoid down(int semid,int sn){/* define P operating*/struct sembuf op;op.sem_num=sn;op.sem_op=-1;op.sem_flg=0;semop(semid,&op,1);}3、对信号灯集中的某个元素进行V操作。

实验二 进程间通讯 实验报告

实验二 进程间通讯 实验报告

Linux信号量实验报告一、实验目的深入理解操作系统中进程间通讯的本质二、实验方法利用UNIX/LINUX所提供的信号量、共享存储器、PV操作、文件锁等机制实现进程间的信息共享、进程间的互斥与同步。

三、实验任务编写一个C语言程序,该程序将一个存放了一个整数的文本文件内容执行加1操作一百万次,同时启动这个程序的多个副本,观察执行结果是否正确。

利用信号量机制对文件上锁,重新运行观察结果是否正确。

四、实验要点信号量概念、PV操作五、实验内容5.1 信号量概念信号量是一种确保特定代码段(临界区)只能被一个进程或者线程调用的一种机制。

在实际应用中,信号量由一种特殊的数据结构——信号量集所管理。

在使用信号量以前,需要创建一个信号量集,使用完成以后需要销毁信号量集。

信号量集的作用相当于一个信号量的计数器。

P操作是向信号量集获取一个信号量的操作,如果此时信号量集中有信号量,则会对信号量中的计数器进行更改(大部分情况下是计数器减一);如果此时信号量集中没有可用信号量(即计数器为0时),则执行P操作的线程或者进程则会被阻塞,直到信号量集中拥有可用的信号量(即计数器不为0)。

具体关系可用下图表示:5.2 信号量的初始化信号量的初始化需要用到两个函数(semget和semctl)和一个联合体结构(该实验中我们只需要用联合体结构中的val值,所以我只定义val变量)。

Semget系统调用的定义如下:int semget(key_t key, int nsems, int semflg)semget这个系统调用的作用是返回一个与key参数相关联的一个信号量集标识,semflg 参数会控制函数的行为;如果semflg为IPC_CREAT或者IPC_PRIVATE,则函数会创建一个拥有nsems个信号量的信号量集;如果semflg的值为IPC_CREAT | IPC_EXCL,在信号量集已经存在的情况下会发生错误。

实验中使用的获得信号量集标识的代码为:int sem_id = semget((key_t)2234, 0, 0);if (sem_id == -1){sem_id = semget((key_t)2234, 1, 0666 | IPC_CREAT);if (!init(sem_id)) return -1;}上述代码的第一行,nsems和semflg参数均为0,目的是只获得与2234这个值相关联的信号量集的标识;如果这个信号量集已经存在,则返回这个信号量集的标识;否则返回-1 下面就对获得的sem_id进行判断,如果值为-1,即信号量集还没有被创建,需要创建一个信号量集。

进程实验-进程间通信(管道、消息、共享内存、软中断)

进程实验-进程间通信(管道、消息、共享内存、软中断)

进程实验3 Linux 进程间通信一、软中断信号的处理,实现同一用户的各进程之间的通信。

●相关的系统调用⏹kill(pid ,sig):发送信号⏹signal(sig, func):指定进程对信号sig的处理行为是调用函数func。

●程序清单#include <unistd.h>#include <stdio.h>#include <signal.h>void waiting();void stop();int wait_mark;main(){int p1,p2;while((p1=fork())==-1);if(p1>0){while((p2=fork())==-1);if(p2>0){ printf("parent\n");/*父进程在此完成某个操作、或接收到用户从键盘输入的特殊按键命令后发出下面的信号。

这里省略。

*/kill(p1,16);kill(p2,17);wait(0);wait(0);printf("parent process id killed! \n");exit(0);}else/* p2==0*/{printf("p2\n");wait_mark=1;signal(17,stop);waiting();printf("child process 2 is killed by parent! \n");exit(0);}}else/*p1==0*/{printf("p1\n");wait_mark=1;signal(16,stop);waiting();printf("child process 1 is kelled by parent! \n");exit(0);}}void waiting(){while(wait_mark!=0);}void stop(){wait_mark=0;}●输入并运行此程序,分析程序的运行结果。

试验二进程通信Linux试验报告

试验二进程通信Linux试验报告

实验报告学号姓名成绩__________实验二进程通信【实验目的和要求】1、了解进程通信的概念及方法;2、了解信号量、管道;3、掌握信号量、管道和命名管道编程方法。

【实验内容】1、利用命名管道实现单机QQ聊天;2、撰写实验报告;【实验原理】1、信号量(semaphore)是为那些访问相同资源的进程以及同一进程不同线程之间提供的一个同步机制。

它不是用于传输数据,而只是简单地协调对共享资源的访问。

信号量包含一个计数器,表示某个资源正在被访问和访问的次数,用来控制多进程对共享数据的访问。

一旦成功拥有了一个信号量,对它所能做的操作只有两种:请求和释放。

当执行释放操作时,系统将该信号值减1(如果小于零,则设置为零);当执行请求操作时,系统将该信号值加1,如果加1后的值大于设定的最大值,那么系统将会挂起处理进程,直到信号值小于最大值为止。

Tuxedo 用信号量来确保在某一时刻只有一个进程对某一块共享内存进程访问。

信号量配置太低会导致Tuxedo系统应用程序无法启动。

2、管道分为两种:管道和命名管道。

管道是UNIX系统IPC的最古老形式,并且所有的UNIX系统都提供这种通信机制。

可以在有亲缘关系(父子进程或者是兄弟进程之间)进行通信,管道的数据只能单向流动,如果想双向流动,必须创建两个管道。

管道应用的一个重大缺陷就是没有名字,因此只能用于亲缘进程之间的通信。

后来以管道为基础提出命名管道(namedpipe,FIFO)的概念,该限制得到了克服。

FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。

这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。

值得注意的是,FIFO严格遵循先进先出(first in first out)规则,对管道及FIFO的读总是从开始处返回数据,对它们的写则是把数据添加到末尾。

进程通讯管理实验报告(3篇)

进程通讯管理实验报告(3篇)

第1篇一、实验目的1. 理解进程通信的概念和原理;2. 掌握进程通信的常用机制和方法;3. 能够使用进程通信机制实现进程间的数据交换和同步;4. 增强对操作系统进程管理模块的理解。

二、实验环境1. 操作系统:Linux2. 编程语言:C3. 开发环境:GCC三、实验内容1. 进程间通信的管道机制2. 进程间通信的信号量机制3. 进程间通信的共享内存机制4. 进程间通信的消息队列机制四、实验步骤1. 管道机制(1)创建管道:使用pipe()函数创建管道,将管道文件描述符存储在两个变量中,分别用于读和写。

(2)创建进程:使用fork()函数创建子进程,实现父子进程间的通信。

(3)管道读写:在父进程中,使用read()函数读取子进程写入的数据;在子进程中,使用write()函数将数据写入管道。

(4)关闭管道:在管道读写结束后,关闭对应的管道文件描述符。

2. 信号量机制(1)创建信号量:使用sem_open()函数创建信号量,并初始化为1。

(2)获取信号量:使用sem_wait()函数获取信号量,实现进程同步。

(3)释放信号量:使用sem_post()函数释放信号量,实现进程同步。

(4)关闭信号量:使用sem_close()函数关闭信号量。

3. 共享内存机制(1)创建共享内存:使用mmap()函数创建共享内存区域,并初始化数据。

(2)映射共享内存:在父进程和子进程中,使用mmap()函数映射共享内存区域。

(3)读写共享内存:在父进程和子进程中,通过指针访问共享内存区域,实现数据交换。

(4)解除映射:在管道读写结束后,使用munmap()函数解除映射。

4. 消息队列机制(1)创建消息队列:使用msgget()函数创建消息队列,并初始化消息队列属性。

(2)发送消息:使用msgsnd()函数向消息队列发送消息。

(3)接收消息:使用msgrcv()函数从消息队列接收消息。

(4)删除消息队列:使用msgctl()函数删除消息队列。

操作系统实验报告2-进程间通信

操作系统实验报告2-进程间通信
*pint=pid;
msg.mtype=1;
msgsnd(msgqid,&msg,sizeof(int),0);
msgrcv(msgqid,&msg,256,pid,0);
printf("client:receive from pid %d\n",*pint);
exit(0);
}
#include<sys/types.h>
信号本质:信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。进程用kill( )向一个进程或一组进程发送一个信号;当一个进程要进入或退出一个低优先级睡眠状态时,或一个进程即将从核心态返回用户态时,核心都要检查该进程是否已收到软中断
signal(17,stop); /*接收到软中断信号17,转stop*/
waiting( );
lockf(stdout,1,0);
printf("Child process 2 is killed by parent!\n");
lockf(stdout,0,0);
exit(0);
}
}
else
{
wait_mark=1;
signal(16,stop); /*接收到软中断信号16,转stop*/
waiting( );
lockf(stdout,1,0);

实验二、进程间通信

实验二、进程间通信

MINIX进程间通信一、消息矩阵的定义在/kernel/glo.h处加入矩阵定义:。

#include <minix/com.h> /*为了引用NR_TASKS*/。

EXTERN int matrix[NR_TASKS + NR_PROCS][NR_TASKS + NR_PROCS];。

二、在/usr/kernel/proc.c文件中添加消息截获功能在CopyMess语句之后加入对数组matrix的增量语句/*===========================================================================** mini_send **===========================================================================*/ PRIVATE int mini_send(caller_ptr, dst, m_ptr, flags)register struct proc *caller_ptr; /* who is trying to send a message? */int dst; /* to whom is message being sent? */message *m_ptr; /* pointer to message buffer */unsigned flags; /* system call flags */{/* Send a message from 'caller_ptr' to 'dst'. If 'dst' is blocked waiting* for this message, copy the message to it and unblock 'dst'. If 'dst' is* not waiting at all, or is waiting for another source, queue 'caller_ptr'.*/register struct proc *dst_ptr = proc_addr(dst);register struct proc **xpp;/* Check if 'dst' is blocked waiting for this message. The destination's* SENDING flag may be set when its SENDREC call blocked while sending.*/if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == RECEIVING &&(dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) {/* Destination is indeed waiting for this message. */CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr,dst_ptr->p_messbuf);matrix[caller_ptr->p_nr + NR_TASKS] [dst_ptr->p_nr + NR_TASKS]++;/*F4*/if ((dst_ptr->p_rts_flags &= ~RECEIVING) == 0) enqueue(dst_ptr);} else if ( ! (flags & NON_BLOCKING)) {/* Destination is not waiting. Block and dequeue caller. */caller_ptr->p_messbuf = m_ptr;if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr);caller_ptr->p_rts_flags |= SENDING;caller_ptr->p_sendto = dst;/* Process is now blocked. Put in on the destination's queue. */xpp = &dst_ptr->p_caller_q; /* find end of list */while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link;*xpp = caller_ptr; /* add caller to end */caller_ptr->p_q_link = NIL_PROC; /* mark new end of list */} else {return(ENOTREADY);}return(OK);}/*===========================================================================* * mini_receive **===========================================================================*/ PRIVATE int mini_receive(caller_ptr, src, m_ptr, flags)register struct proc *caller_ptr; /* process trying to get message */int src; /* which message source is wanted */message *m_ptr; /* pointer to message buffer */unsigned flags; /* system call flags */{/* A process or task wants to get a message. If a message is already queued,* acquire it and deblock the sender. If no message from the desired source* is available block the caller, unless the flags don't allow blocking.*/register struct proc **xpp;register struct notification **ntf_q_pp;message m;int bit_nr;sys_map_t *map;bitchunk_t *chunk;int i, src_id, src_proc_nr;/* Check to see if a message from desired source is already available.* The caller's SENDING flag may be set if SENDREC couldn't send. If it is* set, the process should be blocked.*/if (!(caller_ptr->p_rts_flags & SENDING)) {/* Check if there are pending notifications, except for SENDREC. */if (! (priv(caller_ptr)->s_flags & SENDREC_BUSY)) {map = &priv(caller_ptr)->s_notify_pending;for (chunk=&map->chunk[0]; chunk<&map->chunk[NR_SYS_CHUNKS]; chunk++) {/* Find a pending notification from the requested source. */if (! *chunk) continue; /* no bits in chunk */for (i=0; ! (*chunk & (1<<i)); ++i) {} /* look up the bit */src_id = (chunk - &map->chunk[0]) * BITCHUNK_BITS + i;if (src_id >= NR_SYS_PROCS) break; /* out of range */src_proc_nr = id_to_nr(src_id); /* get source proc */#if DEBUG_ENABLE_IPC_WARNINGSif(src_proc_nr == NONE) {kprintf("mini_receive: sending notify from NONE\n");}#endifif (src!=ANY && src!=src_proc_nr) continue; /* source not ok */*chunk &= ~(1 << i); /* no longer pending *//* Found a suitable source, deliver the notification message. */BuildMess(&m, src_proc_nr, caller_ptr); /* assemble message */CopyMess(src_proc_nr, proc_addr(HARDWARE), &m, caller_ptr, m_ptr);matrix[src_proc_nr + NR_TASKS][caller_ptr->p_nr + NR_TASKS]++;/*F4*/ return(OK); /* report success */}}/* Check caller queue. Use pointer pointers to keep code simple. */xpp = &caller_ptr->p_caller_q;while (*xpp != NIL_PROC) {if (src == ANY || src == proc_nr(*xpp)) {/* Found acceptable message. Copy it and update status. */CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr);matrix[(*xpp)->p_nr + NR_TASKS] [caller_ptr->p_nr + NR_TASKS]++;/*F4*/ if (((*xpp)->p_rts_flags &= ~SENDING) == 0) enqueue(*xpp);*xpp = (*xpp)->p_q_link; /* remove from queue */return(OK); /* report success */}xpp = &(*xpp)->p_q_link; /* proceed to next */}}/* No suitable message is available or the caller couldn't send in SENDREC.* Block the process trying to receive, unless the flags tell otherwise.*/if ( ! (flags & NON_BLOCKING)) {caller_ptr->p_getfrom = src;caller_ptr->p_messbuf = m_ptr;if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr);caller_ptr->p_rts_flags |= RECEIVING;return(OK);} else {return(ENOTREADY);}}/*===========================================================================* * mini_notify **===========================================================================*/ PRIVATE int mini_notify(caller_ptr, dst)register struct proc *caller_ptr; /* sender of the notification */int dst; /* which process to notify */{register struct proc *dst_ptr = proc_addr(dst);int src_id; /* source id for late delivery */message m; /* the notification message *//* Check to see if target is blocked waiting for this message. A process* can be both sending and receiving during a SENDREC system call.*/if ((dst_ptr->p_rts_flags & (RECEIVING|SENDING)) == RECEIVING &&! (priv(dst_ptr)->s_flags & SENDREC_BUSY) &&(dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) {/* Destination is indeed waiting for a message. Assemble a notification* message and deliver it. Copy from pseudo-source HARDWARE, since the* message is in the kernel's address space.*/BuildMess(&m, proc_nr(caller_ptr), dst_ptr);CopyMess(proc_nr(caller_ptr), proc_addr(HARDWARE), &m,dst_ptr, dst_ptr->p_messbuf);matrix[proc_nr(caller_ptr) + NR_TASKS][dst_ptr->p_nr + NR_TASKS]++; /*F4*/dst_ptr->p_rts_flags &= ~RECEIVING; /* deblock destination */if (dst_ptr->p_rts_flags == 0) enqueue(dst_ptr);return(OK);}/* Destination is not ready to receive the notification. Add it to the* bit map with pending notifications. Note the indirectness: the system id* instead of the process number is used in the pending bit map.*/src_id = priv(caller_ptr)->s_id;set_sys_bit(priv(dst_ptr)->s_notify_pending, src_id);return(OK);}三、在/servers/is/dmp_kernel.c处加入函数定义void disp_mess(void){int i,j,r,d;static int oldi = 0;static int oldj = 0;int result;int matrix[NR_TASKS + NR_PROCS] [NR_TASKS + NR_PROCS];result = sys_getmatrix(&matrix);if(result != OK ){report("IS", "warning: couldn't get copy of matrix", result);return;}/*else printf("Wellcome, F4 pressed.\n");*/result = sys_getproctab(proc);if(result != OK){report("IS", "warnng: couldn't get copy of proc tab", result);return;}r=0;for(i=oldi; i<NR_PROCS; i++, oldj=0){if(!isemptyp(&proc[i])){d = 0;for(j = oldj; j < NR_PROCS; j++)if(matrix[i][j] > 0 /* && !isemptyp(&proc[j]) */ ){d=1;break;}if ( d )printf("\n%s -> ", proc[i].p_name);for(j=oldj; j<NR_PROCS; j++){if(matrix[i][j]>0 /* && !isemptyp(&proc[j]) */ ){printf("(%s, %d) ", proc[j].p_name, matrix[i][j]);r++;if(r>=22){printf("\n--more--\r");oldi = j == NR_PROCS - 1 ? i+1 : i;oldj = j == NR_PROCS - 1 ? 0 : j+1;return;}}}}}oldi=oldj=0;}四、修改/servers/is/dmp.c,截住F4功能键{F4, disp_mess, "display message passing"},五、为系统任务添加新功能,即在/kernel/system/do_getinfo.c中加入如下程序段case GET_MA TRIX: {length = sizeof(matrix);src_phys = vir2phys(matrix);break;}六、在/usr/include/minix/syslib.h中加入sys_getmatrix 的声明#define sys_getmatrix(dst) sys_getinfo(GET_MA TRIX, dst, 0, 0, 0)七、在/usr/include/minix/com.h文件中为SYS_GETINFO 服务加入GET_MATRIX子功能#define I_REQUEST m7_i3 /* what info to get */# define GET_KINFO 0 /* get kernel information structure */# define GET_IMAGE 1 /* get system image table */# define GET_PROCTAB 2 /* get kernel process table */# define GET_RANDOMNESS 3 /* get randomness buffer */# define GET_MONPARAMS 4 /* get monitor parameters */# define GET_KENV 5 /* get kernel environment string */# define GET_IRQHOOKS 6 /* get the IRQ table */# define GET_KMESSAGES 7 /* get kernel messages */# define GET_PRIVTAB 8 /* get kernel privileges table */# define GET_KADDRESSES 9 /* get various kernel addresses */# define GET_SCHEDINFO 10 /* get scheduling queues */# define GET_PROC 11 /* get process slot if given process */# define GET_MACHINE 12 /* get machine information */# define GET_LOCKTIMING 13 /* get lock()/unlock() latency timing */# define GET_BIOSBUFFER 14 /* get a buffer for BIOS calls */# define GET_MATRIX 15 /* for F4 disp_mess */。

进程之间的通信实验

进程之间的通信实验

实验:进程之间的通信管道1.Pipe函数与进程通信下面实验为使用管道进行父子进程间通信。

程序首先判断参数是否合法,因为输入的字符将从父进程通过发送到子进程中。

然后,调用pipe函数创建父子进程用于通信的管道。

使用fork函数创建子进程时,子进程会获得与父进程相同的资源,其中包括文件描述符信息。

因此,调用fork函数须在pipe函数调用前。

当父子进程通过管道进行通信时,files[1]为用于数据写入的文件描述符.因此,在子进程中,要读取管道中的数据可以调用read函数,而读取得文件描述符为files[0]。

对于父进程而言,写入数据需要调用write 函数,要写入的文件描述为files[1]。

#include <stdio.h>#include <unistd.h>int main(int argc,char* argv[]){int f_des[2];int pid;char msg[BUFSIZ];if(argc!=2){printf("Usage: %s message\n",argv[0]);return 1;}if(pipe(f_des)==-1){perror("cannot create the IPC pipe");return 1;}pid=fork();if(pid==-1){perror("cannot create new process");return 1;}else if(pid==0){close(f_des[1]);if(read(f_des[0],msg,BUFSIZ)==-1){perror("child process cannot read data from pipe");return 1;}elseprintf("in child process, receive message: %s\n",msg);_exit(0);}else {close(f_des[0]);if(write(f_des[1],argv[1],strlen(argv[1]))==-1){perror("parent process cannot write data to pipe");return 1;}elseprintf("in parent process, send message: %s\n",argv[1]);wait(NULL);_exit(0);}return 0;}2. Shell管道重订向的实现实现了在SHELL中的两个命令的组合。

进程通信 实验分析

进程通信  实验分析

大作业二进程通信姓名学号:班级:日期:2015年7月1 需求说明(1)掌握进程间通讯的编程方法;(2)加深对进程并发执行的理解;(3)学习利用消息队列和共享存储区实现进程通信的方法。

2 设计说明(1)接收键盘输入进程负责接收用户的键盘输入,并以适当的方式将由键盘获得的数据交给其它进程处理。

(2)显示进程负责全部数据显示任务,包括键盘输入数据的显示和提示信息的显示。

(3)分发数据进程将键盘输入的数据分为3类,即字母、数字和其它,并分别将字母写入文件letter.txt中,数字写入文件number.txt中,除字母和数字外其它数据丢弃。

2.1 结构设计1、程序能以适当的方式提示用户输入数据;2、提示用户有数据被丢弃;3、全部的显示任务必须由显示进程完成;4、整个程序能够连续处理多组输入数据,直到用户输入“quit”字符串,整个程序结束;5、进一步要求:同时采用共享存储区和消息2种方法实现进程之间的通信,并比较这2种通信方法的利弊。

2.2 功能设计1、利用fork()函数创建2个子进程,用一个父进程和两个子进程完成上面的三个实验任务,用子进程1实现分发数据任务,子进程2实现接受键盘输入任务,父进程实现全部的显示任务。

2、同时通过共享存储区和消息队列两种进程通讯方式实现上面三个进程之间的同步和互斥。

3、利用while()循环、kill()函数和signal()函数实现连续多组数据输入。

3 测试和使用说明比较消息队列和共享存储区在消息通信机制中的数据传输的时间和性能:由于两种机制实现的机理和用处都不一样,难以直接进行时间上的比较。

如果比较其性能,应更加全面地分析。

3.1 使用说明2、有关字符数组初始化函数的使用:在本实验中频繁使用了memset()函数,且第二个参数均为’\0’,是为了将每次从键盘输入的字符串都能存到一个空的字符数组中,以防止字符的重复和覆盖。

3、在本程序中,需要合理安排父进程和2个子进程的任务,由父进程来负责显示任务是最合理和最简单的情况,因为父进程与子进程在某些方面是共享的,无需另外启用消息通信机制。

实验二 进程通信实验

实验二  进程通信实验

实验二进程通信实验1.实验内容设有二元函数f(x,y) = f(x) + f(y)其中:f(x) = f(x-1) * x (x >1)f(x)=1 (x=1)f(y) = f(y-1) + f(y-2) (y> 2)f(y)=1 (y=1,2)请编程建立3 个并发协作进程,它们分别完成f(x,y)、f(x)、f(y) 2.实验代码(附件中)3.在当前目录中建立Makefile文件5.使用make命令编译连接生成可执行文件pipetest6.编译成功后执行pipetest7实验要求(1)进程协作:系统中一个进程能影响其他进程或被其他进程所影响,那么该进程是协作的。

在该实验中,子进程x和子进程y 需要来自父进程传输的自变量x和y,而父进程的完成需要来自子进程x和子进程y的计算结果f(x)和f(y),所以父进程和两个子进程是协作的。

协作进程需要一种进程间通信机制来允许进程相互交换数据与信息。

(2)在实验中采用管道通信,管道是一个单向通信信道,如果进程间要进行双向通信,通常需要定义两个管道。

管道通过系统调用read(), write()函数进行读写操作。

管道通信机制的机理:一种半双工的通信机制,两个进程可以通过管道一个在管道一端向管道发送其输出,给另一进程可以在管道的另一端从管道得到其输入;另外,管道只能用来在具有公共祖先的两个进程之间通信。

管道通信遵循先进先出的原理,并且数据只能被读取一次,当此段数据被读取后,马上会从数据中消失,当管道中的数据被读取后,管道为空,一个随后的read()调用将默认的被阻塞,并等待某些数据写入。

通常会先调用pipe(),然后调用fork(),从而创建从父进程到子进程的IPC通道.。

实验二 进程间通信

实验二  进程间通信

实验二进程间通信1. 实验目的Linux系统的进程通信机构(IPC)允许在任意进程间大批量地交换数据。

本实验的目的是了解和熟悉Linux支持的消息通信机制、共享存储区和管道通信机制。

2. 实验准备阅读Linux系统的msg.c、sem.c和shm.c等源码文件,熟悉Linux的三种通信机制。

3. 实验内容⑴消息的创建、发送和接收。

要求使用系统调用msgget ( ),msgsnd ( ),msgrev ( )及msgctl ( )编制长度为1K的消息的发送和接收程序,运行程序并观察结果,说明控制消息队列系统调用msgctl ( )在此起什么作用。

⑵共享存储区的创建、附接和断接。

要求使用系统调用shmget ( ),shmat ( ),sgmdt ( ),shmctl ( ),编制一个与上述功能相同的程序。

⑶进程的管道通信。

使用系统调用pipe ( )建立一条管道线,两个子进程P1和P2分别向管道各写一句话:Child 1 is sending a message!Child 2 is sending a message!而父进程则从管道中读出来自于两个子进程的信息并显示在屏幕上,要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。

4. 实验指导⑴消息的创建、发送和接收。

<程序设计>为了便于操作和观察结果,用一个程序作为“引子”,先后fork ( )两个子进程SERVER和CLIENT进行通信,SERVER端建立一个KEY为75的消息队列,等待其他进程发来的消息。

当遇到类型为1的消息时,则作为结束信号,取消该队列并退出SERVER。

SERVER每接收到一个消息后显示一句“( server) received”。

CLIENT端使用KEY为75的消息队列,先后发送类型从10到1的消息后退出。

最后的一个消息即是SERVER端需要的结束信号。

CLIENT每发送一条消息后显示一句“(client)sent”。

实验二_进程间通信(DOC)

实验二_进程间通信(DOC)

实验二 进程间通信一、实验目的 在本实验中,通过对文件映射对象的了解,来加深对 Windows 2000 线程同步的理解. 回顾系统进程、线程的有关概念,加深对 Windows 2000 线程间通讯的理解;了解文件映射 对象;通过分析实验程序,了解线程如何通过文件映射对象发送数据;了解在进程中如何使 用文件映射对象. 二、背景知识 1. 共享内存: Windows 2000 提供了一种在文件中处理数据的方法, 名为内存映射文件, 也称为文件映射.文件映射对象是在虚拟内存中分配的永久或临时文件对象区域 (如果可能 的话,可大到整个文件) ,可将其看作是二进制的数据块.使用这类对象,可获得直接在内 存中访问文件内容的能力. 文件映射对象提供了强大的扫描文件中数据的能力,而不必移动文件指针.对于多线程 的读写操作来说, 这一点特别有用, 因为每个线程都可能想要把读取指针移动到不同的位置 去——为了防止这种情况,就需要使用某种线程同步机制保护文件. 在 CreateFileMapping() API 中,一个新的文件映射对象需要有一个永久的文件对象 (由 CreateFile() 所创建) .该函数使用标准的安全性和命名参数,还有用于允许操作 (如只读) 的保护标志以及映射的最大容量.随后可根据来自 OpenFileMapping() API 的其他线程或进程 使用该映射——这与事件和互斥体的打开进程是非常类似的. 内存映射文件对象的另一个强大的应用是可请求系统创建一个运行映射的临时文件.该 临时文件提供一个临时的区域, 用于线程或进程互相发送大量数据, 而不必创建或保护磁盘 上的文件.利用向创建函数中发送 INVALID_HANDLE_VALUE 来代替真正的文件句柄,就 可创建这一临时的内存映射文件; 指令内核使用系统页式文件来建立支持映射的最大容量的 临时数据区. 为了利用文件映射对象,进程必须将对文件的查看映射到它的内存空间中.也就是说, 应该将文件映射对象想象为进程的第一步,在这一步中,当查看实际上允许访问的数据时, 附加有共享数据的安全性和命名方式.为了获得指向内存区域的指针需要调用 MapViewOfFile() API,此调用使用文件映射对象的句柄作为其主要参数.此外还有所需的访 问等级 (如读-写) 和开始查看时文件内的偏移和要查看的容量.该函数返回一个指向进程内 的内存的指针,此指针可有多种编程方面的应用 (但不能超过访问权限) . 当结束文件映射查看时,必须用接受到的指针调用 UnmapViewOfFlie() API,然后再根 据映射对象调用 CloseHandle() API,从而将其清除。

实验二进程和进程通信

实验二进程和进程通信

实验二进程和进程通信实验二进程和进程通信一、实验目的1、通过使用进程和进程通信方面的系统调用的,加深理解有关进程方面的基本概念。

通过实验对进程有进一步的感性认识,掌握系统V的IPC机制。

二、实验内容1、设计一个程序,创建一个子进程,使父子进程合作,协调地完成某一功能。

要求在该程序中还要使用进程的睡眠、进程图象改换、父进程等待子进程终止、信号的设置与传送(包括信号处理程序)、子进程的终止等有关进程的系统调用。

2、分别利用UNIX的消息通信机制、共享内存机制(用信号灯实施进程间的同步和互斥)实现两个进程间的数据通信。

具体的通信数据可从一个文件读出,接收方进程可将收到的数据写入一个新文件,以便能判断数据传送的正确性(对文件操不熟悉的同学可不必通过读写文件,只要键盘输入和输出至屏幕进行比较即可)。

3、编写一个程序,生成若干个线程,通过这些并发线程的合作,完成较复杂的任务。

通过测试程序的运行结果,比较进程和进程、线程和线程之间对外部变量、静态变量和动态变量的共享方式的相同和不同之处。

三、实验代码3.1 父子进程通信#include#includemain(){int pid,status = 1;void func();signal(SIGUSR1,func); /* 预置信号处理程序,将SIGUSR1设置为func函数的功能*/while ((pid=fork( ))==-1);if (pid) { /* 父进程*/printf("It is the parent process.\n");printf("Parent: will send signal.\n");kill(pid,SIGUSR1); /* 发送信号,即发送执行func函数的信息*/ pid = wait(&status); /* 父进程等待子进程终止*/printf("Child process %d,status=%d \n",pid,status);}else { /* 子进程*/sleep (2); /* 等待接受信号*/printf("It is the child process.\n");printf("Child:signal is received.\n");execvp ("pwd",(char*)0); /* 映像改换,显示当前工作区,exevcp不用给出具体路径,(char*)0指向pwd命令*/printf("execl error.\n"); /* 映像改换失败*/exit(2);}printf ("Parent process finish. \n");}void func (){system("date");}3.2 消息通信/* msgcom.h */#include#include#include#include#define MSGKEY 5678struct msgtype{long mtype;int text;};#include "msgcom.h"main(){ /* 请求进程*/struct msgtype buf;int qid,pid;qid=msgget(MSGKEY,IPC_CREAT|0666); /* MSGKEY为约定的消息队列关键字,访问控制权限为0666 */buf.mtype=1; /* 请求进程发送消息标识为1 */buf.text=pid=getpid(); /* 请求进程发送消息内容为进程标识*/ msgsnd(qid,&buf,sizeof(buf.text), IPC_NOWAIT|04000); /* 发送消息正文长度为buf的大小*/msgrcv(qid,&buf,512,pid,MSG_NOERROR); /* 指定接收mtype=pid的信息,即请求进程发送给服务器处理后的信息*/ printf("Request received a massags from server, type is: %d\n",buf.mtype);}#include "msgcom.h"main(){ /* 服务器进程*/struct msgtype buf;int qid;if((qid=msgget(MSGKEY,IPC_CREAT|0666))== -1)return(-1); /* 出错处理*/while(1){msgrcv(qid,&buf,512,1,MSG_NOERROR); /* 接收所有请求进程发送的消息*/printf("Server receive a request from process %d\n",buf.text);buf.mtype=buf.text; /* 将请求进程的标识数作为mtype的值,以便于请求进程识别*/msgsnd(qid,&buf,sizeof(int),IPC_NOW AIT|04000); /* 将消息发送给请求进程*/ }}3.3 共享内存#include#include#include#include#include#define SHMKEY 18001 /* 共享内存关键字*/#define SHMKEY2 18002#define SIZE 1024 /* 共享内存长度*/#define SEMKEY1 19001 /* 信号灯组1关键字*/#define SEMKEY2 19002 /* 信号灯组2关键字*/#define SEMKEY3 19003 /* 信号灯组3关键字*/static void semcall(sid,op)int sid,op;{struct sembuf sb;sb.sem_num = 0; /* 信号灯编号0 */sb.sem_op = op; /* 信号灯操作数加1或减1 */sb.sem_flg = 0; /* 操作标志*/if(semop(sid,&sb,1) == -1)perror("semop"); /* 出错处理*/};int creatsem(key) /* 信号灯组创建及初始化程序*/key_t key;{int sid;union semun { /* 如sem.h中已定义,则省略*/int val;struct semid_ds *buf;ushort *array;} arg;if((sid=semget(key,1,0666|IPC_CREAT))==-1) /* 创建1个关键字为1的信号灯组,访问控制权限为0666 */perror("semget"); /* 出错处理*/arg.val=1; /* 初值为1 */if(semctl(sid,0,SETV AL,arg)==-1) /* 将信号灯组的第一个信号灯的初值置1 */perror("semctl"); /* 出错处理*/return(sid);}void P(sid)int sid;{semcall(sid,-1); /*对关键字为sid信号灯组值减1,相当于wait */}void V(sid)int sid;{semcall(sid,1); /*对关键字为sid信号灯组值加1,相当于signal */}main(){char *segaddr,*segaddr2;int segid,segid2,sid1,sid2,sid3;if((segid=shmget(SHMKEY,SIZE,IPC_CREAT|0666))==-1) /* 创建共享内存段*/perror("shmget"); /* 出错处理*/if((segid2=shmget(SHMKEY2,SIZE,IPC_CREA T|0666))==-1) /* 创建共享内存段2 */perror("shmget"); /* 出错处理*/segaddr=shmat(segid,0,0); /* 将共享内存映射到进程数据空间*/segaddr2=shmat(segid2,0,0); /* 将共享内存2映射到进程数据空间*/sid1=creatsem(SEMKEY1); /* 创建三个信号灯,初值为1 */sid2=creatsem(SEMKEY2);sid3=creatsem(SEMKEY3);P(sid2); /* 置信号灯2值为0,表示缓冲区1空*/P(sid3); /* 置信号灯3值为0,表示缓冲区2空*/if(!fork()){if(!fork()){while(1){ /* 子进程的子进程,接收和输出*/P(sid3);printf("Received from Parent: %s\n",segaddr2);printf("Received from Grandparent: %s\n",segaddr);V(sid1);}}while(1){ /* 子进程,输入和存储*/P(sid2);scanf("%s",segaddr2);V(sid3);}}while(1) { /* 父进程,输入和存储*/P(sid1);scanf("%s",segaddr);V(sid2);}}3.4 线程#include#include#includeint nthreads = 1; /* 线程执行函数,传入参数为线程序号,传出参数为项: (序号+1) */ void *dowork (void *params){int j = *(int *)params;int term = j+1;*(int *)params = term;printf ("the thread [%d]: term =%d\n",j,term);}void main(int argc,char **argv){int i;pthread_t threads[100];int pthread_data[100];float mean = 0; /* 平均数*/float variance = 0; /* 方差*/if(argc==2)nthreads = atoi (argv[1]); /* 将命令行字符串参数转换为整数*/for (i=0; i<="">pthread_data [i] = i ;pthread_create (&threads[i], NULL, dowork, &pthread_data[i]); /* 创建线程*/ }for (i=0; i<="">pthread_join (threads [i], NULL); /* 等待子线程结束,汇合结果*/mean += (float)pthread_data[i];}mean = mean / nthreads;for(i=0; i<="">variance += (float)(pthread_data[i]-mean)*(pthread_data[i]-mean);}variance = variance / nthreads;printf("The total threads is %d\n",nthreads);printf("The mean = %f\n", mean);printf("The variance = %f\n",variance);}四、运行结果3.1 父子进程通信父进程向子进程发送的消息为函数system(“date”)即显示日期时间。

操作系统实验 进程通信

操作系统实验 进程通信

进程通信(实验二)【实验目的】:掌握用邮箱方式进行进程通信的方法,并通过设计实现简单邮箱理解进程通信中的同步问题以及解决该问题的方法。

【实验原理】:邮箱机制类似于日常使用的信箱。

对于用户而言使用起来比较方便,用户只需使用send()向对方邮箱发邮件receive()从自己邮箱取邮件,send()和receive()的内部操作用户无需关心。

因为邮箱在内存中实现,其空间有大小限制。

其实send()和receive()的内部实现主要还是要解决生产者与消费者问题。

【实验内容】:进程通信的邮箱方式由操作系统提供形如send()和receive()的系统调用来支持,本实验要求学生首先查找资料了解所选用操作系统平台上用于进程通信的系统调用具体形式,然后使用该系统调用编写程序进行进程间的通信,要求程序运行结果可以直观地体现在界面上。

在此基础上查找所选用操作系统平台上支持信号量机制的系统调用具体形式,运用生产者与消费者模型设计实现一个简单的信箱,该信箱需要有创建、发信、收信、撤销等函数,至少能够支持两个进程互相交换信息,比较自己实现的信箱与操作系统本身提供的信箱,分析两者之间存在的异同。

实验背景介绍进程间通信有如下目的:数据的传输,共享数据,通知事情,资源共享,进程控制。

进程间的通信机制(IPC),就是多进程相互通信,交换信息的方法。

Linux IPC机制包括,信号和管道是其中的两个,还支持传统的UNIX SYSTM-V 的IPC 机制。

信号主要用来通知进程异步事情的发生,最初信号设计的目的是为了处理错误,他们也用来作为最基本的IPC机制。

管道是单向的,先进先出,先入先出,无结构的,固定大小的数据流。

UNIX System V 机制中的三种进程间通信机制,它们是:消息队列:用于进程之间传递分类的格式化数据信号量:用于通信之间的同步控制。

信号量通常与共享存储器方式一起使用。

共享内存:使不同进程通过共享彼此的虚拟空间而达到相互对共享区操作和数据通信。

进程通信__实验

进程通信__实验

大作业二进程通信姓名:学号:班级:日期:2015年7月1 需求说明(1)掌握进程间通讯的编程方法;(2)加深对进程并发执行的理解;(3)学习利用消息队列和共享存储区实现进程通信的方法。

2 设计说明(1)接收键盘输入进程负责接收用户的键盘输入,并以适当的方式将由键盘获得的数据交给其它进程处理。

(2)显示进程负责全部数据显示任务,包括键盘输入数据的显示和提示信息的显示。

(3)分发数据进程将键盘输入的数据分为3类,即字母、数字和其它,并分别将字母写入文件letter.txt中,数字写入文件number.txt中,除字母和数字外其它数据丢弃。

2.1 结构设计1、程序能以适当的方式提示用户输入数据;2、提示用户有数据被丢弃;3、全部的显示任务必须由显示进程完成;4、整个程序能够连续处理多组输入数据,直到用户输入“quit”字符串,整个程序结束;5、进一步要求:同时采用共享存储区和消息2种方法实现进程之间的通信,并比较这2种通信方法的利弊。

2.2 功能设计1、利用fork()函数创建2个子进程,用一个父进程和两个子进程完成上面的三个实验任务,用子进程1实现分发数据任务,子进程2实现接受键盘输入任务,父进程实现全部的显示任务。

2、同时通过共享存储区和消息队列两种进程通讯方式实现上面三个进程之间的同步和互斥。

3、利用while()循环、kill()函数和signal()函数实现连续多组数据输入。

3 测试和使用说明比较消息队列和共享存储区在消息通信机制中的数据传输的时间和性能:由于两种机制实现的机理和用处都不一样,难以直接进行时间上的比较。

如果比较其性能,应更加全面地分析。

3.1 使用说明2、有关字符数组初始化函数的使用:在本实验中频繁使用了memset()函数,且第二个参数均为’\0’,是为了将每次从键盘输入的字符串都能存到一个空的字符数组中,以防止字符的重复和覆盖。

3、在本程序中,需要合理安排父进程和2个子进程的任务,由父进程来负责显示任务是最合理和最简单的情况,因为父进程与子进程在某些方面是共享的,无需另外启用消息通信机制。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

实验二 进程间通信一、实验目的 在本实验中,通过对文件映射对象的了解,来加深对 Windows 2000 线程同步的理解. 回顾系统进程、线程的有关概念,加深对 Windows 2000 线程间通讯的理解;了解文件映射 对象;通过分析实验程序,了解线程如何通过文件映射对象发送数据;了解在进程中如何使 用文件映射对象. 二、背景知识 1. 共享内存: Windows 2000 提供了一种在文件中处理数据的方法, 名为内存映射文件, 也称为文件映射.文件映射对象是在虚拟内存中分配的永久或临时文件对象区域 (如果可能 的话,可大到整个文件) ,可将其看作是二进制的数据块.使用这类对象,可获得直接在内 存中访问文件内容的能力. 文件映射对象提供了强大的扫描文件中数据的能力,而不必移动文件指针.对于多线程 的读写操作来说, 这一点特别有用, 因为每个线程都可能想要把读取指针移动到不同的位置 去——为了防止这种情况,就需要使用某种线程同步机制保护文件. 在 CreateFileMapping() API 中,一个新的文件映射对象需要有一个永久的文件对象 (由 CreateFile() 所创建) .该函数使用标准的安全性和命名参数,还有用于允许操作 (如只读) 的保护标志以及映射的最大容量.随后可根据来自 OpenFileMapping() API 的其他线程或进程 使用该映射——这与事件和互斥体的打开进程是非常类似的. 内存映射文件对象的另一个强大的应用是可请求系统创建一个运行映射的临时文件.该 临时文件提供一个临时的区域, 用于线程或进程互相发送大量数据, 而不必创建或保护磁盘 上的文件.利用向创建函数中发送 INVALID_HANDLE_VALUE 来代替真正的文件句柄,就 可创建这一临时的内存映射文件; 指令内核使用系统页式文件来建立支持映射的最大容量的 临时数据区. 为了利用文件映射对象,进程必须将对文件的查看映射到它的内存空间中.也就是说, 应该将文件映射对象想象为进程的第一步,在这一步中,当查看实际上允许访问的数据时, 附加有共享数据的安全性和命名方式.为了获得指向内存区域的指针需要调用 MapViewOfFile() API,此调用使用文件映射对象的句柄作为其主要参数.此外还有所需的访 问等级 (如读-写) 和开始查看时文件内的偏移和要查看的容量.该函数返回一个指向进程内 的内存的指针,此指针可有多种编程方面的应用 (但不能超过访问权限) . 当结束文件映射查看时,必须用接受到的指针调用 UnmapViewOfFlie() API,然后再根 据映射对象调用 CloseHandle() API,从而将其清除。

三、实验内容 1. 编译运行项目 Lab5.1\SHAREMEM.DSW,观察运行结果,并阅读和分析实验程序.2. Lab5.2 目录下的示例程序:ProcessA.exe,ProcessB.exe 用三种方法实现了进程通信. (1)进程 A 中输入一些字符,点“利用 SendMessage 发送消息”按钮可将消息发到进程 B. (2)在进程 A 中输入一些字符,点“写数据到内存映像文件”按钮,然后在进程 B 中点“从 内存映像文件读数据” 按钮可收到消息. (3)先在进程 B 中点“创建管道并接收数据” 按钮,然后在进程 A 中输入一些字符,点“写 数据到管道文件”按钮可将消息发到进程 B(重复第 3 步每次可发一条消息). 消息传递数据通信可参考 SendMessage.txt,共享内存通信可参考 MemFile.txt,管道 通信可参考 Pipe.txt. 3.编写程序利用 WM_COPYDATA 消息机制,实现线程间的通信.进程间通信之管道每个进程各自有不同的用户地址空间, 任何一个进程的全局变量在另一个进程中 都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区, 进程 1 把数据从用户空间拷到内核缓冲区,进程 2 再从内核缓冲区把数据读走, 内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。

如下图所示。

图 3.1 进程间通信3.1 管道管道是一种最基本的 IPC 机制,由 pipe 函数创建:#include <unistd.h>int pipe(int filedes[2]);调用 pipe 函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读 端一个写端, 然后通过 filedes 参数传出给用户程序两个文件描述符, filedes[0] 指向管道的读端,filedes[1]指向管道的写端(很好记,就像 0 是标准输入 1 是标准输出一样)。

所以管道在用户程序看起来就像一个打开的文件,通过 read(filedes[0]);或者 write(filedes[1]);向这个文件读写数据其实是在读 写内核缓冲区。

pipe 函数调用成功返回 0,调用失败返回-1。

开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。

图 3.2 管道1. 父进程调用 pipe 开辟管道, 得到两个文件描述符指向管道的两端。

2. 父进程调用 fork 创建子进程,那么子进程也有两个文件描述符指 向同一管道。

3. 父进程关闭管道读端,子进程关闭管道写端。

父进程可以往管道里 写,子进程可以从管道里读,管道是用环形队列实现的,数据从写 端流入从读端流出,这样就实现了进程间通信。

例、管道#include <stdlib.h> #include <unistd.h> #define MAXLINE 80int main(void) { int n; int fd[2]; pid_t pid; char line[MAXLINE];if (pipe(fd) < 0) { perror("pipe"); exit(1); } if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid > 0) { /* parent */ close(fd[0]); write(fd[1], "hello world\n", 12); wait(NULL);} else {/* child */close(fd[1]); n = read(fd[0], line, MAXLINE);write(STDOUT_FILENO, line, n); } return 0; }使用管道有一些限制: 两个进程通过一个管道只能实现单向通信,比如上面的例子,父进 程写子进程读, 如果有时候也需要子进程写父进程读,就必须另开 一个管道。

请读者思考,如果只开一个管道,但是父进程不关闭读 端,子进程也不关闭写端,双方都有读端和写端,为什么不能实现 双向通信?  管道的读写端通过打开的文件描述符来传递,因此要通信的两个进 程必须从它们的公共祖先那里继承管道文件描述符。

上面的例子是 父进程把文件描述符传给子进程之后父子进程之间通信, 也可以父 进程 fork 两次,把文件描述符传给两个子进程,然后两个子进程 之间通信,总之需要通过 fork 传递文件描述符使两个进程都能访 问同一管道,它们才能通信。

使用管道需要注意以下 4 种特殊情况(假设都是阻塞 I/O 操作,没有设置 O_NONBLOCK 标志): 1. 如果所有指向管道写端的文件描述符都关闭了 (管道写端的引用计 数等于 0),而仍然有进程从管道的读端读数据,那么管道中剩余 的数据都被读取后,再次 read 会返回 0,就像读到文件末尾一样。

2. 如果有指向管道写端的文件描述符没关闭 (管道写端的引用计数大 于 0),而持有管道写端的进程也没有向管道中写数据,这时有进 程从管道读端读数据,那么管道中剩余的数据都被读取后,再次 read 会阻塞,直到管道中有数据可读了才读取数据并返回。

3. 如果所有指向管道读端的文件描述符都关闭了 (管道读端的引用计 数等于 0),这时有进程向管道的写端 write,那么该进程会收到 信号 SIGPIPE,通常会导致进程异常终止。

4. 如果有指向管道读端的文件描述符没关闭 (管道读端的引用计数大 于 0),而持有管道读端的进程也没有从管道中读数据,这时有进 程向管道写端写数据,那么在管道被写满时再次 write 会阻塞,直 到管道中有空位置了才写入数据并返回。

管道的这四种特殊情况具有普遍意义。

3.2 其它 IPC 机制进程间通信必须通过内核提供的通道, 而且必须有一种办法在进程中标识内核提 供的某个通道, 上一节讲的管道是用打开的文件描述符来标识的。

如果要互相通 信的几个进程没有从公共祖先那里继承文件描述符, 它们怎么通信呢?内核提供 一条通道不成问题, 问题是如何标识这条通道才能使各进程都可以访问它?文件 系统中的路径名是全局的, 各进程都可以访问,因此可以用文件系统中的路径名 来标识一个 IPC 通道。

FIFO 和 UNIX Domain Socket 这两种 IPC 机制都是利用文件系统中的特殊文件 来标识的。

可以用 mkfifo 命令创建一个 FIFO 文件:$ mkfifo hello $ ls -l hello prw-r--r-- 1 akaedu akaedu 0 2008-10-30 10:44 helloFIFO 文件在磁盘上没有数据块,仅用来标识内核中的一条通道,各进程可以打 开这个文件进行 read/write, 实际上是在读写内核通道 (根本原因在于这个 file 结构体所指向的 read、write 函数和常规文件不一样),这样就实现了进程间通 信。

UNIX Domain Socket 和 FIFO 的原理类似,也需要一个特殊的 socket 文件 来标识内核中的通道,例如/var/run 目录下有很多系统服务的 socket 文件:$ ls -l /var/run/ total 52 srw-rw-rw- 1 root root 2008-10-30 00:24 acpid.socket ... 0srw-rw-rw- 1 root root 2008-10-30 00:25 gdm_socket ... srw-rw-rw- 1 root 2008-10-30 00:24 sdp ... srwxr-xr-x 1 root root 2008-10-30 00:42 synaptic.socket root000文件类型 s 表示 socket, 这些文件在磁盘上也没有数据块。

相关文档
最新文档