实验六 进程间通信
作者|Ricky的水果摊
时间|2022年6月2日
实验目的
了解 Linux系统的通信机制,管道、消息队列、信号量的创建与使用、共享 内存。
实验原理
1. 进程通信概述
1.1 进程通信简介
进程间通信(Inter-Process Communication)是指多个进程之间相互通信,交换信息的方法
Linux环境下,进程的地址空间相互独立,任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程间不能互相访问,要交换数据必须通过内核,在内核中开辟一块缓存区,进程1把数据从用户空间拷贝到内存缓冲区,进程2再从内存缓冲区中把数据读走,内核提供的这种机制称为进程间通信(IPC)
1.2 进程通信目的
- 传输数据
- 共享数据/资源
- 通知事件
- 进程控制
1.3 进程通信方式
- 管道/FIFO
- 消息队列
- 共享内存
- 信号量
- 信号
- Socket
2. 进程通信的6方式
2.1 Pipe/FIFO
2.2 消息队列
2.3 共享内存
2.4 信号量
2.5 信号
实验作业
1. ftok.c实践
源代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main( void )
{
int i;
for ( i = 1; i <= 5; i++ )
printf( "key[%d] = %ul \n", i, ftok( ".", i) );
exit(0);
}
问题描述
程序的运行结果是什么?请简单分析
程序运行截图
结果分析
- 代码用一个for循环,生成了5个键值
2. sendmsg.c实践
源代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUF_SIZE 256
#define PROJ_ID 32
#define PATH_NAME "."
int main(void) {
//用户自定义消息缓冲
struct mymsgbuf {
long msgtype;
char ctrlstring[BUF_SIZE];
} msgbuffer;
int qid;
int msglen;
key_t msgkey;
//获取键值
if((msgkey = ftok (PATH_NAME, PROJ_ID)) == -1) {
perror ("ftok error!\n");
exit (1);
}
/*创建消息队列*/
if((qid = msgget (msgkey, IPC_CREAT|0660)) == -1) {
perror ("msgget error!\n");
exit (1);
}
/*填充消息结构,发送到消息队列*/
msgbuffer. msgtype = 3;
strcpy (msgbuffer.ctrlstring , "Hello,message queue");
msglen = sizeof(msgbuffer) - 4;
if(msgsnd (qid, &msgbuffer, msglen, 0) == -1) {
perror ("msgget error!\n");
exit (1);
}
exit(0);
}
问题描述
程序的运行结果是什么?请简单分析
程序运行截图
结果分析
- 从运行结果来看,本次操作成功把消息"Hello,message queue"写入了消息队列中,无报错提示
3. rcvmsg.c实践
源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUF_SIZE 256
#define PROJ_ID 32
#define PATH_NAME "."
int main(void) {
struct mymsgbuf{
long msgtype;
char ctrlstring[BUF_SIZE];
} msgbuffer;
int qid,msglen;
key_t msgkey;
if((msgkey = ftok(PATH_NAME, PROJ_ID)) == -1){
perror("ftok error!\n");
exit(1);
}
if((qid = msgget(msgkey, IPC_CREAT|0660)) == -1){
perror ("msgget error!\n");
exit (1);
}
msglen = sizeof(struct mymsgbuf) - 4;
if (msgrcv(qid, &msgbuffer, msglen, 3, 0) == -1){
perror ("msgrcv error!\n");
exit (1);
}
printf("Get message %s\n", msgbuffer.ctrlstring);
exit(0);
}
问题描述
编译运行,分析程序运行结果
程序运行截图
结果分析
- 从运行结果来看,本次操作成功读取了sendmsg.c中写入消息队列中的消息,并且将其输出在屏幕上
4. server.c / client.c实践
server.c源代码
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <linux/sem.h>
#include <unistd.h>
#define MAX_RESOURCE 5
int main(void) {
key_t key;
int semid;
struct sembuf sbuf = {0, -1, IPC_NOWAIT};
union semun semopts;
if ((key = ftok(".", 's')) == -1) {
perror ("ftok error!\n");
exit (1);
}
if ((semid = semget (key,1,IPC_CREAT|0666)) == -1) {
perror ("semget error!\n");
exit (1);
}
semopts.val = MAX_RESOURCE;
if (semctl (semid,0,SETVAL, semopts) == -1) {
perror ("semctl error!\n");
exit (1);
}
while (1){
if(semop(semid, &sbuf, 1) == -1) {
perror ("semop error!\n");
exit (1);
}
sleep (3);
}
exit (0);
}
client.c源代码
#include <stdio.h>
#include <sys/types.h>
#include <linux/sem.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
key_t key;
int semid, semval;
union semun semopts;
if((key = ftok (".",'s')) == -1) {
perror ("ftok error!\n");
exit (1);
}
if((semid = semget (key, 1, IPC_CREAT | 0666)) == -1) {
perror ("semget error!\n");
exit (1);
}
while(1) {
if ((semval = semctl(semid, 0, GETVAL, 0)) == -1) {
perror ("semctl error!\n");
exit (1);
}
if (semval > 0) {
printf ("Still %d resources can be used\n", semval);
}else{
printf ("No more resources can be used!\n");
break;
}
sleep (3);
}
exit (0);
}
问题描述
首先在终端上编译并运行 server.c再在另外一个终端上编译运行client.c,观察client的运行结果,分析程序的执行过程
程序运行截图
client窗口
server窗口
结果分析
- 在client窗口,不断输出剩余的可用资源数量
- 在server窗口,不断对可用资源进行信号量操作,即每3秒减少一个资源
5. wrshm.c / rdshm.c实践
wrshm.c源代码
#include "sharemem.h"
#define SHM_SIZE 1024
int main() {
int semid, shmid;
char *shmaddr;
char write_str[SHM_SIZE];
if ((shmid = createshm (".", 'm', SHM_SIZE)) == -1) {
exit(1);
}
if ((shmaddr = shmat (shmid, (char *)0, 0)) ==(char *)-1) {
perror ("attach shared memory error!\n");
exit (1);
}
if ((semid = createsem (".", 's', 1, 1)) == -1) {
exit (1);
}
while (1) {
wait_sem (semid, 0);
sem_p (semid, 0); /*P操作*/
printf ("writer: ");
fgets (write_str, 1024, stdin);
int len = strlen (write_str) - 1;
write_str[len] = '\0';
strcpy (shmaddr, write_str);
sleep (10); /*使reader处于阻塞状态*/
sem_v (semid, 0); /*V操作*/
sleep (10); /*等待reader进行读操作*/
}
}
rdshm.c源代码
#include "sharemem.h"
int main() {
int semid, shmid;
char *shmaddr;
if ((shmid = createshm(".", 'm', SHM_SIZE)) == -1) {
exit (1);
}
if((shmaddr = shmat (shmid, (char *)0, 0)) == (char *)-1) {
perror ("attach shared memory error!\n");
exit (1);
}
if((semid = opensem("." ,'s')) == -1) {
exit (1);
}
while(1) {
printf("reader: ");
wait_sem(semid,0); /* 等待信号值为1 */
sem_p(semid,0); /* P操作 */
printf("%s\n", shmaddr);
sleep(10); /* 使writer处于阻塞状态 */
sem_v(semid,0); /* V操作 */
sleep(10); /* 等待writer进行写操作 */
}
}
sharemen.h源代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#define SHM_SIZE 1024
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
/*创建信号量函数*/
int createsem (const char * pathname, int proj_id, int members, int init_val) {
key_t msgkey;
int index, sid;
union semun semopts;
if ((msgkey = ftok(pathname, proj_id)) == -1) {
perror ("ftok error!\n");
return -1;
}
if ((sid = semget (msgkey, members, IPC_CREAT | 0666)) == -1) {
perror ("semget call failed.\n");
return -1;
}
/*初始化操作*/
semopts.val = init_val;
for (index = 0; index < members; index++) {
semctl (sid, index, SETVAL, semopts);
}
return (sid);
}
/*打开信号量函数*/
int opensem(const char * pathname, int proj_id) {
key_t msgkey;
int sid;
if ((msgkey = ftok(pathname, proj_id)) == -1) {
perror ("ftok error!\n");
return -1;
}
if ((sid = semget(msgkey, 0, IPC_CREAT | 0666)) == -1) {
perror("semget call failed.\n");
return -1;
}
return (sid);
}
/*P操作函数*/
int sem_p(int semid, int index) {
struct sembuf buf = {0,-1,IPC_NOWAIT};
if (index < 0){
perror("index of array cannot equals a minus value!");
return -1;
}
buf.sem_num = index;
if (semop (semid ,& buf,1) == -1) {
perror ("a wrong operation to semaphore occurred!");
return -1;
}
return 0;
}
/*v操作函数*/
int sem_v (int semid, int index) {
struct sembuf buf = {0, +1, IPC_NOWAIT};
if (index < 0) {
perror("index of array cannot equals a minus value!");
return -1;
}
buf.sem_num = index;
if (semop (semid,& buf,1) == -1) {
perror ("a wrong operation to semaphore occurred!");
return -1;
}
return 0;
}
/*删除信号集函数*/
int sem_delete (int semid) {
return (semctl(semid, 0, IPC_RMID));
}
/*等待信号为1*/
int wait_sem( int semid, int index) {
while (semctl (semid, index, GETVAL, 0) == 0) {
sleep (1);
}
return 1 ;
}
/*创建共享内存函数*/
int createshm( char * pathname, int proj_id, size_t size) {
key_t shmkey;
int sid;
/*获取键值*/
if ((shmkey = ftok(pathname, proj_id)) == -1){
perror("ftok error!\n");
return -1;
}
if ((sid = shmget(shmkey, size, IPC_CREAT | 0666)) == -1) {
perror ("shmget call failed.\n");
return -1;
}
return (sid);
}
问题描述
编译两个程序,同时在两个终端运行writer和reader,在writer端输入字符串 “hello,reader”,等待一会,看到reader端输出后,在writer.c端的提示符后面输入 字符串“new information”
运行的结果是什么?请分析程序的执行过程
程序运行截图
writer窗口:
reader窗口:
结果分析
- 从运行结果来看,本次操作成功利用共享内存与信号量,完成了writer与reader进程间的通信