700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux 进程通信 -上 (有无名管道 消息队列 mmap 共享内存)

Linux 进程通信 -上 (有无名管道 消息队列 mmap 共享内存)

时间:2021-02-13 21:06:33

相关推荐

Linux 进程通信 -上 (有无名管道 消息队列 mmap 共享内存)

进程间的通信

进程间的通信方式(重要!!)1、无名管道(重要)无名管道 用于进程的通信案例:无名管道的特点:调用write函数向管道里写数据,当缓冲区已满时write也会阻塞设置为非阻塞: fcntl(fd, FSETFL, O_NONBLOCK); 1.1、文件描述符复制 dup案例1:我让标准输出文件描述符1 指向磁盘文件案例2:实现 ps -aux | grep bash(理解) 1.2、文件描述符复制 dup2 2、有名管道FIFO(没有血缘关系的进程间通信)有名管道的特点案例 案例:单机聊天总结:无名、命名管道 都是一对一的通信 3、消息队列(多对多通信)消息队列的特点:在ubuntu 某些版本中消息队列限制值如下:消息队列的创建:1、获得唯一的key值2、根据唯一的key创建消息队列:使用shell命令操作消息队列: (ipcs -q)删除消息队列(ipcrm -q msqid)消息队列的消息的格式(重要!!!) 3、发送消息4、消息队列的接收消息队列的控制: 案例:消息队列实现多人聊天4、mmap存储映射mmap使用流程:1、打开一个open磁盘文件 获得文件描述符2、truncate拓展文件大小3、mmap 函数根据 文件描述符 映射内存空间4、操作 内存空间 完成通信5、munmap 释放映射空间 案例:写案例:读操作 5、内存共享共享内存的特点:共享内存的步骤:1、获得唯一的key值2、通过key得到获得一个共享存储标识符id使用shell命令操作共享内存: 查看共享内存 ipcs -m , 删除共享内存 ipcrm -m shmid 3、将物理内存 映射到虚拟内存:4、操作空间内容5、断开映射6、物理内存控制(不必须):案例:写案例:读

进程间的通信方式(重要!!)

1、无名管道(重要)

管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

函数说明:

#include <unistd.h>int pipe(int filedes[2]);功能:经由参数filedes返回两个文件描述符参数:filedes为int型数组的首地址,其存放了管道的文件描述符filedes[0]、filedes[1]。filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入。返回值:成功:返回 0失败:返回-1

案例:

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>int main(){//创建一个无名管道int fd[2];int ret = pipe(fd);if(ret == -1){perror("pipe");exit(0);}printf("管道的读端fd[0]=%d\n", fd[0]);printf("管道的写端fd[1]=%d\n", fd[1]);//关闭文件描述符close(fd[0]);close(fd[1]);return 0;}

运行结果:

无名管道 用于进程的通信

案例:

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>int main(){//父发消息 子接受消息//创建管道int fd[2];pipe(fd);//创建进程pid_t pid = fork();if(pid == 0)//子进程{//fd[0]读 fd[1]写//由于子进程只用到了fd[0] 可以先关写端fd[1]close(fd[1]);//先关闭无用的//子进程接受消息 循环接受 whilechar buf[128]="";read(fd[0],buf,sizeof(buf));//默认阻塞printf("子进程%d接收到的消息为:%s\n",getpid(),buf);//子进程对fd[0]使用完毕 应该关闭fd[0]close(fd[0]);//最后关闭 使用exit(0);//显示的退出}else if(pid > 0)//父进程{//fd[0]读 fd[1]写//由于父进程只用到了fd[1] 可以先关读端fd[0]close(fd[0]);//父进程发送消息printf("父进程%d:5s后将发送消息\n",getpid());sleep(5);printf("父进程%d:已发送消息\n",getpid());write(fd[1],"hello msg",strlen("hello msg"));wait(NULL);//父进程对fd[1]使用完毕 应该关闭fd[1]close(fd[1]);//最后关闭 使用}return 0;}

运行结果:

无名管道的特点:

1、默认用read函数从管道中读数据是阻塞的。

2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞。

3、通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号)退出。

4、写端意外关闭 读端的read将解阻塞

编程时可通过fcntl函数设置文件的阻塞特性。

设置为阻塞: fcntl(fd, FSETFL, 0);

设置为非阻塞: fcntl(fd, FSETFL, O_NONBLOCK);

调用write函数向管道里写数据,当缓冲区已满时write也会阻塞

通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号)退出。

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>int main(){//父发消息 子接受消息//创建管道int fd[2];pipe(fd);//创建进程pid_t pid = fork();if(pid == 0)//子进程{close(fd[1]);int i=0;for(i=0;i<4;i++){char buf[128]="";read(fd[0], buf,sizeof(buf));printf("子进程读到的消息%s\n", buf);}printf("子进程将关闭读端\n");close(fd[0]);}else if(pid > 0)//父进程{close(fd[0]);int i=0;for(i=0;i<10;i++){write(fd[1],"hehehehe", 4);printf("i=%d\n",i);sleep(1);}}return 0;}

运行结果:

设置为非阻塞: fcntl(fd, FSETFL, O_NONBLOCK);

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){//父发消息 子接受消息//创建管道int fd[2];pipe(fd);//创建进程pid_t pid = fork();if(pid == 0)//子进程{close(fd[1]);//设置读端为非阻塞fcntl(fd[0], F_SETFL, O_NONBLOCK);int i=0;for(i=0;i<10;i++){char buf[128]="";read(fd[0], buf,sizeof(buf));printf("%d秒子进程读到的消息%s\n",i, buf);sleep(1);}}else if(pid > 0)//父进程{printf("父进程将在5秒后发送信息\n");sleep(5);write(fd[1],"hehe", 4);printf("父进程已发送信息\n");}return 0;}

运行结果:

1.1、文件描述符复制 dup

int dup(int fd);int dup2(int fd1,int fd2);两个均为复制一个现存的文件的描述两个函数的返回:若成功为新的文件描述,若出错为-1;由dup返回的新文件描述符一定是当前可用文件描述中的最小数值。用dup2则可以用fd2参数指定新的描述符数值。如果fd2已经打开,则先关闭。若fd1=fd2,则dup2返回fd2,而不关闭它。通常使用这两个系统调用来重定向一个打开的文件描述符。

#include <unistd.h>int dup(int oldfd);复制oldfd文件描述符 复制的结果==是系统最小可用的文件描述符

案例:

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){int old_fd = open("a.txt",O_WRONLY|O_CREAT,0777);printf("old_fd = %d\n", old_fd);//用new_fd复制old_fdint new_fd = dup(old_fd);printf("new_fd = %d\n", new_fd);write(new_fd,"hello dup",strlen("hello dup"));close(new_fd);close(old_fd);return 0;}

运行结果:

案例1:我让标准输出文件描述符1 指向磁盘文件

用printf 往文件中输入数据

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){int old_fd = open("b.txt", O_WRONLY|O_CREAT,0777);printf("old_fd = %d\n", old_fd);//关闭1 让1是最小可用的文件描述符close(1);//复制old_fd 让最小可用的1 复制 old_fdint ret = dup(old_fd);printf("ret = %d\n",ret);printf("hello udp\n");close(old_fd);close(ret);return 0;}

运行结果:

案例2:实现 ps -aux | grep bash(理解)

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){//创建一个管道int fd[2];pipe(fd);pid_t pid = fork();if(pid == 0)//子进程{//关闭0 让0最小可用close(0);//0重定向fd[0]dup(fd[0]);//execlp执行grepexeclp("grep", "grep","bash",NULL);exit(0);}else if(pid >0)//父进程 写{//关闭1 让1最小可用close(1);//1重定向到fd[1]dup(fd[1]);//execlp执行psexeclp("ps","ps","-aux",NULL);wait(NULL);}}

运行结果:

1.2、文件描述符复制 dup2

#include <unistd.h>int dup2(int oldfd, int newfd);//如果newfd是打开的 dup2会先自动关闭newfd 然后再复制oldfd

dup步骤:close(new_fd) dup(oldfd) 例如:close(1)---->dup(oldfd)

dup2步骤:dup(oldfd,newfd) 例如:dup2(oldfd,1);

#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){int oldfd = open("c.txt", O_WRONLY|O_CREAT,0666);//让1 指向 oldfd//close(1)---->dup(oldfd)dup2(oldfd,1);printf("hello dup2\n");fflush(stdout);//刷新标准输出缓冲区close(oldfd);close(1);return 0;}

运行结果:

2、有名管道FIFO(没有血缘关系的进程间通信)

FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。

创建命令管道:只是将文件名 和 内存关系起来

#include <sys/types.h>#include <sys/stat.h>int mkfifo( const char *pathname, mode_t mode);参数:pathname:FIFO的路径名+文件名。mode:mode_t类型的权限描述符。返回值:成功:返回 0失败:如果文件已经存在,则会出错且返回-1

有名管道的特点

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同,其特点是:

1、半 双工,数据在同一时刻只能在一个方向上流动。

2、写入FIFO中的数据遵循先入 先出的规则。

3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方 必须事先约定好数据的格式,如多少字节算一个消息等。

4、FIFO在文件系统中 作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。

5、管道在内存 中对应一个缓冲区。不同的系统其大小不一定相同。

6、从FIFO读数据是一次 性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。

7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使 用。

8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。

案例

写端:

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){//创建一个命令管道mkfifo("my_fifo", 0666);//open打开my_fifo 写//(阻塞对方以写的方式打开)int fd = open("my_fifo", O_WRONLY);printf("写端打开了\n");printf("请输入尧发送的数据\n");char buf[128]="";fgets(buf,sizeof(buf),stdin);//获取键盘输入write(fd,buf,strlen(buf));printf("写端已发送数据\n");close(fd);return 0;}

读端:

#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){//创建一个命令管道(如果存在)mkfifo("my_fifo", 0666);//open打开my_fifo 读//阻塞到 对方以读的方式 打开int fd = open("my_fifo", O_RDONLY);printf("读端打开了\n");char buf[128]="";printf("读端准备读取数据中....\n");read(fd, buf,sizeof(buf));printf("读到的数据为:%s\n",buf);close(fd);return 0;}

运行结果:

案例:单机聊天

lucy.c

#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){int i = 0;for(i = 0; i<2; i++){pid_t pid = fork();if(pid == 0)break;}if(i == 0)//子进程 1 发{//lucy_to_petermkfifo("lucy_to_peter",0666);//创建有名管道int fd = open("lucy_to_peter",O_WRONLY); //打开管道while (1){printf("LUCY:");fflush(stdout);//刷新输出缓冲区char msg[128] = "";fgets(msg,sizeof(msg),stdin); //获取键盘输入msg[strlen(msg)-1]=0;//去掉\nwrite(fd,msg,strlen(msg));if(strcmp("bye",msg)==0)break;}close(fd);}else if(i==1)//子进程2{mkfifo("peter_to_lucy",0666);int fd = open("peter_to_lucy",O_RDONLY);while(1){char msg[128]="";read(fd,msg,sizeof(msg));printf("\nPETER发送的消息为:%s\n",msg);if(strcmp("bye",msg)==0)break;}}else if(i==2)//父进程{//等待子进程的退出while(1){pid_t pid = waitpid(-1,NULL, WNOHANG);if(pid > 0)//等到一个退出{printf("子进程%d退出了\n",pid);}else if(pid ==0)//还有子进程在运行{continue;}else if(pid == -1)//所有子进程都退出了{break;}}}return;}

peter.c

#include<stdio.h>#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>int main(){int i=0;for(i=0;i<2; i++){pid_t pid = fork();if(pid == 0)break;}if(i==0)//子进程1 发{//peter_to_lucymkfifo("peter_to_lucy",0666);int fd = open("peter_to_lucy", O_WRONLY);while(1){printf("PETER:");fflush(stdout);char msg[128]="";fgets(msg,sizeof(msg),stdin);msg[strlen(msg)-1]=0;write(fd,msg,strlen(msg));if(strcmp("bye",msg)==0)break;}close(fd);}else if(i==1)//子进程2 收{mkfifo("lucy_to_peter",0666);int fd = open("lucy_to_peter",O_RDONLY);while(1){char msg[128]="";read(fd,msg,sizeof(msg));printf("\nLUCY发送的消息为:%s\n",msg);if(strcmp("bye",msg)==0)break;}}else if(i==2)//父进程{//等待子进程的退出while(1){pid_t pid = waitpid(-1,NULL, WNOHANG);if(pid > 0)//等到一个退出{printf("子进程%d退出了\n",pid);}else if(pid ==0)//还有子进程在运行{continue;}else if(pid == -1)//所有子进程都退出了{break;}}}return;}

运行结果:

总结:无名、命名管道 都是一对一的通信

3、消息队列(多对多通信)

消息队列的特点:

消息队列是消息的链表,存放在内存中,由内核维护 消息队列的特点

1、消息队列中的消息是有类型的。

2、消息队列中的消息是有格式的。

3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。

4、消息队列允许一个或多个进程向它写入或者读取消息。

5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。

6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。

7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中

在ubuntu 某些版本中消息队列限制值如下:

每个消息内容最多为8K字节每个消息队列容量最多为16K字节系统中消息队列个数最多为1609个系统中消息个数最多为16384个

消息队列的创建:

1、获得唯一的key值

#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);功能:获得项目相关的唯一的IPC键值。参数:pathname:路径名proj_id:项目ID,非0整数(只有低8位有效)返回值:成功返回key值,失败返回 -1

2、根据唯一的key创建消息队列:

#include <sys/msg.h>int msgget(key_t key, int msgflg);功能:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的标识符。参数:key:IPC键值。msgflg:标识函数的行为及消息队列的权限。参数:msgflg的取值:IPC_CREAT:创建消息队列。IPC_EXCL:检测消息队列是否存在。位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和open函数的mode_t一样,但可执行权限未使用。返回值:成功:消息队列的标识符,失败:返回-1

//通ftok得到唯一key值key_t key = ftok("/", );printf("key = %#x\n", key);//通过key创建消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);

使用shell命令操作消息队列: (ipcs -q)
删除消息队列(ipcrm -q msqid)
消息队列的消息的格式(重要!!!)

typedef struct _msg{long mtype; /*消息类型 固定的*/ char mtext[100]; /*消息正文*/... /*消息的正文可以有多个成员*/}MSG;

消息类型必须是长整型的,而且必须是结构体类 型的第一个成员,类型下面是消息正文,正文可以 有多个成员(正文成员可以是任意数据类型的)。

3、发送消息

#include <sys/msg.h>int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);功能:将新消息添加到消息队列。参数:msqid:消息队列的标识符。msgp:待发送消息结构体的地址。msgsz:消息正文的字节数。msgflg:函数的控制属性0:msgsnd调用阻塞直到条件满足为止。IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。返回值:成功:0;失败:返回-1。

#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<string.h>//消息的结构体typedef struct{//消息的类型long mtype;//消息的正文char mtext[128];}MSG;int main(){//通ftok得到唯一key值key_t key = ftok("/", );printf("key = %#x\n", key);//通过key创建消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);//发送消息MSG msg;memset(&msg,0,sizeof(msg));msg.mtype = 10;//赋值类型strcpy(msg.mtext,"hello msg");//赋值正文内容//注意:正文的大小msgsnd(msg_id, &msg,sizeof(MSG)-sizeof(long), 0);return 0;}

4、消息队列的接收

#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);功能:从标识符为msqid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。参数:msqid:消息队列的标识符,代表要从哪个消息列中获取消息。msgp: 存放消息结构体的地址。msgsz:消息正文的字节数。msgtyp:消息的类型、可以有以下几种类型msgtyp = 0:返回队列中的第一个消息msgtyp > 0:返回队列中消息类型为msgtyp的消息msgtyp < 0:返回队列中消息类型值小于或等于msgtyp绝对值的消息,如果这种消息有若干个,则取类型值 最小的消息。注意:若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的。在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原msgflg:函数的控制属性0:msgrcv调用阻塞直到接收消息成功为止。MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。返回值:成功返回读取消息的长度,失败返回-1

#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<string.h>//消息的结构体typedef struct{//消息的类型long mtype;//消息的正文char mtext[128];}MSG;int main(){//通ftok得到唯一key值key_t key = ftok("/", );printf("key = %#x\n", key);//通过key创建消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);//接收消息消息MSG msg;memset(&msg,0,sizeof(msg));printf("等待消息队列的消息\n");int len = msgrcv(msg_id, &msg, sizeof(msg)-sizeof(long), 10, 0);printf("收到类型为%d的消息为:%s 长度为:%d\n", 10,msg.mtext,len );return 0;}

运行结果:

消息一旦从消息队列中 读走 消息将从消息队列中删除

消息队列的控制:

#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);功能:对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。参数:msqid:消息队列的标识符。cmd:函数功能的控制。buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性。cmd:函数功能的控制IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构。IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构中。IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。返回值:成功:返回 0;失败:返回 -1

//删除消息队列msgctl(msg_id, IPC_RMID, NULL);

案例:消息队列实现多人聊天

消息结构体类型typedef struct msg{long type; //接收者类型char text[100]; //发送内容char name[20]; //发送者姓名}MSG;

lucy.c

#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<string.h>#include<unistd.h>#include<stdlib.h>#include<fcntl.h>//lucy类型10 bob类型11 tom类型12typedef struct msg{long type; //接收者类型char text[100]; //发送内容char name[20]; //发送者姓名}MSG;char *p_name[]={"lucy","bob","tom"};int n = sizeof(p_name)/sizeof(p_name[0]);int p_type[]={10,11,12};int main(){//获得唯一的keykey_t key = ftok("/", );//创建一个消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);int i=0;for(i=0;i<2; i++){pid_t pid = fork();if(pid == 0)break;}if(i==0)//子进程1 发{while(1){char name[32]="";printf("请输入发送的姓名:");scanf("%s",name);getchar();int i=0;for(i=0;i<n; i++){if(strcmp(name, p_name[i]) == 0 )break;}if(i == n)//名字不匹配{printf("输入的名字有误!!!\n");continue;}else{printf("请输入要发送的消息:");char buf[128]="";fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;MSG msg;memset(&msg,0,sizeof(msg));msg.type = p_type[i];//类型strcpy(msg.text, buf);//发送的内容strcpy(msg.name, "lucy");//发送的内容//注意:正文的大小msgsnd(msg_id, &msg,sizeof(MSG)-sizeof(long), 0);}}exit(0);}else if(i==1)//子进程2 收{while(1){MSG msg;memset(&msg, 0,sizeof(msg));msgrcv(msg_id,&msg, sizeof(msg)-sizeof(long), 10, 0);printf("\r来自%s的消息:%s\n", msg.name, msg.text);printf("请输入发送的姓名:");fflush(stdout);}}else if(i==2)//父进程{//等待子进程的退出while(1){pid_t pid = waitpid(-1,NULL, WNOHANG);if(pid > 0)//等到一个退出{printf("子进程%d退出了\n",pid);}else if(pid ==0)//还有子进程在运行{continue;}else if(pid == -1)//所有子进程都退出了{break;}}}}

bob.c

#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<string.h>#include<unistd.h>#include<stdlib.h>#include<fcntl.h>//lucy类型10 bob类型11 tom类型12typedef struct msg{long type; //接收者类型char text[100]; //发送内容char name[20]; //发送者姓名}MSG;char *p_name[]={"lucy","bob","tom"};int n = sizeof(p_name)/sizeof(p_name[0]);int p_type[]={10,11,12};int main(){//获得唯一的keykey_t key = ftok("/", );//创建一个消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);int i=0;for(i=0;i<2; i++){pid_t pid = fork();if(pid == 0)break;}if(i==0)//子进程1 发{while(1){char name[32]="";printf("请输入发送的姓名:");scanf("%s",name);getchar();int i=0;for(i=0;i<n; i++){if(strcmp(name, p_name[i]) == 0 )break;}if(i == n)//名字不匹配{printf("输入的名字有误!!!\n");continue;}else{printf("请输入要发送的消息:");char buf[128]="";fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;MSG msg;memset(&msg,0,sizeof(msg));msg.type = p_type[i];//类型strcpy(msg.text, buf);//发送的内容strcpy(msg.name, "bob");//发送的内容//注意:正文的大小msgsnd(msg_id, &msg,sizeof(MSG)-sizeof(long), 0);}}exit(0);}else if(i==1)//子进程2 收{while(1){MSG msg;memset(&msg, 0,sizeof(msg));msgrcv(msg_id,&msg, sizeof(msg)-sizeof(long), 11, 0);printf("\r来自%s的消息:%s\n", msg.name, msg.text);printf("请输入发送的姓名:");fflush(stdout);}}else if(i==2)//父进程{//等待子进程的退出while(1){pid_t pid = waitpid(-1,NULL, WNOHANG);if(pid > 0)//等到一个退出{printf("子进程%d退出了\n",pid);}else if(pid ==0)//还有子进程在运行{continue;}else if(pid == -1)//所有子进程都退出了{break;}}}}

tom.c

#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<string.h>#include<unistd.h>#include<stdlib.h>#include<fcntl.h>//lucy类型10 bob类型11 tom类型12typedef struct msg{long type; //接收者类型char text[100]; //发送内容char name[20]; //发送者姓名}MSG;char *p_name[]={"lucy","bob","tom"};int n = sizeof(p_name)/sizeof(p_name[0]);int p_type[]={10,11,12};int main(){//获得唯一的keykey_t key = ftok("/", );//创建一个消息队列int msg_id = msgget(key, IPC_CREAT|0666);printf("msg_id = %d\n", msg_id);int i=0;for(i=0;i<2; i++){pid_t pid = fork();if(pid == 0)break;}if(i==0)//子进程1 发{while(1){char name[32]="";printf("请输入发送的姓名:");scanf("%s",name);getchar();int i=0;for(i=0;i<n; i++){if(strcmp(name, p_name[i]) == 0 )break;}if(i == n)//名字不匹配{printf("输入的名字有误!!!\n");continue;}else{printf("请输入要发送的消息:");char buf[128]="";fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;MSG msg;memset(&msg,0,sizeof(msg));msg.type = p_type[i];//类型strcpy(msg.text, buf);//发送的内容strcpy(msg.name, "tom");//发送的内容//注意:正文的大小msgsnd(msg_id, &msg,sizeof(MSG)-sizeof(long), 0);}}exit(0);}else if(i==1)//子进程2 收{while(1){MSG msg;memset(&msg, 0,sizeof(msg));msgrcv(msg_id,&msg, sizeof(msg)-sizeof(long), 12, 0);printf("\r来自%s的消息:%s\n", msg.name, msg.text);printf("请输入发送的姓名:");fflush(stdout);}}else if(i==2)//父进程{//等待子进程的退出while(1){pid_t pid = waitpid(-1,NULL, WNOHANG);if(pid > 0)//等到一个退出{printf("子进程%d退出了\n",pid);}else if(pid ==0)//还有子进程在运行{continue;}else if(pid == -1)//所有子进程都退出了{break;}}}}

运行结果:

4、mmap存储映射

mmap映射:

mmap使用流程:

1、打开一个open磁盘文件 获得文件描述符

2、truncate拓展文件大小

int truncate(const char *path, off_t length);path 要拓展的文件length 要拓展的长度

3、mmap 函数根据 文件描述符 映射内存空间

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);addr 地址,填NULLlength 长度 要申请的映射区的长度prot 权限PROT_READ 可读PROT_WRITE 可写flags 标志位MAP_SHARED 共享的 -- 对映射区的修改会影响源文件MAP_PRIVATE 私有的fd文件描述符 需要打开一个文件offset 指定一个偏移位置 ,从该位置开始映射返回值成功 返回映射区的首地址失败 返回 MAP_FAILED ((void *) -1)

4、操作 内存空间 完成通信

5、munmap 释放映射空间

释放映射区:int munmap(void *addr, size_t length);addr 映射区的首地址length 映射区的长度返回值成功 返回0失败 返回 -1

案例:写

#include<stdio.h>#include<string.h>#include<sys/mman.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>int main(){//打开open一个文件int fd = open("tmp.txt", O_RDWR|O_CREAT, 0666);//2、拓展文件大小truncate("tmp.txt", 16);//3、映射空间char *str = (char *)mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//4、使用空间(写操作)strcpy(str,"hello map");//5、解除空间映射munmap(str, 16);return 0;}

运行结果:

案例:读操作

#include<stdio.h>#include<string.h>#include<sys/mman.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>int main(){//打开open一个文件int fd = open("tmp.txt", O_RDWR|O_CREAT, 0666);//2、拓展文件大小truncate("tmp.txt", 16);//3、映射空间char *str = (char *)mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//4、使用空间(读操作)printf("文件的内容为:%s\n",str);//5、解除空间映射munmap(str, 16);return 0;}

运行结果:

5、内存共享

共享内存的特点:

1、共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。

2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。 若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。

在ubuntu 部分版本中共享内存限制值如下 共享存储区的最小字节数:1 共享存储区的最大字节数:32M 共享存储区的最大个数:4096 每个进程最多能映射的共享存储区的个数:4096

共享内存的步骤:

1、获得唯一的key值

#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);功能:获得项目相关的唯一的IPC键值。参数:pathname:路径名proj_id:项目ID,非0整数(只有低8位有效)返回值:成功返回key值,失败返回 -1

2、通过key得到获得一个共享存储标识符id

#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size,int shmflg);功能:创建或打开一块共享内存区参数:key:IPC键值size:该共享存储段的长度(字节)shmflg:标识函数的行为及共享内存的权限。参数:shmflg:IPC_CREAT:如果不存在就创建IPC_EXCL:如果已经存在则返回失败位或权限位:共享内存位或权限位后可以设置共享内存的访问权限,格式和open函数的mode_t一样,但可执行权限未使用。返回值:成功:返回共享内存标识符。失败:返回-1。

使用shell命令操作共享内存: 查看共享内存 ipcs -m , 删除共享内存 ipcrm -m shmid

3、将物理内存 映射到虚拟内存:

#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr,int shmflg);功能:将一个共享内存段映射到调用进程的数据段中。参数:shmid:共享内存标识符。shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL。shmflg:共享内存段的访问权限和映射条件0:共享内存具有可读可写权限。SHM_RDONLY:只读。SHM_RND:(shmaddr非空时才有效)没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。指定了SHM_RND则此段连接到shmaddr- shmaddr%SHMLBA 所表示的地址上。返回值:成功:返回共享内存段映射地址失败:返回 -1注意:shmat函数使用的时候第二个和第三个参数一般设为NULL和0,即系统自动指定共享内存地址,并且共享内存可读可写

4、操作空间内容

5、断开映射

#include <sys/types.h>#include <sys/shm.h>int shmdt(const void *shmaddr);功能:将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。参数:shmaddr:共享内存映射地址。返回值:成功返回 0,失败返回 -1。

6、物理内存控制(不必须):

#include <sys/ipc.h>#include <sys/shm.h>int shmctl(int shmid, int cmd,struct shmid_ds *buf);功能:共享内存空间的控制。参数:shmid:共享内存标识符。cmd:函数功能的控制。buf:shmid_ds数据类型的地址,用来存放或修改共享内存的属性。cmd:函数功能的控制IPC_RMID:删除。IPC_SET:设置shmid_ds参数。IPC_STAT:保存shmid_ds参数。SHM_LOCK:锁定共享内存段(超级用户)。SHM_UNLOCK:解锁共享内存段。返回值:成功返回 0,失败返回 -1。注意: SHM_LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。 这样做的优势在于让共享内存一直处于内存中,从而提高程序性能

案例:写

#include<stdio.h>#include<string.h>#include<sys/shm.h>#include<sys/ipc.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>int main(){//1、ftok获得key值key_t key = ftok("/", );//2、得到物理内存标识int shm_id = shmget(key, 16, IPC_CREAT|0666);printf("shm_id=%d\n", shm_id);//3、将物理内存 映射到虚拟内存:char *str = (char *)shmat(shm_id,NULL, 0);//4、操作空间内容strcpy(str,"hello shm");//5、解除映射shmdt(str);}

案例:读

#include<stdio.h>#include<string.h>#include<sys/shm.h>#include<sys/ipc.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>int main(){//1、ftok获得key值key_t key = ftok("/", );//2、得到物理内存标识int shm_id = shmget(key, 16, IPC_CREAT|0666);printf("shm_id=%d\n", shm_id);//3、将物理内存 映射到虚拟内存:char *str = (char *)shmat(shm_id,NULL, 0);//4、操作空间内容printf("共享内存的内容为:%s\n",str);//5、解除映射shmdt(str);}

运行结果:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。