实验八 Linux进程通信实验
学号:6130116217
专业班级:计算机科学与技术165班
课程名称:Linux程序设计实验
一、实验项目名称
Linux进程通信实验
二、实验目的
熟练掌握利用管道、消息队列以及共享内存方式实现进程之间的通信。
三、实验基本原理
Linux进程通信
四、主要仪器设备及耗材
硬件: PC机;
软件:Windows OS,VMware,Fedora10.0或其他Linux发行版。
五、实验步骤
1. 编写一个聊天程序,要求分别采用管道、消息队列以及共享内存三种不同方式实现。
①管道方式
进程一:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char* argv[])
{//c1->cl2 cl2->cl1
int fd_send=open(argv[1],O_WRONLY);
int fd_recv=open(argv[2],O_RDONLY);
if(-1==fd_send||-1==fd_recv)
{
perror("open");
exit(1);
}
printf("ok!\n");
char buf[1024];
while(memset(buf,0,1024),read(0,buf,1024)!=0)
{
write(fd_send,buf,strlen(buf));
printf("begin read from pipe...\n");
memset(buf,0,1024);
read(fd_recv,buf,1024);
printf("from cl2:%s",buf);
}
close(fd_send);
close(fd_recv);
printf("pipe exit!\n");
return 0;
进程二:
int main(int argc,char* argv[])
{//cl2->cl1 cl1->cl2
int fd_recv=open(argv[2],O_RDONLY);
int fd_send=open(argv[1],O_WRONLY);
if(-1==fd_send||-1==fd_recv)
{
perror("open");
exit(1);
}
printf("ok!\n");
char buf[1024];
while(printf("begin read from pipe...!\n"), memset(buf,0,1024),read(fd_recv,buf,1024)!=0)
{
printf("from cl1:%s",buf);
memset(buf,0,1024);
read(0,buf,1024);
write(fd_send,buf,strlen(buf));
}
close(fd_send);
close(fd_recv);
printf("pipe exit!\n");
return 0;
}
运行结果
②消息队列
进程1:
//这是一个以system V消息队列实现的聊天程序客户端
1.创建消息队列
2.从消息队列中获取一个数据,打印出来
3.从标准输入中获取一个数据,组织成消息队列节点发送
4.删除消息队列
接口:
创建 msgget
接收数据 msgrcv
发送数据 msgsnd
控制 msgctl
int msgget(key_t key,int msgflg);
msgflg:
key:内核中消息队列的标识
IPC_CREAT 不存在则创建,存在则打开
IPC_EXCL 与IPC_CREAT 同用,若存在则报错 防止程序重复启动
mod 权限
返回值:一个代码操作的句柄:失败:-1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgid:msgget返回的操作句柄
smgp:用于接收数据
msgsz:指定接收的数据大小
msgtyp:指定接收的数据类型
msgflg:标准选项,0
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define IPC_KEY 0x12345678
//下面两个宏,用于我们赋值我们传输的数据块类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1024]; /* message data */
};
int main()
{
int msgid=-1;
//ftok
// //key_t ftok(const char *pathname, int proj_id);
// //ftok通过文件的inode节点号和一个proj_id计算出一个key值
// //缺点:如果文件被删除,或者替换,那么将打开的不是同一个消息队列
//
//1.创建消息队列
msgid=msgget(IPC_KEY,IPC_CREAT | 0664);
if(msgid<0){
perror("msgget error");
return -1;
}
while(1){
//2.接收数据
//struct msgbuf这个结构体需要我们自己定义
struct msgbuf buf;
//msgrcv:默认阻塞的获取数据
//msgid:操作句柄
//
//buf:接收数据的结构体,自己定义
//1024 用于指定接收的数据的最大长度,不包含mtype
//TYPE_C 用于指定接收的数据类型
//msgflag 0默认
// MSG_NOERROR 当数据长度超过指定长度,则截断数据
msgrcv(msgid,&buf,1024,TYPE_C,0);
printf("client say:%s\n",buf.mtext);
//发送数据
memset(&buf,0x00,sizeof(struct msgbuf));
buf.mtype=TYPE_S;
scanf("%s",buf.mtext);
msgsnd(msgid,&buf,1024,0);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
进程2:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define IPC_KEY 0x12345678
//下面两个宏,用于我们赋值我们传输的数据块类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1024]; /* message data */
};
int main()
{
int msgid=-1;
//ftok
// //key_t ftok(const char *pathname, int proj_id);
// //ftok通过文件的inode节点号和一个proj_id计算出一个key值
// //缺点:如果文件被删除,或者替换,那么将打开的不是同一个消息队列
//
//1.创建消息队列
msgid=msgget(IPC_KEY,IPC_CREAT | 0664);
if(msgid<0){
perror("msgget error");
return -1;
}
while(1){
struct msgbuf buf;
//发送数据
memset(&buf,0x00,sizeof(struct msgbuf));
buf.mtype=TYPE_C;
scanf("%s",buf.mtext);
msgsnd(msgid,&buf,1024,0);
//2.接收数据
//struct msgbuf这个结构体需要我们自己定义
//msgrcv:默认阻塞的获取数据
//msgid:操作句柄
//
//buf:接收数据的结构体,自己定义
//1024 用于指定接收的数据的最大长度,不包含mtype
//TYPE_C 用于指定接收的数据类型
//msgflag 0默认
// MSG_NOERROR 当数据长度超过指定长度,则截断数据
msgrcv(msgid,&buf,1024,TYPE_S,0);
printf("server say[%s]\n",buf.mtext);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
运行结果:
③共享内存
进程1:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
typedef struct {
char str1[50];
char str2[50];
int no1,no2;
} str;
main(int argc, char** argv) {
int shm_id,i;
key_t key;
str *p_map;
time_t timep;
struct tm *p;
char* name = "/dev/shm/myshm2";
key = ftok(name,0); /*调用ftok 函数,产生标准的key*/
shm_id=shmget(key,4096,IPC_CREAT); /*调用shmget 函数,获取共享内存区域的ID*/
if(shm_id==-1) {
perror("获取共享内存区域的ID 出错");
return;
}
p_map=(str*)shmat(shm_id,NULL,0);/* 调用shmat 函数,映射共享内存*/
(*p_map).no1=0;
(*p_map).no2=0;
printf("This is A.\n");
fgets((*p_map).str2,sizeof((*p_map).str2),stdin);
(*p_map).no1=1;
while(1) {
if ((*p_map).no2==1) {
time(&timep);
gmtime(&timep);
p=localtime(&timep);
printf("B:%s%d:%d:%d\n",(*p_map).str1,p->tm_hour,p->tm_min,p->tm_sec);
(*p_map).str1[0]='\0';
(*p_map).no2=0;
} else continue;
if ((*p_map).no1==0) {
fgets((*p_map).str2,sizeof((*p_map).str2),stdin);
(*p_map).no1=1;
}
}
if(shmdt(p_map)==-1) /*调用shmdt 函数,解除进程对共享内存区域的映射*/
perror("解除映射出错");
}
进程2:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
typedef struct {
char str1[50];
char str2[50];
int no1,no2;
} str;
main(int argc, char** argv) {
int shm_id,i;
key_t key;
str *p_map;
time_t timep;
struct tm *p;
char* name = "/dev/shm/myshm2";
key = ftok(name,0); /*调用ftok 函数,产生标准的key*/
shm_id = shmget(key,4096,IPC_CREAT); /*调用shmget 函数,获取共享内存区域的ID*/
if(shm_id == -1) {
perror("获取共享内存区域的ID 出错");
return;
}
p_map = (str*)shmat(shm_id,NULL,0);
(*p_map).no1=0;
(*p_map).no2=0;
printf("This is B.\n");
while(1) {
if ((*p_map).no1==1) {
time(&timep);
gmtime(&timep);
p=localtime(&timep);
printf("A:
%s%d:%d:%d\n",(*p_map).str2,p->tm_hour,p->tm_min,p->tm_sec);
(*p_map).str2[0]='\0';
(*p_map).no1=0;
} else continue;
if ((*p_map).no2==0) {
fgets((*p_map).str1,sizeof((*p_map).str1),stdin);
(*p_map).no2=1;
}
}
if(shmdt(p_map) == -1) /*调用shmdt 函数,解除进程对共享内存区域的映射*/
perror("解除映射出错");
}
运行结果:
2. 调试教材P285页例7.9
vim input.c
#include <string.h>
#include <stdio.h>
#include <ctype.h>
int main()
{
int c;
//用户输入数据
while ((c = getchar()) != EOF)
{
if (isupper(c))
{ c = tolower(c); }
//输出数据作为testinput的输入数据
if (putchar(c) == EOF)
{ fputs("output error\n", stdout); }
if (c == '\n')
{ fflush(stdout); }
}
return 0;
}
vim 2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAXLINE 100
int main()
{
char line[MAXLINE];
FILE *fpin;
//popen的第一个参数是命令,这里执行一个可执行文件
if ((fpin = popen("./input", "r")) == NULL)
{
fputs("popen error\n", stdout);
}
for ( ; ; )
{
fputs("prompt> ", stdout);
fflush(stdout);
if (fgets(line, MAXLINE, fpin) == NULL)
{ break; }
if (fputs(line, stdout) == EOF)
{ fputs("fputs error to pipe", stdout); }
}
if (pclose(fpin) == -1)
{ fputs("pclose error\n", stdout); }
puts("");
exit(0);
}
编译运行
六、实验数据及处理结果
七、思考讨论题
对管道、消息队列以及共享内存三种不同进程通信方法进行比较
答:
1.管道(Pipe):管道可用于具有亲缘关系进程间的通信,有名管道,除了具有管道所具有功能外,它
还允许无亲缘关系进程的通信。
2.消息队列:(Messge Queue):消息队列是消息的连接表,包括Posix消息队列SystemV消息队列,它克服了前两种通信方式中信息量有限的的缺点,具有写权限的进程可以按照一定的规划向消息队列中添加新的消息,对消息队列有读权限的进程则可以从消息队列中读取消息。
3.共享内存(Shared memory):可以说这是最有用的进程通信方式,它使得多个进程可以访问同一块内存空间,
不同进程可以及时看到对方进程中对共享内存中数据的更新,这种通信方式需要依靠某种同步机制,如互斥锁和信号量等.
八、参考资料
1.金国庆等,Linux程序设计(第二版),浙江大学出版社,2014年4月
2. Neil Matthew,《Linux程序设计》(第4版), 人民邮电出版社,2014年9月
3. 杨宗德,《Linux高级程序设计》(第三版),人民邮电出版社,2012年11月
4. Daniel P.,《深入理解Linux内核》(第三版),中国电力出版社,2013年1月