搜档网
当前位置:搜档网 › 利用消息队列实现多进程通信过程

利用消息队列实现多进程通信过程

利用消息队列实现多进程通信过程
利用消息队列实现多进程通信过程

课程名称:Unix课程设计

设计题目:利用消息队列实现多进程通信过程

姓名:

专业:网络工程

班级:

学号:

计算机科学与技术学院

网络系

2013 年12月30 日

一、选题背景

在UNIX程序设计中消息队列是使用频率最高的几个对象之一,它常应用于对等进程间的通信和客户—服务器之间的通信。

采用消息队列作为货物托运渠道可以弥补以下缺陷:

(1)消息队列是一种先进先出的队列型数据结构,可以保证先送的货物先到达,后送的货物后到达,避免了插队现象。

(2)消息队列将输出的信息进行了打包处理,这样就可以保证以每个消息为单位进行接收了。

(3)消息队列还可以将货物进行分类服务,标记各种类别的服务,这样就可以根据货物类别分别出货。

消息队列是IPC对象的一种与同样提供先进先出服务的管道相比,它有如下特点:

(1)消息队列提供了消息的自动拆分功能,同时不能接收两次发送的消息。

(2)消息队列提供了不完全随机读取的服务,引入消息类型后,一个消息队列在逻辑上可以化身为多个不同消息类型的链表,用户可以自主选择接收某条逻辑链表上的消息,而不必依次接收队列的首条消息。

(3)消息队列提供了完全异步的读写服务。

基于以上背景,我们发现利用消息队列实现进程间的通信可以为我们提供方便,也会是通信效率更为提高。

二、设计思路

可以采用客户-服务器结构,其中服务器端实现各个用户的登录并存储相关信息,客户端通过服务器端获取当前登录用户信息,然后各客户进程通过消息队列实现双向通信。

编程实现两个进程间的通信,一个server(服务器)进程,一个client(客户)进程。

在client(客户)进程下选择注册或者登陆。

若是注册,则server(服务器)进程分给该客户一个新的登陆账号。多个客户的账号存储在register.txt文件中。

若是选择登陆,登陆成功后服务器进程会显示登陆成功,否则显示登陆失败。登陆后输入你要发送消息的对方的账号,输入账号后,服务器方显示该账号是否合理,如合理,即可实行通信。

三、主要问题的解决方法和关键技术

实现密码格式登录与注册,通过帐号显示用户名,显示发送消息的日期

四、程序流程图

五、原程序清单

//client.c

#include

#include

#include

#include

#include

#include

#include

#include

struct msgbuf{

long type;

char buf[1024];

};

struct msgbuf0{

long type;

pid_t pid;

char username[48];

char password[48];

char number[28];

};

void func();

void regist();//注册

void passw();

void inputpassw(char *P);

void data();

void stol(char*);//加密

void ltos(char*);//解密

struct msgbuf _msgbuf ;

struct msgbuf0 _msgbuf0,_msgbuf1;

int msgid,index,flag ;

long number;

pid_t pid,pid1;

FILE * fpr;

char username[16],password[16],buf[64],inde[4],flg[4];

void main(){//28

pid_t pid = getpid();

if((msgid = msgget(0x4321,0666|IPC_CREAT)) < 0){

fprintf(stderr,"msgget() failed.\n");

return ;

}

fprintf(stderr,"注册please input 'z',登录 input 'd':");

scanf("%s",buf);

memset(&_msgbuf0,0,sizeof(_msgbuf0));

if(buf[0] == 100){

enter();//登录

}

else if(buf[0] == 122){

regist(); //注册

}

}

void func(){

memset(&_msgbuf,0,sizeof(_msgbuf));

_msgbuf.type = atoi(_https://www.sodocs.net/doc/8d12886325.html,ername)+100;

while(msgrcv(msgid,&_msgbuf,sizeof(_msgbuf.buf),_msgbuf.type,IPC_NOWAIT) != -1){

fprintf(stderr,"%s [receice].\n",_msgbuf);

memset(&_msgbuf,0,sizeof(_msgbuf));

}

//signal(KILL,func);

}

void regist(){

memset(_https://www.sodocs.net/doc/8d12886325.html,ername,0,16);

memset(_msgbuf0.password,0,16);

fprintf(stderr,"please input username (注册):");

scanf("%s",_https://www.sodocs.net/doc/8d12886325.html,ername);

getchar();

if(strlen(_https://www.sodocs.net/doc/8d12886325.html,ername) < 3||strlen(_https://www.sodocs.net/doc/8d12886325.html,ername) > 16){

16.\n");

regist();

}

_msgbuf0.pid = getpid();

_msgbuf0.type = 2;//查询

stol(_https://www.sodocs.net/doc/8d12886325.html,ername);///加密

passw();//密码

_msgbuf0.type = 2 ;

_msgbuf0.pid = getpid();stol(_msgbuf0.password);///加密

fprintf(stderr,"do you want to get a number for chat:(y/n)");

if(getchar() == 'y'){

msgsnd(msgid,&_msgbuf0,128,0);

memset(&_msgbuf0,0,sizeof(_msgbuf0));

msgrcv(msgid,&_msgbuf0,128,getpid()+10000,0);

ltos(_msgbuf0.number);

fprintf(stderr,"恭喜你获得一个聊天账号:%s.\n",_msgbuf0.number);

fprintf(stderr,"登录 please input 'd':");

scanf("%s",buf);

if(buf[0] == 100){

enter();//登录

}

}

}

void passw(){

fprintf(stderr,"please input password (注册):");

inputpassw(_msgbuf0.password);

//scanf("%s",_msgbuf0.password);

while(strlen(_msgbuf0.password) < 3||strlen(_msgbuf0.password) >

16){

fprintf(stderr,"the length of password cann't less 3(注册) and more

16.\n");

fprintf(stderr,"please input password (注册):");

inputpassw(_msgbuf0.password);

//scanf("%s",_msgbuf0.password);

}

fprintf(stderr,"please input password again (注册):");

inputpassw(password);

//scanf("%s",password);

if(strcmp(_msgbuf0.password,password)){

memset(_msgbuf0.password,0,49);

memset(password,0,17);

fprintf(stderr,"两次输入的密码不一致,请从新填写信息。\n");

}

}

void enter(){

memset(&_msgbuf0,0,sizeof(_msgbuf0));

fprintf(stderr,"please input number (登录):");

scanf("%s",_msgbuf0.number);

getchar();

while((strlen(_msgbuf0.number) != 8)||(atoi(_msgbuf0.number) > 100000000)||(atoi(_msgbuf0.number) < 10000000)){

fprintf(stderr,"please input number (登录):");

scanf("%s",_msgbuf0.number);

getchar();

}

fprintf(stderr,"please input password (登录):");

inputpassw(_msgbuf0.password);

_msgbuf0.type = 1 ;

number = atoi(_msgbuf0.number) ;

stol(_msgbuf0.number);///加密

stol(_msgbuf0.password);///加密 msgsnd(msgid,&_msgbuf0,128,0);

memset(&_msgbuf,0,sizeof(_msgbuf));

msgrcv(msgid,&_msgbuf,sizeof(_msgbuf.buf),number,0);

ltos(_msgbuf.buf);

sscanf(_msgbuf.buf,"%01[^:]:%s",buf,username);

if(!(strcmp(buf,"n"))){

fprintf(stderr,"%s 登陆失败.\n.\n",username);

enter();

}

fprintf(stderr,"%s 登陆成功.\n.\n",username);

if((pid1 = fork()) < 0){

fprintf(stderr,"fork() failed.\n");

return ;

}

if(pid1 > 0){

data();

}

if(pid1 == 0){

while(1){

memset(&_msgbuf,0,sizeof(_msgbuf));

if(msgrcv(msgid,&_msgbuf,sizeof(_msgbuf.buf),number+100000000,MSG_NOERRO R) != -1){

ltos(_msgbuf.buf);//解密

fprintf(stderr,"\n %s\n\n[send]:",_

memset(&_msgbuf,0,sizeof(_msgbuf));

}

}

}

}

void data(){

long number1 ;

char username1[16];

struct tm when ;

time_t now ;

while(1){

fprintf(stderr,"please input 你要发送对方的账号 :");

memset(&_msgbuf1,0,sizeof(_msgbuf1));

scanf("%s",_msgbuf1.number);

while((strlen(_msgbuf1.number) != 8)||(atoi(_msgbuf1.number) > 100000000)||(atoi(_msgbuf1.number) < 10000000)||(atoi(_msgbuf1.number) == number)){

fprintf(stderr,"please input number again:");

scanf("%s",_msgbuf1.number);

}

_msgbuf1.type = 4 ;

_msgbuf1.pid = getpid();

number1 = atoi(_msgbuf1.number) ;

stol(_msgbuf1.number);///加密

msgsnd(msgid,&_msgbuf1,128,0);

msgrcv(msgid,&_msgbuf,sizeof(_msgbuf.buf),getpid(),MSG_NOERROR);

ltos(_msgbuf.buf);

sscanf(_msgbuf.buf,"%01[^:]:%s",buf,username1);

if(!(strcmp(buf,"y"))){

while(1){

memset(&_msgbuf,0,sizeof(_msgbuf));

time(&now);

when = *localtime(&now);

fprintf(stderr,"[send %s %02d:%02d:%02d %02d,%02d,%04d]:",username1,whe n.tm_hour,when.tm_min,when.tm_sec,when.tm_mon+1,when.tm_mday,when.tm_yea r+1900);

scanf("%s",_msgbuf.buf);

if(!strcmp(_msgbuf.buf,"switch")){

data();

break;

}

if(!strcmp(_msgbuf.buf,"exit")) return ;

when = *localtime(&now);

sprintf(_msgbuf.buf,"%s:[receive--%s--%d--%02d:%02d:%02d--%02d,%02d,%04d \n\n\n]",_msgbuf.buf,username,number,when.tm_hour,when.tm_min,when.tm_se c,when.tm_mon+1,when.tm_mday,when.tm_year+1900);

stol(_msgbuf.buf);//加密

_msgbuf.type = number1 + 100000000 ;

msgsnd(msgid,&_msgbuf,strlen(_msgbuf.buf),0);

}

}

}

}

//加密

void stol(char *buf){

long i ;

char buf1[1024];

sscanf(buf,"%s",buf1);

memset(buf,0,sizeof(buf));

for(i = 0;i < strlen(buf1);i++){

sprintf(buf,"%s%03d",buf,buf1[i]+456);

}

}

//解密

void ltos(char *buf){

int i ,n = strlen(buf);

char buf1[4],data[1024];

memset(data,0,sizeof(data));

for(i = 0 ;i < n;){

memset(buf1,0,sizeof(buf1));

sscanf(buf,"%03s%s",buf1,buf);

i = i+3 ;

sprintf(data,"%s%c",data,atoi(buf1)-456);

}

sprintf(buf,"%s",data);

}

void inputpassw(char *buf){

struct termio tm_new,tm_old;

int fd = 0,c = 0;

memset(buf,0,sizeof(buf));

ioctl(fd,TCGETA,&tm_old) ;

tm_new = tm_old ;

tm_new.c_lflag &=~(ECHO|ICANON);//ECHO 显示输入字符 ICANON 使用标准输入模式

while((c = getchar()) != 10){

fprintf(stderr,"*");

sprintf(buf,"%s%c",buf,c);

}

ioctl(fd,TCSETA,&tm_old) ;

fprintf(stderr,"\n");

}

//Server.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

struct msgbuf{

long type;

char buf[1024];

};

struct msgbuf0{

long type;

pid_t pid;

char username[48];

char password[48];

char number[28];

};

void func();

void function();

void stol(char*);//加密

void ltos(char*);//解密

int rand1();//随机产生一个9位数

struct msgbuf _msgbuf ;

struct msgbuf0 _msgbuf0,_msgbuf1 ;

struct sembuf _buf;

// pthread_t pid ;

FILE *fpr,*fpw ;

char buf[64],username[16],password[16],inde[4],flg[4],*p,number[10];

int msgid,semid,shmid,index = 0,flag = 0,sum;

pid_t pid ,pid_c;

void main(){

if((semid = semget(0x1234,2,0666|IPC_CREAT)) < 0){

fprintf(stderr,"semget() failed.\n");

}

semctl(semid,0,SETVAL,1);

semctl(semid,1,SETVAL,1);

_buf.sem_flg = _buf.sem_flg & ~ IPC_NOWAIT ;

if((shmid = shmget(0x123,4,0666|IPC_CREAT)) < 0){

fprintf(stderr,"shmget() failed.\n");

return ;

}

p = shmat(shmid,0,0);

(*p) = 0 ;

if((msgid = msgget(0x4321,0666|IPC_CREAT)) < 0){

fprintf(stderr,"msgget() failed.\n");

return ;

}

func();

}

void func(){

if(msgrcv(msgid,&_msgbuf0,128,-2,0) != -1){

if((pid = fork()) < 0){

fprintf(stderr,"fork() failed.\n");

return ;

}

if(pid == 0){

fprintf(stderr,"解密前 receive [number] is %s, [username] is %s, [password]

is %s.\n",_msgbuf0.number,_https://www.sodocs.net/doc/8d12886325.html,ername,_msgbuf0.password);

ltos(_https://www.sodocs.net/doc/8d12886325.html,ername);//解密

ltos(_msgbuf0.number);//解密

ltos(_msgbuf0.password);//解密

pid_c = _msgbuf0.pid ;

if(_msgbuf0.type == 1){

//denglu

function();

}

if(_msgbuf0.type == 2){

//zhuce

_buf.sem_num = 0 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

}

_buf.sem_num = 0 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if((fpr = fopen("./register.txt","r")) == NULL){

fprintf(stderr,"fopen() failed.\n");

return ;

}

memset(buf,0,sizeof(buf));

see();

fclose(fpr);

_buf.sem_num = 0 ;///////////////

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

(*p)-- ;

_buf.sem_num = 0 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

}

_buf.sem_num = 1 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

if((fpw = fopen("./register.txt","a+")) == NULL){

fprintf(stderr,"fopen() failed.\n");

return ;

}

index++ ;

memset(buf,0,sizeof(buf));

sprintf(buf,"%d %s %s %s %d",index,_msgbuf0.number,_https://www.sodocs.net/doc/8d12886325.html,ername ,_msgbuf0.password,flag);

fputs(buf,fpw);

fputs("\n",fpw);

fclose(fpw);

_buf.sem_num = 1 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

fprintf(stderr,"user %s register success.\n",_https://www.sodocs.net/doc/8d12886325.html,ername);

stol(_msgbuf0.number);

memset(&_https://www.sodocs.net/doc/8d12886325.html,ername,0,sizeof(_https://www.sodocs.net/doc/8d12886325.html,ername));

memset(&_msgbuf0.password,0,sizeof(_msgbuf0.password));

fprintf(stderr,"\n\n%d---%s",_msgbuf0.type,_msgbuf0.number);

msgsnd(msgid,&_msgbuf0,128,0);

}

exit(1);

}

}else {

fprintf(stderr,"msgrcv() failed.\n");

return ;

}

func();

}

void function(){

_buf.sem_num = 0 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

}

(*p)++ ;

_buf.sem_num = 0 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if((fpr = fopen("./register.txt","r")) == NULL){

fprintf(stderr,"fopen() failed.\n");

return ;

}

memset(buf,0,sizeof(buf));

while(fgets(buf,sizeof(buf),fpr)){

sscanf(buf,"%[^ ] %[^ ] %[^ ] %[^ ] %[^ ] ",inde,number,username,password,flg);

memset(buf,0,sizeof(buf));

if(!strcmp(number,_msgbuf0.number)

&& !strcmp(password,_msgbuf0.password)){

memset(&_msgbuf,0,sizeof(_msgbuf));

_msgbuf.type = atoi(_msgbuf0.number) ;

sprintf(_msgbuf.buf,"y:%s",username);

stol(_msgbuf.buf);

flag = 2;

break ;

}

}

fclose(fpr);

_buf.sem_num = 0 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

(*p)-- ;

_buf.sem_num = 0 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

}

if(flag == 2){

fprintf(stderr,"user %s 登录成功.\n\n\n",username);

data();

}

if(flag != 2){

memset(&_msgbuf,0,sizeof(_msgbuf));

_msgbuf.type = atoi(_msgbuf0.number) ;

strcpy(_msgbuf.buf,"n:enter-failed");

stol(_msgbuf.buf);

msgsnd(msgid,&_msgbuf,sizeof(_msgbuf.buf),0);

fprintf(stderr," %s 登录failed.\n\n\n",_msgbuf0.number);

}

}

void data(){

while(1){

memset(&_msgbuf,0,sizeof(_msgbuf));

if(msgrcv(msgid,&_msgbuf1,128,4,MSG_NOERROR) != -1){

ltos(_msgbuf1.number);//解密

_buf.sem_num = 0 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

}

(*p)++ ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if((fpr = fopen("./register.txt","r")) == NULL){

fprintf(stderr,"fopen() failed.\n");

return ;

}

memset(buf,0,sizeof(buf));

while(fgets(buf,sizeof(buf),fpr)){

sscanf(buf,"%[^ ] %[^ ] %[^ ] %[^ ]%[^ ] ",inde,number,username,password,flg);

if(!strcmp(number,_msgbuf1.number)){

memset(&_msgbuf,0,sizeof(buf));

flag = 1;

_msgbuf.type = _msgbuf1.pid;

sprintf(_msgbuf.buf,"y:%s",username);

stol(_msgbuf.buf);

msgsnd(msgid,&_msgbuf,sizeof(_msgbuf.buf),0);

fprintf(stderr,"%s exist.\n",_msgbuf1.number);

break ;

}//if

}//while

fclose(fpr);

_buf.sem_num = 0 ;///////////////

_buf.sem_op = -1 ;

semop(semid,&_buf,1);//wait();

(*p)-- ;

_buf.sem_num = 0 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

if(*p == 0){

_buf.sem_num = 1 ;

_buf.sem_op = 1 ;

semop(semid,&_buf,1);//signal();

}

if(flag != 1){

_msgbuf.type = _msgbuf1.pid;

_msgbuf.buf[0] = 'n';

_msgbuf.buf[1] = 0;

msgsnd(msgid,&_msgbuf,sizeof(_msgbuf.buf),0);

fprintf(stderr,"%s donn't exist.\n",_msgbuf1.number);

}

} //msgrcv

}

}

void stol(char *buf){

long i ;

char buf1[1024];

strcpy(buf1,buf);

memset(buf,0,sizeof(buf));

for(i = 0;i < strlen(buf1);i++){

sprintf(buf,"%s%03d",buf,buf1[i]+456);

}

}

//解密

void ltos(char *buf){

int i ,n = strlen(buf);

char buf1[4],data[1024];

memset(data,0,sizeof(data));

for(i = 0 ;i < n;){

memset(buf1,0,sizeof(buf1));

sscanf(buf,"%03s%s",buf1,buf);

i = i+3 ;

sprintf(data,"%s%c",data,atoi(buf1)-456);

}

sprintf(buf,"%s",data);

}

//随机产生一个9位数

int rand1(){

char buf[64];

int number ;

number = rand()%100000000;

while((number < 10000000) || (number >= 100000000)){

number = rand()%100000000;

}

return number ;

}

void see(){

long num ;

int flag = 0;

num = rand1();

while(fgets(buf,sizeof(buf),fpr)){

sscanf(buf,"%[^ ] %[^ ] %[^ ] %[^ ] %[^ ] ",inde,number,username,password,flg);

index = (atoi(inde) > index) ? atoi(inde) : index ;

if(num == atoi(number)) {

flag = 1 ;

break ;

}

if(flag != 1){

memset(_msgbuf0.number,0,28);

sprintf(_msgbuf0.number,"%d",num);

}else{

see();

}

}

六、程序运行结果

七、设计总结

心得体会

(1)通过这门课程设计,加强了我们的程序设计能力,改正了我们以往编程方面的不少错误和坏习惯。

(2)通过这门课程设计,我们认识到知识不是一蹴而就的,是需要慢慢积累的,积少成多,在学习生活中应该有这种精神。

(3)通过此次练习,我们认识到一个好的课程设计是需要大家共同努力的,一个人的知识面是有局限的,只有大家在一起相互学习才会更加进步,更好的完

成任务。

(4)这次练习,我们的老师对我们帮助不少,使我们认识到在学习中不懂就要问,

尽快解决所有学习上的疑难杂症。

进程间通信的四种方式

一、剪贴板 1、基础知识 剪贴板实际上是系统维护管理的一块内存区域,当在一个进程中复制数据时,是将这个数据放到该块内存区域中,当在另一个进程中粘贴数据时,是从该内存区域中取出数据。 2、函数说明: (1)、BOOL OpenClipboard( ) CWnd类的OpenClipboard函数用于打开剪贴板。若打开剪贴板成功,则返回非0值。若其他程序或当前窗口已经打开了剪贴板,则该函数返回0值,表示打开失败。若某个程序已经打开了剪贴板,则其他应用程序将不能修改剪贴板,直到前者调用了CloseClipboard函数。 (2)、BOOL EmptyClipboard(void) EmptyClipboard函数将清空剪贴板,并释放剪贴板中数据的句柄,然后将剪贴板的所有权分配给当前打开剪贴板的窗口。 (3)、HANDLE SetClipboardData(UINT uFormat, HANDLE hMem) SetClipboardData函数是以指定的剪贴板格式向剪贴板上放置数据。uFormat指定剪贴板格式,这个格式可以是已注册的格式,或是任一种标准的剪贴板格式。CF_TEXT表示文本格式,表示每行数据以回车换行(0x0a0x0d)终止,空字符作为数据的结尾。hMem指定具有指定格式的数据的句柄。hMem参数可以是NULL,指示采用延迟提交技术,则该程序必须处理WM_RENDERFORMA T和WM_RENDERALLFORMATS消息。应用程序在调用SetClipboardData函数之后,就拥有了hMem参数所标识的数据对象,该应用程序可以读取该数据对象,但在应用程序调用CloseClipboard函数之前,它不能释放该对象的句柄,或者锁定这个句柄。若hMem标识了一个内存对象,那么这个对象必须是利用GMEM_MOVEABLE标志调用GlobalAlloc函数为其分配内存。 注意:调用SetClipboardData函数的程序必须是剪贴板的拥有者,且在这之前已经打开了剪贴板。 延迟提交技术:当一个提供数据的进程创建了剪贴板数据之后,直到其他进程获取剪贴板数据之前,这些数据都要占据内存空间。若在剪贴板上放置的数据过大,就会浪费内存空间,降低对资源的利用率。为了避免这种浪费,就可以采用延迟提交计数,也就是由数据提供进程先提供一个指定格式的空剪贴板数据块,即把SetClipboardData函数的hMem参数设置为NULL。当需要获取数据的进程想要从剪贴板上得到数据时,操作系统会向数据提供进程发送WM_RENDERFORMA T消息,而数据提供进程可以响应这个消息,并在此消息的响应函数中,再一次调用SetClipboardData函数,将实际的数据放到剪贴板上。当再次调用SetClipboardData函数时,就不再需要调用OpenClipboard函数,也不再需要调用EmptyClipboard函数。也就是说,为了提高资源利用率,避免浪费内存空间,可以采用延迟提交技术。第一次调用SetClipboardData函数时,将其hMem参数设置为NULL,在剪贴板上以指定的剪贴板格式放置一个空剪贴板数据块。然后直到有其他进程需要数据或自身进程需要终止运行时再次调用SetClipboardData函数,这时才真正提交数据。 (4)、HGLOBAL GlobalAlloc( UINT uFlags,SIZE_T dwBytes); GlobalAlloc函数从堆上分配指定数目的字节。uFlags是一个标记,用来指定分配内存的方式,uFlags为0,则该标记就是默认的GMEM_FIXED。dwBytes指定分配的字节数。

进程同步与通信作业习题与答案

第三章 一.选择题(50题) 1.以下_B__操作系统中的技术是用来解决进程同步的。 A.管道 B.管程 C.通道 2.以下_B__不是操作系统的进程通信手段。 A.管道 B.原语 C.套接字 D.文件映射 3.如果有3个进程共享同一程序段,而且每次最多允许两个进程进入该程序段,则信号量的初值应设置为_B__。 4.设有4个进程共享一个资源,如果每次只允许一个进程使用该资源,则用P、V操作管理时信号量S的可能取值是_C__。 ,2,1,0,-1 ,1,0,-1,-2 C. 1,0,-1,-2,-3 ,3,2,1,0 5.下面有关进程的描述,是正确的__A__。 A.进程执行的相对速度不能由进程自己来控制 B.进程利用信号量的P、V 操作可以交换大量的信息 C.并发进程在访问共享资源时,不可能出现与时间有关的错误 、V操作不是原语操作 6.信号灯可以用来实现进程之间的_B__。 A.调度 B.同步与互斥 C.同步 D.互斥 7.对于两个并发进程都想进入临界区,设互斥信号量为S,若某时S=0,表示_B__。 A.没有进程进入临界区 B.有1个进程进入了临界区 C. 有2个进程进入了临界区 D. 有1个进程进入了临界区并且另一个进程正等待进入 8. 信箱通信是一种_B__方式 A.直接通信 B.间接通信 C.低级通信 D.信号量 9.以下关于临界区的说法,是正确的_C__。

A.对于临界区,最重要的是判断哪个进程先进入 B.若进程A已进入临界区,而进程B的优先级高于进程A,则进程B可以 打断进程A而自己进入临界区 C. 信号量的初值非负,在其上只能做PV操作 D.两个互斥进程在临界区内,对共享变量的操作是相同的 10. 并发是指_C__。 A.可平行执行的进程 B.可先后执行的进程 C.可同时执行的进程 D.不可中断的进程 11. 临界区是_C__。 A.一个缓冲区 B.一段数据区 C.一段程序 D.栈 12.进程在处理机上执行,它们的关系是_C__。 A.进程之间无关,系统是封闭的 B.进程之间相互依赖相互制约 C.进程之间可能有关,也可能无关 D.以上都不对 13. 在消息缓冲通信中,消息队列是一种__A__资源。 A.临界 B.共享 C.永久 D.可剥夺 14. 以下关于P、V操作的描述正确的是__D_。 A.机器指令 B. 系统调用 C.高级通信原语 D.低级通信原语 15.当对信号量进行V源语操作之后,_C__。 A.当S<0,进程继续执行 B.当S>0,要唤醒一个就绪进程 C. 当S<= 0,要唤醒一个阻塞进程 D. 当S<=0,要唤醒一个就绪 16.对临界区的正确论述是__D_。 A.临界区是指进程中用于实现进程互斥的那段代码 B. 临界区是指进程中用于实现进程同步的那段代码 C. 临界区是指进程中用于实现进程通信的那段代码 D. 临界区是指进程中访问临界资源的那段代码 17. __A__不是进程之间的通信方式。 A.过程调用 B.消息传递 C.共享存储器 D.信箱通信 18. 同步是指进程之间逻辑上的__A__关系。

进程间通信实验报告

进程间通信实验报告 班级:10网工三班学生姓名:谢昊天学号:1215134046 实验目的和要求: Linux系统的进程通信机构 (IPC) 允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉Linux支持的消息通讯机制及信息量机制。 实验内容与分析设计: (1)消息的创建,发送和接收。 ①使用系统调用msgget (), msgsnd (), msgrev (), 及msgctl () 编制一长度为1k 的消息的发送和接收程序。 ②观察上面的程序,说明控制消息队列系统调用msgctl () 在此起什么作用? (2)共享存储区的创建、附接和段接。 使用系统调用shmget(),shmat(),sgmdt(),shmctl(),编制一个与上述功能相同的程序。(3)比较上述(1),(2)两种消息通信机制中数据传输的时间。 实验步骤与调试过程: 1.消息的创建,发送和接收: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)在SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER 。SERVER每接收到一个消息后显示一句“(server)received”。 (3)CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,既是 SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client)sent”。 (4)父进程在 SERVER和 CLIENT均退出后结束。 2.共享存储区的创建,附接和断接: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)SERVER端建立一个KEY为75的共享区,并将第一个字节置为-1。作为数据空的标志.等待其他进程发来的消息.当该字节的值发生变化时,表示收到了该消息,进行处理.然后再次把它的值设为-1.如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER.SERVER 每接收到一次数据后显示”(server)received”. (3)CLIENT端建立一个为75的共享区,当共享取得第一个字节为-1时, Server端空闲,可发送请求. CLIENT 随即填入9到0.期间等待Server端再次空闲.进行完这些操作后, CLIENT退出. CLIENT每发送一次数据后显示”(client)sent”. (4)父进程在SERVER和CLIENT均退出后结束。 实验结果: 1.消息的创建,发送和接收: 由 Client 发送两条消息,然后Server接收一条消息。此后Client Server交替发送和接收消息。最后一次接收两条消息。Client 和Server 分别发送和接收了10条消息。message 的传送和控制并不保证完全同步,当一个程序不再激活状态的时候,它完全可能继续睡眠,造成上面现象。在多次send message 后才 receive message.这一点有助于理解消息转送的实现机理。

进程间通信方式比较

进程间的通信方式: 1.管道(pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 2.信号(signal): 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致得。 3.消息队列(message queue): 消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。 消息缓冲通信技术是由Hansen首先提出的,其基本思想是:根据”生产者-消费者”原理,利用内存中公用消息缓冲区实现进程之间的信息交换. 内存中开辟了若干消息缓冲区,用以存放消息.每当一个进程向另一个进程发送消息时,便申请一个消息缓冲区,并把已准备好的消息送到缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程.接收进程收到发送里程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出所需的信息,然后把消息缓冲区不定期给系统.系统负责管理公用消息缓冲区以及消息的传递. 一个进程可以给若干个进程发送消息,反之,一个进程可以接收不同进程发来的消息.显然,进程中关于消息队列的操作是临界区.当发送进程正往接收进程的消息队列中添加一条消息时,接收进程不能同时从该消息队列中到出消息:反之也一样. 消息缓冲区通信机制包含以下列内容:

(1) 消息缓冲区,这是一个由以下几项组成的数据结构: 1、消息长度 2、消息正文 3、发送者 4、消息队列指针 (2)消息队列首指针m-q,一般保存在PCB中。 (1)互斥信号量m-mutex,初值为1,用于互斥访问消息队列,在PCB中设置。 (2)同步信号量m-syn,初值为0,用于消息计数,在PCB中设置。(3)发送消息原语send (4)接收消息原语receive(a) 4.共享内存(shared memory): 可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。 这种通信模式需要解决两个问题:第一个问题是怎样提供共享内存;第二个是公共内存的互斥关系则是程序开发人员的责任。 5.信号量(semaphore): 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。 6.套接字(socket); 这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。 https://www.sodocs.net/doc/8d12886325.html,/eroswang/archive/2007/09/04/1772350.aspx linux下的进程间通信-详解

linux进程间通讯的几种方式的特点和优缺点

1. # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 # 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 # 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 # 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。#共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 # 套接字( socket ) :套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 管道的主要局限性正体现在它的特点上: 只支持单向数据流; 只能用于具有亲缘关系的进程之间; 没有名字; 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等; 2. 用于进程间通讯(IPC)的四种不同技术: 1. 消息传递(管道,FIFO,posix和system v消息队列) 2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯) 3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区) 4. 过程调用(Solaris门,Sun RPC) 消息队列和过程调用往往单独使用,也就是说它们通常提供了自己的同步机制.相反,共享内存区

进程之间的通信实验

实验:进程之间的通信管道 1.Pipe函数与进程通信 下面实验为使用管道进行父子进程间通信。程序首先判断参数是否合法,因为输入的字符将从父进程通过发送到子进程中。然后,调用pipe函数创建父子进程用于通信的管道。使用fork函数创建子进程时,子进程会获得与父进程相同的资源,其中包括文件描述符信息。因此,调用fork函数须在pipe函数调用前。 当父子进程通过管道进行通信时,files[1]为用于数据写入的文件描述符.因此,在子进程中,要读取管道中的数据可以调用read函数,而读取得文件描述符为files[0]。对于父进程而言,写入数据需要调用write 函数,要写入的文件描述为files[1]。 #include #include 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; }else printf("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; }else printf("in parent process, send message: %s\n",argv[1]); wait(NULL); _exit(0); }

进程的管道通信

计算机操作系统实验第六次实验报告 学院:计算机科学与信息学院专业:通信工程班级:081姓名学号 实验 组 实验时间2010年11月17日指导教师成绩 实验项目名称进程的管道通信实 验目的 1、了解什么是管道; 2、熟悉UNIX/LINUX支持的管道通信方式。 实 验要求 1、了解管道的概念和管道的类型; 2、熟悉UNIX/LINUX支持的管道通信方式。 实 验 原 理 在管道通信时系统会调用:pipe( )建立一无名管道;read( );write( ) 。 实 验 仪 器 PC机或工作站一台; RedHat9.0操作系统;

实验步骤一、什么是管道 UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。 所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 句柄fd[0] 句柄fd[1] 读出端 写入端 二、管道的类型: 1、有名管道 一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。 2、无名管道 一个临时文件。利用pipe( )建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe( )的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。 二种管道的读写方式是相同的,本文只讲无名管道。 3、pipe文件的建立 分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符 4、读/写进程互斥 内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。 为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。 三、所涉及的系统调用 1、pipe( ) 建立一无名管道。 系统调用格式 pipe(filedes) 参数定义 int pipe(filedes); int filedes[2]; 其中,filedes[1]是写入端,filedes[0]是读出端。 该函数使用头文件如下: #include #inlcude #include 2、read( ) 系统调用格式

Linux下的进程间通信-详解

Linux下的进程间通信-详解 详细的讲述进程间通信在这里绝对是不可能的事情,而且笔者很难有信心说自己对这一部分内容的认识达到了什么样的地步,所以在这一节的开头首先向大家推荐著 名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文译本《UNIX环境高级编程》已有机械工业出版社出版,原文精彩,译文同样地道,如果你的确对在Linux下编程有浓 厚的兴趣,那么赶紧将这本书摆到你的书桌上或计算机旁边来。说这么多实在是难抑心中的景仰之情,言归正传,在这一节里,我们将介绍进程间通信最最初步和最 最简单的一些知识和概念。 首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来, 进程间通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。 2.3.1 管道 管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。 无名管道由pipe()函数创建: #include int pipe(int filedis[2]); 参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。 #define INPUT 0 #define OUTPUT 1 void main() { int file_descriptors[2]; /*定义子进程号 */ pid_t pid; char buf[256]; int returned_count; /*创建无名管道*/ pipe(file_descriptors); /*创建子进程*/ if((pid = fork()) == -1) { printf("Error in fork\n"); exit(1); } /*执行子进程*/ if(pid == 0) { printf("in the spawned (child) process...\n"); /*子进程向父进程写数据,关闭管道的读端*/ close(file_descriptors[INPUT]); write(file_descriptors[OUTPUT], "test data", strlen("test data"));

RocketMq消息队列实施计划方案-完整版

消息队列实施方案 1、背景 异步解耦合、给前端系统提供最高效的反应 2、常见消息队列对比 2、1 ActiveMq ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规的JMS Provider实现 优点: Java语言 支持集群模式 缺点: 性能在消息中间件中处于下游 2、2 Rabbitmq Rabbitmq是基于AMQP使用erlang语言实现的消息队列系统 优点: 1、完整的消息队列系统,支持多种消息队列模式,包括竞争消费; 2、支持集群模式,扩展集群容量和性能比较方便,集成了集群的监控和管理; 3、支持消息的持久化; 缺点: 1、需要学习比较复杂的接口和协议,比较耗费时间; 2、性能不是特别理想大概在1wqps左右; 3、使用Erlang语言,语言基础; 2、3 Kafka Kafka 是LinkedIn 开发的一个高性能、分布式的消息发布订阅系统。 优点: 1、分布式集群可以透明的扩展,增加新的服务器进集群。 2、高性能。单机写入TPS约在百万条/秒 3、容错。数据都会复制到几台服务器上。 缺点: 1、复杂性。Kafka需要zookeeper 集群的支持,T opic通常需要人工来创建,部署和维护较一般消息队列成本更高

定位于日志传输、存在消息丢失的肯能、消息乱序 3、消息发送错误无重试 2、4 RocketMQ RockerMq 是阿里公司中间件团队参考Kafka思想,用Java语言实现的消息传输系统 优点: 1、较高性能,单机写入TPS单实例约7万条/秒 2、容错,多种集群模式、可以解决容错问题 3、消息重试发送 4、顺序消息可以严格执行 缺点: 1、消息重复、消费端需要做去重操作 2、5 选用结论 从项目业务与团队技术偏向考虑,我们应该需要一种数据安全性比较高,保证每个消息都会被执行;有容错机制、支持集群模式高可用;性能不错,可以在毫秒级处理消息;支持顺序消息的消息中间件,RockerMq 可以满足这些要求。 3、RockerMq简介 3、1 RockerMq 产品介绍 参考阿里公司提供的《RocketMQ 开发指南》,最新版针对v3.2.4 3、2 RockerMq集群 3、2、1 部署方式 Rockermq共有四种部署方式,分别是: 1、单个Master 一旦Broker 重启或者宕机时,会导致整个服务不可用 2、多Master 模式 一个集群无Slave,全是Master,例如2 个Master 戒者3 个Master 优点: 1、配置简单, 2、容错,单个Master 宕机或重启维护对应用无影响,在磁盘配置为RAID10 时,即使机器宕机不可恢复情况下,由于RAID10 磁盘非常可靠,在同步刷盘时消息不会丢,异步刷盘丢失少量消息, 3、性能最高。

实验4 进程的管道通信

实验4 进程的管道通信 1. 目的 1)加深对进程概念的理解,明确进程和程序的区别。 2)进一步认识并发执行的实质。 3)分析进程争用资源的现象,学习解决进程互斥的方法。 4)学习解决进程同步的方法。 5)了解Linux系统中进程通信的基本原理。 进程是操作系统中最重要的概念,贯穿始终,也是学习现代操作系统的关键。通过本次实验,要求理解进程的实质和进程管理的机制。在Linux系统下实现进程从创建到终止的全过程,从中体会进程的创建过程、父进程和子进程之间的关系、进程状态的变化、进程之间的互斥、同步机制、进程调度的原理和以管道为代表的进程间的通信方式的实现。 2. 内容及要求 这是一个设计型实验,要求自行编制程序。 使用系统调用pipe()建立一条管道,两个子进程分别向管道写一句话: Child process1 is sending a message! Child process2 is sending a message! 父进程从管道读出来自两个子进程的信息,显示在屏幕上。 要求: 1)父进程先接收子进程1发来的消息,然后再接收子进程2发来的消息。 2)实现管道的互斥使用,当一个子进程正在对管道进行写操作时,另一子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对 管道的锁定。 3)实现父子进程的同步,当子进程把数据写入管道后,便去睡眠等待;当父进程试图从一空管道中读取数据时,也应等待,直到子进程将数据写入管道后,才将其唤醒。 3.相关的系统调用 1)fork() 用于创建一个子进程。 格式:int fork(); 返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 2)wait() 常用来控制父进程与子进程的同步。 在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。 返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 3)exit() 是进程结束时最常调用的。 格式:void exit( int status); 其中,status为进程结束状态。 4)pipe() 用于创建一个管道 格式:pipe(int fd); 其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型数组,fd[0]是管道的读端口,用

进程间的通信

实验三进程间的通信 【实验类型】 综合性实验 【目的要求】 学习如何利用管道机制、消息缓冲队列、共享存储区机制进行进程间的通讯,并加深对上述通信机制的理解。 【内容提要】 1、了解系统调用pipe()、msgget()、msgsnd()、msgrcv ()、msgctl()、shmget()、shmat()、shmdt()、shmctl()的功能和实现过程。 2、编写一段程序,使其用管道来实现父子进程之间的进程通讯。子进程向父进程发送自己的进程标识符,以及字符串“is sending a message to parent!”。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。 3、编写一段程序,使用系统调用fork()来创建两个子进程CLIENT进程和SERVER进程,使其用消息缓冲队列来实现CLIENT进程和SERVER进程之间的通信。SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接收到一条消息后显示一句“(server) received”。CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client) sent”。父进程在SERVER和CLIENT均退出后结束。 4、编写一个与3具有类似功能的程序,使其用共享存储区来实现两个进程之间的通讯。 【主要仪器设备】 每人一台计算机,硬件要求:CPU PII以上,64M内存,1OOM硬盘空间即可;软件要求: Linux操作系统。

实验三 进程间通信

实验三进程间通信(2学时) 一、实验目的 (1)了解什么是信号。 (2)熟悉LINUX系统中进程之间软中断通信的基本原理。 (3)熟悉LINUX支持的管道通信方式。 二、实验内容 (1)编写一段程序,使其现实进程的软中断通信。 即:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按 ctrl+c 键);当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止: Child Process11 is killed by Parent! Child Process12 is killed by Parent! 父进程等待两个子进程终止后,输出如下的信息后终止 Parent Process is killed! 要求:运行以下参考程序并分析结果。 <参考程序> #include #include #include #include void waiting(),stop(),alarming(); int wait_mark; main() { int p1,p2; if(p1=fork()) /*创建子进程p1*/ { if(p2=fork()) /*创建子进程p2*/ { //父进程 wait_mark=1; signal(SIGINT,stop); /*接收到^c信号,转stop*/

signal(SIGALRM,alarming);/*接受SIGALRM*/ waiting(); kill(p1,16); /*向p1发软中断信号16*/ kill(p2,17); /*向p2发软中断信号17*/ wait(0); /*同步*/ wait(0); printf("parent process is killed!\n"); exit(0); //会暂时停止目前进程的执行,直到有信号来到或子进程结束。 } else { wait_mark=1; signal(17,stop); signal(SIGINT,SIG_IGN); /*忽略 ^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process2 is killed by parent!\n"); lockf(1,0,0); exit(0); } } else { wait_mark=1; signal(16,stop); signal(SIGINT,SIG_IGN); /*忽略^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process1 is killed by parent!\n"); lockf(1,0,0); exit(0); } } void waiting() { sleep(5); if (wait_mark!=0) kill(getpid(),SIGALRM); } void alarming()

实验四进程的管道通信

实验四:进程的管道通信 1.实验目的 1)加深对进程概念的理解,明确进程和程序的区别。 2)学习进程创建的过程,进一步认识进程并发执行的实质。 3)分析进程争用资源的现象,学习解决进程互斥的方法。 4)学习解决进程同步的方法。 5)掌握Linux系统中进程间通过管道通信的具体实现。 2.实验内容 使用系统调用pipe()建立一条管道,系统调用fork()分别创建两个子进程,它们分别向管道写一句话,如: Child process1 is sending a message! Child process2 is sending a message! 父进程分别从管道读出来自两个子进程的信息,显示在屏幕上。 3.实验要求 这是一个设计型实验,要求自行、独立编制程序。 两个子进程要并发执行。 实现管道的互斥使用。当一个子进程正在对管道进行写操作时,另一个欲写入管道的子进程必须等待。 使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。 实现父子进程的同步,当父进程试图从一空管道中读取数据时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。 fork() 用于创一个子进程。格式:int fork();返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 wait() 常用来控制父进程与子进程的同步。在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 exit() 是进程结束时最常调用的。格式:void exit( int status); 其中,status为进程结束状态。 pipe() 用于创建一个管道格式:pipe(int fd);其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型数组,fd[0]是管道的读端口,用于从管道读出数据,fd[1]是管道的写端口,用于向管道写入数据。返回值:0 调用成功;-1 调用失败。 sleep() 使调用进程睡眠若干时间,之后唤醒。格式:sleep(int t);其中t为睡眠时间。 lockf() 用于对互斥资源加锁和解锁。在本实验中该调用的格式为: lockf(fd[1],1,0);/* 表示对管道的写入端口加锁。 lockf(fd[1],0,0);/* 表示对管道的写入端口解锁。

进程间的通信

# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 # 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 # 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 # 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。 # 套接字( socket ) :套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 windows进程通信的几种方式(转) 2008-10-13 16:47 1 文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。 Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。 应用程序有三种方法来使多个进程共享一个文件映射对象。 (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。 (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。 (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。 文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。 2 共享内存 Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射

ACE-[消息队列和消息块]

消息队列 线程安全的消息排队机制 如果MT_Synch被用于实例化Message_Queue,所有的公共方法都将是线程安全的,但同时也带来相应的开销。相反,如果Null_Synch类用于实例化Message_Queue,所有公共方法都不是线程安全的,同时也就没有额外的开销。只有一个线程,消息队列的模板同步参数被设置为空(ACE_NULL_SYNCH)。 如果队列中没有数据可用,它就进入休眠状态。如果有其他任务将消息插入它的队列,它就会苏醒过来,从队列中拾取数据并处理 它。因而,在这种情况下,接收任务将从发送任务那里接收消息,并以应用特定的方式作出反馈。(上面所说的前提是在 ACE_MT_SYNCH 下)如果是ACE_Null_Synch就没有这种相应的唤醒机制了!! ACE 中的每个任务(ACE_Task)都有一个底层消息队列(ACE_Message_Queue)。这个消息队列被用作任务间通信的一种方法。当一个任务想要与另一任务“谈话”时,它创建一个消息,并将此消息放入它想要与之谈话的任务的消息队列。接收任务通常用 getq()从消息队列里获取消息。 ACE_Activation_Queue: 默认采用ACE_Message_Queue< ACE_MT_SYNCH > 保存数据 1. ACE_Method_Request * ACE_Activation_Queue::dequeue (ACE_Time_Value *tv = 0);如果tv没有值;则阻塞如果tv有 值,超时返回EWOULDBLOCK。 2. ACE_Message_Queue 采用双向链表组织结构 1. 指定队列是否同步化(ACE_MT_SYNCH 、 ACE_NULL_SYNCH) 2. 流控 统计所有block的字节总数cur_bytes_,在新消息放到队列之前,判断字节总数是否高于高水位标(缺省高低水

实验二_进程间通信

实验二 进程间通信
一、实验目的 在本实验中,通过对文件映射对象的了解,来加深对 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,观察运行结果,并阅读和分析实验程序.

相关主题