在这次课堂的学习中,我们主要学习了进程间通信的相关知识,首先是对命名管道的了解,再是消息队列以及信号量和共享内存。
在命名管道的讲解中,主要使用FIFO实现进程间通信,需要read和write两段程序来实现。首先进行write操作,之后可以用read来读取所写的内容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int paraArgc,char *paraArgv[]){
if(paraArgc < 2){ //判断是否传入文件名
printf("./a.out fifoname\n");
exit(1);
}//of if
int tempRet = access(paraArgv[1], F_OK); //判断fifo文件是否存在
if(tempRet == -1){ //若fifo不存在就创建fifo
int tempFIFO = mkfifo(paraArgv[1], 0664);
if(tempFIFO == -1){ //判断文件是否创建成功
perror("mkfifo");
exit(1);
} else{
printf("fifo creat success!\n");
}//of if
}//of if
int tempFd = open(paraArgv[1], O_RDONLY); //只读方式打开
if(tempFd == -1){
perror("open");
exit(1);
}//of if
while(1){ //不断读取fifo中的数据并打印
char temBuf[1024]={0};
read(tempFd, temBuf, sizeof(temBuf));
printf("buf=%s\n", temBuf);
}//of while
close(tempFd); //关闭文件
return 0;
}//of main
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int paraArgc,char *paraArgv[]){
if(paraArgc < 2){ //判断是否传入文件名
printf("./a.out fifoname\n");
exit(1);
}//of if
int tempRet = access(paraArgv[1], F_OK); //判断fifo文件是否存在
if(tempRet == -1){ //若fifo不存在就创建fifo
int tempFIFO = mkfifo(paraArgv[1], 0664);
if(tempFIFO == -1){ //判断文件是否创建成功
perror("mkfifo");
exit(1);
} else{
printf("fifo creat success!\n");
}//of if
}//of if
int tempFd = open(paraArgv[1], O_WRONLY); //读写方式打开
while(1){ //循环写入数据
char *tempStrp="hello,world!";
write(tempFd, tempStrp, strlen(tempStrp)+1);
sleep(1);
}//of while
close(tempFd);
return 0;
}//of main
在消息队列的讲解中,解释了对不同消息的处理以及存在的问题和用途。
消息队列的本质是一个存放消息的链表,该链表由内核来维护。一个消息队列由一个标识符(即队列key)来标识。
消息队列的通信机制传递的数据具有某种结构,而不是简单的字节流;
向消息队列中写数据,实际上是向这个数据结构中插入一个新结点;
从消息队列中读数据,实际上是从这个数据结构中删除一个结点;
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法;
消息队列具有和管道一样的不足,每个数据块的最大长度是有上限的,系统上全体队列的最大总长度也是有上限的。
msgsnd函数向指定消息队列发送一个消息;如果msgflg = 0,调用函数的进程会被挂起,直到消息写入消息队列为止。主要用于判断消息队列的标识符。
msgrcv函数从消息队列中读取消息,被读取的消息会从消息列表中移除。
msgrcv.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st{
long int my_msg_type;
char anytext[MAX_TEXT];
};
int main(){
int tempIdx = 1;
int tempMsgid;
struct my_msg_st tempData;
long int tempMsgToRcv = 0;
//rcv msg
tempMsgid = msgget((key_t)1000, 0664 | IPC_CREAT);//获取消息队列
if (tempMsgid == -1){
perror("msgget err");
exit(-1);
}//of if
while (tempIdx < 5){
//接收消息
if (msgrcv(tempMsgid, (void*)&tempData, BUFSIZ, tempMsgToRcv, 0) == -1){
perror("msgrcv err");
exit(-1);
}//of if
//打印消息
printf("msg type:%ld\n", tempData.my_msg_type);
printf("msg content is:%s", tempData.anytext);
tempIdx ++;
}//of while
//删除消息队列
if (msgctl(tempMsgid, IPC_RMID, 0) == -1){
perror("msgctl err");
exit(-1);
}//of if
exit(0);
}//of main
msgsend.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>
#define MAX_TEXT 512
//消息结构体
struct my_msg_st{
long int my_msg_type; //消息类型
char anytext[MAX_TEXT]; //消息数据
};
int main() {
int tempIdx = 1;
int tempMsgid;
struct my_msg_st tempData;
char tempBuf[BUFSIZ]; //设置缓存变量
tempMsgid = msgget((key_t)1000, 0664 | IPC_CREAT);//创建消息队列
if (tempMsgid == -1){
perror("msgget err");
exit(-1);
}//of if
while (tempIdx < 5){ //发送消息
printf("enter some text:");
fgets(tempBuf, BUFSIZ, stdin);
tempData.my_msg_type = rand() % 3 + 1; //随机获取消息类型
strcpy(tempData.anytext, tempBuf);
//发送消息
if (msgsnd(tempMsgid, (void*)&tempData, sizeof(tempData), 0) == -1){
perror("msgsnd err");
exit(-1);
}//of if
tempIdx ++;
}//of while
return 0;
}//of main
在案例中,msgrcv主要进行消息的输入,msgsend进行消息类型的输出。
oseasy@PC7:~$ ./msgrcv
enter some text:/home
enter some text:/oseasy
enter some text:123
enter some text:text
oseasy@PC7:~$ ./msgsend
msg type:2
msg content is :/home
msg type:2
msg content is :/oseasy
msg type:1
msg content is :123
msg type:2
msg content is :text
在共享内存章节进行的主要内容是同时对内存进行访问,这种方法的引入节约了大量的使劲,提升了工作的效率,但是使用者要遵循一定的守则,即读写操作不能同时进行。
shmget函数用于创建一个新的共享内存或打开一块已存在的共享内存。
shmat函数用于进行地址映射,将共享内存映射到进程虚拟地址空间中。
shm_w.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define SEGSIZE 4096 //定义共享内存容量
typedef struct{ //读写数据结构体
char name[8];
int age;
} Stu;
int main() {
int tempShmId, i;
key_t tempKey;
char tempName[8];
Stu *tempSmap;
/*ftok函数的作用:系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。*/
tempKey = ftok("/", 0); //获取关键字
if (tempKey == -1) {
perror("ftok error");
return -1;
}//of if
printf("key=%d\n", tempKey);
//创建共享内存
tempShmId= shmget(tempKey, SEGSIZE, IPC_CREAT | IPC_EXCL | 0664);
if (tempShmId == -1) {
perror("create shared memory error\n");
return -1;
}//of if
printf("shm_id=%d\n", tempShmId);
tempSmap = (Stu*)shmat(tempShmId, NULL, 0); //将进程与共享内存绑定
memset(tempName, 0x00, sizeof(tempName));
strcpy(tempName, "Jhon");
tempName[4] = '0';
for (i = 0; i < 3; i++){ //写数据
tempName[4] += 1;
strncpy((tempSmap+ i)->name, tempName, 5);
(tempSmap + i)->age = 20 + i;
}//of for i
if (shmdt(tempSmap) == -1){ //解除绑定
perror("detach error");
return -1;
}//of if
return 0;
}//of main
shm_r.c
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
char name[8];
int age;
} Stu;
int main() {
int tempShmId, i;
key_t tempKey;
Stu *tempSmap;
struct shmid_ds tempBuf;
tempKey = ftok("/", 0); //获取关键字
if (tempKey == -1) {
perror("ftok error");
return -1;
}//of if
printf("key=%d\n", tempKey);
tempShmId = shmget(tempKey, 0, 0); //创建共享内存
if (tempShmId == -1) {
perror("shmget error");
return -1;
}//of if
printf("shm_id=%d\n", tempShmId);
tempSmap = (Stu*)shmat(tempShmId, NULL, 0); //将进程与共享内存绑定
for (i = 0; i < 3; i++){ //读数据
printf("name:%s\n", (*(tempSmap + i)).name);
printf("age :%d\n", (*(tempSmap + i)).age);
}//of for i
if (shmdt(tempSmap) == -1){ //解除绑定
perror("detach error");
return -1;
}//of if
shmctl(tempShmId, IPC_RMID, &tempBuf); //删除共享内存
return 0;
}//of main
在案例中,同样是先进行write操作,再由read操作执行。
oseasy@PC7:~$ ./shm_w
key=65538
shm_id=327712
oseasy@PC7:~$
oseasy@PC7:~$ ./shm_r
key=65538
shm_id=327712
name:Jhon1
age :20
name:Jhon2
age :21
name:Jhon3
age :22