写给看这篇博客的杭电学弟:
按理说操作系统实验应该自己做,这样能锻炼自己。鉴于我的报告还是比较有参考价值,能让以后的同学参考一下,就做成md的形式。
(1)实验名:Linux进程管理
(2)实验要求:
1)实现一个模拟的shell
2)实现一个管道通信程序
3)利用Linux消息队列通信机制实现两个线程间的通信
4)利用Linux共享内存通信机制实现两个进程间的通信
pid = fork()会返回多个值,只需在fork()后使用多个判断语句即可。
pid<0表示错误,我打印error之后退出
pid=0表示子进程运行,使用execl替换进程,替换为我们想要的进程,如cmd.o。
pid>0表示父进程运行,使用wait(NULL)函数等待所有子进程的退出。
效果见实验结果(1)
代码见附录demo1
使用7个信号量:
Mutex = sem_open(“Mutex”, O_CREAT, 0666, 1);
send1 = sem_open(“send1”, O_CREAT, 0666, 1);
send2 = sem_open(“send2”, O_CREAT, 0666, 1);
send3 = sem_open(“send3”, O_CREAT, 0666, 1);
receive1 = sem_open(“receive1”, O_CREAT, 0666, 0);
receive2 = sem_open(“receive2”, O_CREAT, 0666, 0);
receive3 = sem_open(“receive3”, O_CREAT, 0666, 0);
1)
建立无名管道:
int fd[2];
int ret = pipe(fd);//无名管道
2)先fork三个子进程,编写各自的操作
每个子进程等待各自的send信号量,再等待Mutex信号量,进行,完成后释放各自的receiver:
P(send)
P(Mutex)
。。。发送内容到管道。。。
V(receiver)
V(Mutex)
父进程等待三个receiver信号量,接收管道内容后,释放三个send信号量:
P(receiver1)
P(receiver2)
P(receiver3)
P(Mutex)
。。。发送内容到管道。。。
V(receiver1)
V(receiver2)
V(receiver3)
V(Mutex)
效果见实验结果(2)
代码见附录demo2
四个信号量:
sem_send = sem_open(“send”, O_CREAT, 0666, 1);
sem_receive = sem_open(“receive”, O_CREAT, 0666, 0);
sem_over1 = sem_open(“over1”, O_CREAT, 0666, 0);
sem_over2 = sem_open(“over2”, O_CREAT, 0666, 0);
发送:(以sender1为例)
While(1)
{
P(send)
发送消息给接收消息队列
if(发送的是”exit”)
Break;
P(receive)
}
P(over1)
发送退出消息
V(send)
退出
接收:
Int flag1 = 0;
Int flag2 = 0;
While(1)
{
P(receive)
发送消息给接收消息队列
if(接收到的是1发来的”exit”)
Flag1 = 1
V(over1)
if(接收到的是2发来的”exit”)
Falg2 = 1
V(over2)
P(send)
If(flag1 && flag2)
Break;
}
退出
代码见附录demo3
1)
共享内存中放的是字符串
支持不断地发送,不断地接收(加接收延时即可)
在发送消息中,第一位表示是哪个发送者发送的,以此区分发送者。
2)
sem_send = sem_open(“send”, O_CREAT, 0666, 2);
sem_receive = sem_open(“receive”, O_CREAT, 0666, 0);
sem_over1 = sem_open(“over1”, O_CREAT, 0666, 0);
sem_over2 = sem_open(“over2”, O_CREAT, 0666, 0);
发送:
While(1)
P(send)
发送信息
V(receive)
P(over1)
显示传回信息
V(send)
接收:
Int flag1 = 0;
Int flag2 = 0;
While(1)
P(receive)
If(是sender1发送的)
If(发送的是“exit”)
Flag1 = 1
V(over1)
If(是sender2发送的)
If(发送的是“exit”)
Falg2 = 1
V(over2)
(1)实验一
遇到的问题及解决:一开始父进程没有等待子进程结束导致出错
(2)实验二
经测试,可以做到不同顺序!.:
遇到的问题及解决:一开始连续三个fork(),其实创建了孙子进程,并不是三个并列的子进程,正确写法是:
pid1 = fork();
if(pid1 > 0)
{
pid2 = fork();
if(pid2 > 0)
{
pid3 = fork();
}
}
遇到的问题及解决:接收的数据乱码。原因是当初放数据时,没有考虑‘\0’的存在,应该合理设计,只在最后加‘\0’。
(3)实验三
支持多终端发送,单终端接收。
发送顺序不是固定。
遇到的问题及解决:无法申请到消息队列,试过各种方法,都是返回-1。重启就行了,估计是调试太多次,把能用的消息队列都搞没了。
(4)实验四
支持多终端发送,单终端接收。
发送顺序不是固定。
四、实验总结
这个实验我是最早验收成功的。一开始,投机求巧,只完成第四个小实验的多终端,而第三个小实验依旧使用单终端多线程(参考网上的),刚好贾老师出差就找赵伟华老师验收,是想让自己碰碰运气,结果运气不好只演示第三个,结果分数有点低。一开始的心态的确和大多数人一样,觉得水水过就好了,后来看到室友都实现了第三个实验的多终端,我也不甘示弱,还是把第三个小实验重写了一下。
(1)Demo1
#include
#include
#include
#include
int main()
{
char input[20];
pid_t pid;
// printf("输入要运行的程序名$\n");
// scanf("%s", input);
while(1)
{
printf("输入要运行的程序名$\n");
scanf("%s", input);
if(strcmp(input,"exit") == 0)
{
printf("父进程退出\n");
printf("\n进程结束,pid:%d\n", getpid());
exit(0);
}
else if(strcmp(input,"cmd3") == 0 || strcmp(input,"cmd2") == 0 || strcmp(input,"cmd1") == 0)
{
//创建子进程
pid = fork();
if(pid < 0)
{
printf("vfork() error\n");
exit(-1);
}
else if(pid == 0)
{
printf("i am the child process, my process id is %d\n",getpid());
char path[80] = "../t1/";
char *lastName = ".o";
strcat(path, input);
strcat(path, lastName);
execl(path,"",NULL);
}
else
{
printf("i am the parent process, my process id is %d\n",getpid());
pid_t temp = wait(NULL);
printf("\n进程结束,pid:%d\n", temp);
}
}
else
{
printf("Command not found\n");
// printf("输入要运行的程序名$\n");
// scanf("%s", input);
continue;
}
}
return 0;
}
(2)Demo2
#include
#include
#include
#include
#include //信号量头文件
#include
#define MAX_PIPE_CAPACIPY 100
int main()
{
int fd[2];
sem_t *Mutex;
sem_t *send1, *send2, *send3;
sem_t *receive1, *receive2, *receive3;
sem_unlink("Mutex");
sem_unlink("send1");
sem_unlink("send2");
sem_unlink("send3");
sem_unlink("receive1");
sem_unlink("receive2");
sem_unlink("receive3");
Mutex = sem_open("Mutex", O_CREAT, 0666, 1);
send1 = sem_open("send1", O_CREAT, 0666, 1);
send2 = sem_open("send2", O_CREAT, 0666, 1);
send3 = sem_open("send3", O_CREAT, 0666, 1);
receive1 = sem_open("receive1", O_CREAT, 0666, 0);
receive2 = sem_open("receive2", O_CREAT, 0666, 0);
receive3 = sem_open("receive3", O_CREAT, 0666, 0);
int ret = pipe(fd);//无名管道
if (ret == -1)
{
printf("pipe create error\n");
exit(-1);
}
pid_t pid1;
pid_t pid2;
pid_t pid3;
// pid1 = fork();
// pid2 = fork();
// pid3 = fork();
pid1 = fork();
if(pid1 > 0)
{
pid2 = fork();
if(pid2 > 0)
{
pid3 = fork();
}
}
if(pid1 < 0 || pid2 < 0 || pid3 < 0)
{
sem_unlink(Mutex);
sem_unlink(send1);
sem_unlink(send2);
sem_unlink(send3);
sem_unlink(receive1);
sem_unlink(receive2);
sem_unlink(receive3);
exit(0);
}
if(pid1 == 0)
{
close(fd[0]);
sem_wait(send1);
sem_wait(Mutex);
char buf[3];
memset(buf, '1', 3);
write(fd[1], buf, sizeof(buf));
printf("pid:%d 进程1放入数据:%s\n",getpid(),"111");
sleep(1);
sem_post(receive1);
sem_post(Mutex);
// exit(0);
}
if(pid2 == 0)
{
close(fd[0]);
sem_wait(send2);
sem_wait(Mutex);
char buf[3];
memset(buf, '2', 3);
write(fd[1], buf, sizeof(buf));
printf("pid:%d 进程2放入数据:%s\n",getpid(),"222");
sleep(1);
sem_post(receive2);
sem_post(Mutex);
}
if(pid3 == 0)
{
close(fd[0]);
sem_wait(send3);
sem_wait(Mutex);
char buf[3]="33";
write(fd[1], buf, sizeof(buf));
printf("pid:%d 进程3放入数据:%s\n",getpid(),buf);
sleep(1);
sem_post(receive3);
sem_post(Mutex);
}
else
{
close(fd[1]);
sem_wait(receive1);
sem_wait(receive2);
sem_wait(receive3);
sem_wait(Mutex);
char str[1024];
read(fd[0], str, 1024);
printf("pid:%d 父进程接收数据:%s\n", getpid(), str);
sleep(1);
sem_post(send1);
sem_post(send2);
sem_post(send3);
sem_post(Mutex);
exit(0);
}
return 0;
}
(3)Demo3
//线程间的通信s_r.h
//IPC消息队列
#include
#include
#include
#include
#include
#include
#include
#include
#include //信号量头文件
sem_t *sem_send;
sem_t *sem_receive;
sem_t *sem_over1;
sem_t *sem_over2;
//缓冲区mymsg
struct my_msgbuf
{
long mtype;//消息类型1为发送者的消息,2为接收的消息。
int sendid;//1为发送者1,2为发送者2。
char mtext[100];
};
void init_signal()
{
//初始化信号量
sem_send = sem_open("send", O_CREAT, 0666, 1);
sem_receive = sem_open("receive", O_CREAT, 0666, 0);
sem_over1 = sem_open("over1", O_CREAT, 0666, 0);
sem_over2 = sem_open("over2", O_CREAT, 0666, 0);
}
//sender1.c
#include "s_r.h"
int main()
{
int msgid;//消息队列标识符
scanf("%d", &msgid);
printf("%d\n", msgid);
init_signal();
char str[100];
struct my_msgbuf s_msg;//消息发送区
s_msg.mtype = 1;
s_msg.sendid=1;
//不断发送
while(1)
{
memset(str, '\0', strlen(str));
printf("\ntid:%u 线程1发送: ", (unsigned int)pthread_self());
scanf("%s", str);
sem_wait(sem_send);
if(strcmp(str, "exit") == 0)
{
strcpy(s_msg.mtext, "end1");
msgsnd(msgid, &s_msg, sizeof(struct my_msgbuf), 0);
sem_post(sem_receive);
break;
}
strcpy(s_msg.mtext, str);
printf("%s", s_msg.mtext);
msgsnd(msgid, &s_msg, sizeof(struct my_msgbuf), 0);
sem_post(sem_receive);
}
sem_wait(sem_over1);
struct my_msgbuf r_msg;
msgrcv(msgid, &r_msg, sizeof(struct my_msgbuf), 0, 0);
printf("****线程1收到线程%d的: %20s\n", r_msg.sendid, r_msg.mtext);
sem_post(sem_send);
return 0;
// pthread_exit(NULL);//线程终止
}
//sender2.c
#include "s_r.h"
int main()
{
int msgid;//消息队列标识符
scanf("%d", &msgid);
printf("%d\n", msgid);
init_signal();
char str[100];
struct my_msgbuf s_msg;//消息发送区
s_msg.mtype = 1;
s_msg.sendid= 2;
//不断发送
while(1)
{
printf("\ntid:%u 线程2发送: ", (unsigned int)pthread_self());
scanf("%s", str);
sem_wait(sem_send);
if(strcmp(str, "exit") == 0)
{
strcpy(s_msg.mtext, "end2");
msgsnd(msgid, &s_msg, sizeof(struct my_msgbuf), 0);
sem_post(sem_receive);
break;
}
strcpy(s_msg.mtext, str);
msgsnd(msgid, &s_msg, sizeof(struct my_msgbuf), 0);
sem_post(sem_receive);
}
sem_wait(sem_over2);
struct my_msgbuf r_msg;
msgrcv(msgid, &r_msg, sizeof(struct my_msgbuf), 0, 0);
printf("****线程2收到线程%d的: %20s\n", r_msg.sendid, r_msg.mtext);
sem_post(sem_send);
return 0;
// pthread_exit(NULL);//线程终止
}
//receiver.c
#include "s_r.h"
int main()
{
int msgid;//消息队列标识符
msgid = msgget(IPC_PRIVATE, 0666|IPC_CREAT);
printf("%d\n", msgid);
sem_unlink("send");
sem_unlink("receive");
sem_unlink("over1");
sem_unlink("over2");
init_signal();
struct my_msgbuf r_msg;//消息接受区
struct my_msgbuf s_msg;
s_msg.mtype = 2;
s_msg.sendid = 3;
int flag_over1 = 0;
int flag_over2 = 0;
while (1)
{
sem_wait(sem_receive);
msgrcv(msgid, &r_msg, sizeof(struct my_msgbuf), 0, 0);
printf("********线程3收到线程%d的: %20s\n", r_msg.sendid, r_msg.mtext);
if (r_msg.sendid == 1)
{
if (strcmp(r_msg.mtext, "end1") == 0)
{
printf("发送over1\n");
strcpy(s_msg.mtext, "over1");
msgsnd(msgid, &s_msg, strlen(s_msg.mtext), 0);
sem_post(sem_over1);
flag_over1 = 1;
}
else
{
sem_post(sem_send);
}
}
else if(r_msg.sendid == 2)
{
if (strcmp(r_msg.mtext, "end2") == 0)
{
printf("发送over2\n");
strcpy(s_msg.mtext, "over2");
msgsnd(msgid, &s_msg, sizeof(struct my_msgbuf), 0);
sem_post(sem_over2);
flag_over2 = 1;
}
else
{
sem_post(sem_send);
}
}
if (flag_over1 && flag_over2)
break;
}
sem_unlink("send");
sem_unlink("receive");
sem_unlink("over1");
sem_unlink("over2");
return 0;
}
Makefile:
all: sender1 sender2 receiver
sender1: sender1.c
gcc sender1.c -pthread -o sender1
sender2: sender2.c
gcc sender2.c -pthread -o sender2
receiver: receiver.c
gcc receiver.c -pthread -o receiver
(4)Demo4
//sender1创建共享内存
#include "../t4/s_r.h"
int main()
{
//初始化信号量
sem_unlink("send");
sem_unlink("receive");
sem_unlink("over1");
sem_unlink("over2");
init_signal();
// printf("shmid: %d, shmp");
//初始化共享内存
memset((char *)shmp, '\0', MEM_MIN_SIZE);
char s_str[100];
char temp[MEM_MIN_SIZE];
while(1)
{
printf("输入你要发送的信息:\n");
scanf("%s", s_str);
sem_wait(sem_send);
//输入到共享内存中
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
char t[2] = "1";
strcat(temp, t);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
sem_post(sem_receive);
sem_wait(sem_over1);
char r_str[100];
strcpy(r_str, (char *)shmp);
printf("接收到: %s\n", r_str);
if(strcmp(r_str, "over1") == 0)
{
break;
}
sem_post(sem_send);
}
printf("退出sender1!!\n");
shmdt(shmp);
exit(0);
}
//sender2创建共享内存
#include "../t4/s_r.h"
int main()
{
init_signal();
// printf("shmid: %d, shmp");
//初始化共享内存
memset((char *)shmp, '\0', MEM_MIN_SIZE);
char s_str[100];
char temp[MEM_MIN_SIZE];
while(1)
{
printf("输入你要发送的信息:\n");
scanf("%s", s_str);
sem_wait(sem_send);
//输入到共享内存中
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
char t[2] = "2";
strcat(temp, t);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
sem_post(sem_receive);
sem_wait(sem_over2);
char r_str[100];
strcpy(r_str, (char *)shmp);
printf("接收到: %s\n", r_str);
if(strcmp(r_str, "over2") == 0)
{
break;
}
sem_post(sem_send);
}
printf("退出sender2!!\n");
shmdt(shmp);
exit(0);
}
//receiver
#include "../t4/s_r.h"
int main()
{
init_signal();
int flag_end1 = 0;
int flag_end2 = 0;
char r_str[100];
char temp[MEM_MIN_SIZE];
while(1)
{
sem_wait(sem_receive);
char recv[100];
strcpy(recv, (char *)shmp);
strncpy(r_str, recv+1, strlen(recv));
printf("从sender%c 接收到: %s\n",recv[0],r_str);
if(recv[0] == '1')
{
if(strcmp(r_str, "exit") == 0)
{
char s_str[100]="over1";
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
flag_end1 = 1;
}
else
{
char s_str[100]="receiver success";
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
}
sem_post(sem_over1);
}
if(recv[0] == '2')
{
if(strcmp(r_str, "exit") == 0)
{
char s_str[100]="over2";
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
flag_end2 = 1;
}
else
{
char s_str[100]="receiver success";
memset((char *)shmp, '\0', MEM_MIN_SIZE);
strcpy(temp, (char *)shmp);
strcat(temp, s_str);
strcpy((char *)shmp, temp);
}
sem_post(sem_over2);
}
if(flag_end1 == 1 && flag_end2 == 1)
{
break;
}
}
sem_unlink("send");
sem_unlink("receive");
sem_unlink("over1");
sem_unlink("over2");
shmdt(shmp);
shmctl(shmid, IPC_RMID, NULL);
printf("接收端退出!!\n");
exit(0);
}
//s_r.h
#include
#include
#include
#include
#include
#include //信号量头文件
#include
#define MEM_MIN_SIZE 1024
#define KEY 1111
sem_t *sem_send;
sem_t *sem_receive;
sem_t *sem_over1;
sem_t *sem_over2;
key_t key;
int shmid;
void *shmp;
void init_signal()
{
//初始化信号量
sem_send = sem_open("send", O_CREAT, 0666, 2);
sem_receive = sem_open("receive", O_CREAT, 0666, 0);
sem_over1 = sem_open("over1", O_CREAT, 0666, 0);
sem_over2 = sem_open("over2", O_CREAT, 0666, 0);
shmid = shmget(KEY, MEM_MIN_SIZE, 0666|IPC_CREAT); //创建共享内存,key = 0(IPC_PRIVATE) 创建一个新对象。成功则返回id (一个与key相关的标识符)
if(shmid < 0)
{
printf("创建共享内存出错!\n");
exit(-1);
}
shmp = shmat(shmid, NULL, 0);//指定共享内存映射到新虚拟地址空间,返回起始地址
if((int)shmp == -1)
{
printf("映射内存出错!\n");
exit(-1);
}
}
Makefile:
all: sender1 sender2 receiver
sender1: sender1.c
gcc sender1.c -pthread -o sender1
sender2: sender2.c
gcc sender2.c -pthread -o sender2
receiver: receiver.c
gcc receiver.c -pthread -o receiver