不知道这一年是我感觉过的快,还是大家都觉得过的快。
转眼2018年就要过去了,很长一段时间(大概三四个月)没有写博客了,随着学的东西越来越多,也越来越觉得自己是那么的无知。也就不知道该怎么下笔才好,昨天晚上肝完了操作系统实验作业,准备也写一篇博客。
现在,要开始准备明年的春招了,博客也会开始慢慢更新。
操作系统实验报告
一、 实验名称:Linux进程通信
二、 实验目的:掌握Linux进程通信的各种方式
三、 实验内容:软中断+消息队列
四、 程序清单
客户端:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/shm.h>
#include<signal.h>
#define MSG_SIZE 128
int wait_mark;
int waiting()
{
while(wait_mark != 0);
}
void stop()
{
wait_mark = 0;
}
struct msgbuf
{
long mtype;
char mtext[MSG_SIZE];
};
int main()
{
int senderPid = -1;
int qid;
key_t key;
int ret;
struct msgbuf buf;
/*
函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键
*/
key=ftok("/home/oswork", 'jzzzz'); //可以自定义,这里调用ftok函数生成消息队列的键值
if (key<0)
{
perror("ftok error");
exit(1);
}
qid=msgget(key, IPC_CREAT|0666);
if (qid<0)
{
perror("msgget error");
exit(1);
}
int flag = 1;
while (1)
{
if(flag == 1)
{
flag = 0;
printf("input the message:");
fgets(buf.mtext,MSG_SIZE,stdin);
if (strncmp(buf.mtext, "exit",4)==0)
{
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
msgctl(qid, IPC_RMID,0);
break;
}
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
if (ret<0)
{
perror("msgsnd error");
exit(1);
}
else
{
printf("send!\n");
}
wait_mark = 1;
signal(17, stop);
waiting();
}
else
{
flag = 1;
memset(&buf, 0, sizeof(buf));
if(senderPid == -1)
{
ret=msgrcv(qid, &buf, MSG_SIZE, 0, 0);
}
else
{
ret=msgrcv(qid, &buf, MSG_SIZE, senderPid, 0);
}
if(senderPid == -1)
{
senderPid = buf.mtype;
}
wait_mark = 1;
if (ret<0)
{
perror("msgrcv error");
exit(1);
}
else
{
if (strncmp(buf.mtext, "exit",4)==0)
{
msgctl(qid, IPC_RMID,0);
break;
}
printf("received message:\n");
printf("text:%s\n",buf.mtext);
kill(senderPid, 17);
}
}
}
return 0;
}
服务端:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<sys/shm.h>
#include<signal.h>
#define MSG_SIZE 128
int wait_mark;
int qid;
int waiting()
{
while(wait_mark != 0);
}
void stop()
{
wait_mark = 0;
}
void sig_handler()
{
msgctl(qid, IPC_RMID,0);
}
struct msgbuf
{
long mtype;
char mtext[MSG_SIZE];
};
int main()
{
int senderPid = -1;
int recPid = -1;
key_t key;
int ret;
struct msgbuf buf;
key=ftok("/home/oswork", 'jzzzz');
if (key<0)
{
printf("ftok error");
exit(1);
}
qid=msgget(key,IPC_EXCL|0666);
if (qid<0)
{
perror("msgget error");
exit(1);
}
int flag = 0;
while (1)
{
if(flag == 0)
{
flag = 1;
memset(&buf, 0, sizeof(buf));
if(senderPid == -1)
{
ret=msgrcv(qid, &buf, MSG_SIZE, 0, 0);
}
else
{
ret=msgrcv(qid, &buf, MSG_SIZE, senderPid, 0);
}
if(senderPid == -1)
{
senderPid = buf.mtype;
}
wait_mark = 1;
if (ret<0)
{
perror("msgrcv error");
exit(1);
}
else
{
if (strncmp(buf.mtext, "exit",4)==0)
{
break;
}
printf("received message:\n");
printf("text:%s\n",buf.mtext);
kill(senderPid, 17);
}
}
else
{
flag = 0;
printf("input the message:");
signal(SIGINT, sig_handler);
fgets(buf.mtext,MSG_SIZE,stdin);
if (strncmp(buf.mtext, "exit",4)==0)
{
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
break;
}
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
if (ret<0)
{
perror("msgsnd error");
exit(1);
}
else
{
printf("send!\n");
}
wait_mark = 1;
signal(17, stop);
waiting();
}
}
return 0;
}
五、 程序测试
六、 实验总结
最后,选择了“软中断+消息队列”的方式来实现了一个简单的聊天工具。
可以实现两人的在线聊天:
- 一人一句,不能争抢,这个机制是通过软中断信号处理来实现的,kill向指定进程发送信号,signal接受信号并进行相应的处理。
- 两人间对话消息的传递是通过消息队列实现的,选择消息队列而没有用管道通信是因为管道通信是按FIFO的方式单向传递的,而且只允许在建立者和子进程间使用。消息队列允许一个或多个进程向它读写消息,而且允许随机读取(按消息的类型读取,比如我程序中将消息队列标识符id就设置成了等于进程标识符pid),不是FIFO。
- 功能:
a) 有两个会话A,B,
b) 一开始会话A处于等待键盘输入发送给B消息的状态,会话B处于等待接受A的发送消息的状态。
c) 键盘输入消息,A成功发送,A的状态变为等待接受B的消息,B成功接受,B的状态变为等待键盘输入文字发送给A消息。
d) 重复b,c环节
e) 直到一方输入”exit”字符串,双方同时结束会话,并且清空消息队列。
在实验中也遇到了一些问题,其中比较麻烦的是:
在我写好代码调试运行的时候,因为一方A是发送端,另一方B肯定是接受端,但一开始两边程序刚开始运行的时候,在键盘还没有输入任何字符时,接收端B会莫名其妙的“收到消息”!!!
但这个消息又不是A发送过来的,那就造成了B不能给A发送信号,告诉它“我已经收到了你的消息”,所以A还是处于等待键盘输入发送消息的状态。但此时B因为收到了消息,已经变成了等待键盘输入发送消息的状态。这样就造成了A在等B接受到消息而发送回信号,同时B也在等A接受到消息而发送回信号。但此时因为两边都是等待键盘输入准备发送消息的状态,而没有接受消息的一端,无论在A、B哪一端发消息,另一端都不会收到,僵持的状态也不会改变。
这样就造成了死锁。
重新检查了一遍代码,发现了问题出在了我没有在每次会话结束后,把消息队列清空。
于是,我在两个地方增加了清空消息队列的代码:
1. 会话正常结束,即我在程序中写的通过发送“exit”字符串的方式主动结束会话,在结束之前调用了msgctl(qid, IPC_RMID,0);来清除队列中的消息
if (strncmp(buf.mtext, "exit",4)==0)
{
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
msgctl(qid, IPC_RMID,0);
break;
}
2. 会话强制结束,即键盘按下Ctrl + C结束会话。通过软中断来实现。
void sig_handler()
{
msgctl(qid, IPC_RMID,0);
}
signal(SIGINT, sig_handler);