- 在linux环境下,进程地址空间相互独立,彼此隔离,因此进程间的数据之间不能访问,如果要交换数据,就必须通过内核,在内核开辟一块缓冲区,进程a把数据从用户空间拷贝到内核缓冲区,进程b再把数据从内核缓冲区中拷贝走,内核提供的这种机制称为进程通信
- 进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的程序进程,使之能在一个操作系统里同时运行。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。IPC接口就提供了这种可能性。
进程间通信主要包括系统IPC(进程间通信)(包括消息队列,信号量集,共享存储), 套接字(SOCKET),管道(PIPLE)
- 消息队列是System V 中的一种进程间的通信机制(信号量、消息队列、共享内存),消息队列就好比是快递柜,发送方发送消息的时候会把要发送的数据放在快递柜中,接收方在方便的时候可以从快递柜中把消息拿出来,在linux系统中,消息队列的本质是内核维护的一片内存
- 消息队列可以有效避免管道的弊端:无名管道的通信需要进程之间有血缘关系,有名管道通信的数据是单向流动的(半双工),并且管道通信智能单播(单对单通信)
- 消息队列有如下特点:①消息队列中的消息是有类型的;②消息队列的消息是有格式的;③消息队列的消息可以随机查看,不一定按照次序读取,可以只读取某一个执行的数据;④消息队列允许多个线程向队列写入或者读取;⑤与管道相同,读取到的消息会被队列删除;⑥消息队列会有消息队列标识符,消息队列标识符在整个系统唯一;⑦只有内核重启或者人工删除消息队列时。消息队列才会被删除,否则消息队列一直存在于系统中;⑧在一个系统中可能有若干个消息队列,由所有的消息队列的头标组成一个头标数组。**
//running.c
#include
#include
#include
#include
#include
#include
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(){
int running=1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
while(running){
printf("Enter some text:");
fgets(buffer,BUFSIZ,stdin);
some_data.my_msg_type=1;
strcpy(some_data.some_text,buffer);
if(msgsnd(msgid,(void*)&some_data,MAX_TEXT,0)==-1){//send 发送消息到msgid中
fprintf(stderr,"msgsnd failed\n");
exit(EXIT_FAILURE);
}
if(strncmp(some_data.some_text,"end",3)==0) running=0;
}
exit(EXIT_SUCCESS);
}
//running2.c
#include
#include
#include
#include
#include
#include
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(){
int running=1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive=0;//接受消息类型设置,按照先进先出的顺序接收数据
//获取消息队列
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
while(running){
if(msgrcv(msgid,(void*)&some_data,BUFSIZ,msg_to_receive,0)==-1){
fprintf(stderr,"msgrcv failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
printf("You wrote:%s",some_data.some_text);
if(strncmp(some_data.some_text,"end",3)==0) running=0;
}
if(msgctl(msgid,IPC_RMID,0)==-1){//把消息队列删除
fprintf(stderr,"msgctl(IPC_RMID)failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
int msgget(key_t key, int msgflg)
:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的标识。key:IPC键值;msgflg:消息队列的权限值和mode_t一样,无可执行权限。相关宏如下:
IPC_CREAT:创建消息队列
IPC_EXCL:监测消息队列是否存在
函数返回值:成功返回消息队列标识符,失败返回-1
消息队列的消息格式
:消息队列的消息类型必须是长整型,且必须是结构体的第一个成员,消息下面是消息正文正文可以有多个成员(正文的成员可以是任意类型)如上面代码的struct my_msg_st
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
:将新消息添加到消息队列;msqid:消息队列标识符;msgp: 待发送消息结构体的地址;msgsz:消息正文的字节数;msgflg: 函数的控制属性
0 : msgsnd调用阻塞直到条件满足为止(eg:如果消息队列满了,阻塞;推荐);
IPC_NOWAIT:若消息没有立即发送则调用该函数的进程会立即返回。
函数返回值:成功返回0,失败返回-1;
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
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
:对消息队列进行各种控制,如修改消息队列的属性,或删除消息队列。msqid:消息队列的标识符;cmd:函数功能的控制
IPC_RMID:删除由msqid指示的消息队列,将它从系统中删除并破坏相关数据结构。
IPC_STAT:将msqid相关的数据结构中各个元素的当前值存入到由buf指向的结构体中。
IPC_SET:将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值。
buf:msqid_ds数据类型的地址,用来存放或更改消息队列的属性。
函数返回值:成功返回0,失败返回-1
ipcs 命令可以查看系统中已经创建的消息队列
;参数:
-m 查看系统共享内存信息
-q 查看系统消息队列信息
-s 查看系统信号量信息
-a 显示系统内所有的IPC信息
移除:
ipcrm -m shmid 移除用shmid标识的共享内存段
ipcrm -s semid 移除用semid标识的信号量
ipcrm -q msgid 移除用msgid标识的消息队列
运行结果:
[cch@aubin ipc]$ gcc running.c -o running
[cch@aubin ipc]$ gcc running2.c -o running2
[cch@aubin ipc]$ ./running
Enter some text:hello
Enter some text:hja
Enter some text:jejje
Enter some text:qkkoq
Enter some text:jwjs
Enter some text:zid
Enter some text:end
[cch@aubin ipc]$
[cch@aubin ipc]$ ./running2
You wrote:hello
You wrote:hja
You wrote:jejje
You wrote:qkkoq
You wrote:jwjs
You wrote:zid
You wrote:end
[cch@aubin ipc]$ ipcs
- 消息的发送
当进程要与其它进程通信时,可利用msgsnd( )系统调用来发送消息。对于msgsnd( )系统调用,核心检查消息队列描述符和许可权是否合法,消息长度是否超过系统规定的长度。 通过检查后,核心为消息分配消息数据区,并将消息从用户消息缓冲区拷贝到消息数据区。分配消息首部,将它链入消息队列的末尾;在消息首部中填写消息的类型、大小以及指向消息数据区的指针等;还要修改消息队列头标中的数据(如消息队列中的消息数、字节数等)。然后,唤醒在等待消息到来的睡眠进程。- 消息的接收
进程可利用msgrcv( )系统调用,从指定消息队列中读一个消息。对于msgrcv( )系统调用,先由核心检查消息队列标识符和许可权,继而根据用户指定的消息类型做相应的处理。消息类型msgtyp的参数可能有三种情况:当msgtyp=0时,核心寻找消息队列中的第一个消息,并将它返回给调用进程;当msgtyp为正整数时,核心返回指定类型的第一个消息;当msgtyp为负整数时,核心应在其类型值小于或等于msgtyp绝对值的所有消息中,选出类型值最低的第一个消息返回。如果所返回消息的大小等于或小于用户的请求,核心便将消息正文拷贝到用户区,再从队列中删除该消息,并唤醒睡眠的发送进程;如果消息长度比用户要求的大,则系统返回出错信息。用户也可忽略对消息大小的限制,此时,核心无需理会消息的大小而一概把消息内容拷贝到用户区。
头文件
#include
#include
#include
#include
#include
#include
int main(){
//创建一个信号量集,并且设置信号量集中信号量的值为1
key_t key=12345;
//int sem_id=semget(key,1,0666|IPC_CREAT);//不存在就创建
int sem_id=semget(IPC_PRIVATE,1,0666|IPC_CREAT);//不存在就创建
if(sem_id==-1)
{
perror("semget error");
exit(1);
}
union semun{
int val;
struct semid_ds *buf;
ushort *array;
}argument;//联合体
argument.val=1;
if(semctl(sem_id,0,SETVAL,argument)==-1){//设置信号量的值为1
perror("semctl error");
exit(1);
}
//对信号p操作
struct sembuf sops;
sops.sem_num=0;
sops.sem_op=-1;//p
sops.sem_flg=0;
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//互斥区
printf("互斥区操作\n");
sops.sem_op=1;//v操作
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//删除信号量
if(semctl(sem_id,0,IPC_RMID)<0)
{
perror("semctl error");
return -1;
}
return 0;
}
[cch@aubin os]$ cd ipc
[cch@aubin ipc]$ gcc sem.c -o sem
[cch@aubin ipc]$ ./sem
互斥区操作
[cch@aubin ipc]$
int semget(key_t key, int nsems, int semflg)
:是 Linux 中的一个系统调用,用于创建或获取一个信号量集的标识符。信号量集是一个包含若干个信号量的数据结构,每个信号量都是一个整型值,用于在进程之间进行同步和互斥。参数:
key:一个整型值,用于标识信号量集。如果 key 为 0,则系统会自动生成一个唯一的 key 值。
nsems:表示信号量集中包含的信号量个数。
semflg:表示创建信号量集的权限和行为。可以使用以下值:
IPC_CREAT:如果信号量集不存在,则创建一个新的信号量集;如果信号量集已存在,则获取它的标识符。
IPC_EXCL:与 IPC_CREAT 一起使用时,如果信号量集已存在,则调用 semget() 函数失败,并返回错误。
返回值:
成功:返回信号量集的标识符。
失败:返回 -1,并设置 errno 为相应的错误码。
int semop(int semid, struct sembuf *sops, unsigned nsops)
:用于对信号量集进行操作的函数。通过修改信号量集中每个信号量的值来实现对信号量的 P 操作和 V 操作。semid 指信号量集的标识符;sops 指向一个信号操作结构体的指针;nsops 为要执行的信号操作的数量。
返回值为 0 表示执行成功,-1 表示执行失败。
struct sembuf
{
unsigned short int sem_num; /* 信号量的序号从 0~nsems-1 */
short int sem_op; /* 对信号量的操作,>0, 0, <0 */
short int sem_flg; /* 操作标识:0, IPC_WAIT, SEM_UNDO */
};
其中,sem_num 为信号量在信号量集中的编号;
sem_op 为要执行的信号操作(P操作或V 操作)的操作数;
sem_flg 为执行信号操作的标志(如是否阻塞等)。
//一个p操作
int sem_id; // 信号量的标识符
struct sembuf sops; // 信号量操作结构体
sops.sem_num = 0; // 信号量的编号(对应信号量集中的第几个信号量)
sops.sem_op = -1; // 要执行的操作(-1 表示 P 操作,1 表示 V 操作)
sops.sem_flg = 0; // 操作标志
semop(sem_id, &sops, 1); // 执行信号量操作
//两个p操作
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
semop(sem_id, sops, 2);
int semctl(int semid, int semnum, int cmd, ... /* arg */)
:用于控制信号量集的函数。它支持多种操作,可以用于获取信号量集的属性、设置信号量集的属性、控制信号量的值等。semid:信号量集的标识符。semnum:要操作的信号量的编号。是信号量在信号量集中的编号;cmd 是要执行的命令,后面的参数是根据 cmd 的值而定的。
如果 cmd 为 SETVAL 或 SETALL,则此参数被忽略。
cmd:指定要执行的操作。(操作命令)
可以是以下值之一:
GETVAL:获取信号量的值。
SETVAL:设置信号量的值,取自 arg 的 val 元素。
GETPID:返回最后一个执行 semop 函数的进程的进程号。
GETNCNT:获取当前等待信号量为正值的进程数。
GETZCNT:获取当前等待信号量为 0 的进程数。
GETALL:获取信号量集中所有信号量的值。
SETALL:设置信号量集中所有信号量的值。
IPC_STAT:获取信号量集的属性。
IPC_SET:设置信号量集的属性。
IPC_RMID:删除信号量集。
常用的有:
semctl() 函数有多种用途,常用的命令有以下几种:
GETVAL:获取信号量的值。
SETVAL:设置信号量的值。
IPC_RMID:删除信号量集。
IPC_STAT:获取信号量集的属性。
IPC_SET:设置信号量集的属性。
arg:可变参数,依赖于 cmd 的值而不同。如果 cmd 为 SETVAL 或 SETALL,则 arg 指向一个 int 类型的值,表示要设置的信号量的值。
头文件
#include
#include
int shmget(key_t key, size_t size, int shmflg)
:创建或获取共享内存; key: 共享内存的键值,是共享内存在系统中的编号,不同共享内存编号不能相同,用十六进制比较好; size: 共享内存的大小,单位byte; shmflg: 共享内存的访问权限,与文件差不多, 0666 | IPC_CREAT 表示所有用户都可读写,IPC_CREAT 表示如果不存在则创建;返回值:成功返回一个id,失败返回-1,并设置errno
void *shmat(int shmid, const void *shmaddr, int shmflg)
:将共享内存链接到当前进程的地址空间;shmid: shmflg返回的id;shmaddr: 共享内存链接到当前进程中的地址位置,通常为NULL,表示让系统来选择; shmflg: 标志位,通常为0;返回值:成功返回共享内存段的地址,失败返回 (void *)-1, 并设置errno
int shmdt(const void *shmaddr)
:将共享内存从当前进程分离;shmaddr: 共享内存地址,返回值: 成功返回0, 失败返回-1, 设置errno
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
:操作共享内存,可以用来删除一个共享内存;shmid: 共享内存id;cmd: 执行的操作, IPC_RMID表示删除;buf: 设置为NULL;返回值:失败返回-1, 设置errno
//share2.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096
int read_file(char *buf, int size, const char *filename);
int main() {
int n, shm_id;
char buf[MAXBUFSIZE] = {0};
char *shm_p = NULL;
/*
获取共享内存,如果不存在则创建
*/
if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644 | IPC_CREAT)) < 0) {
perror("shmget error");
return 1;
}
/*
把共享内存链接到当前进程的地址空间
*/
shm_p = (char *)shmat(shm_id, NULL, 0);
if (shm_p == (void *) -1) {
perror("shmat error:");
return 1;
}
// 从文件中读取数据
n = read_file(buf, MAXBUFSIZE, "./read_text");
if (n == -1) {
return 1;
}
printf("read from file: %s\n", buf);
// 将数据拷贝到共享内存
memcpy(shm_p, buf, strlen(buf));
// 把共享内存从当前进程分离
shmdt(shm_p);
return 0;
}
// 读文件内容到buf
int read_file(char *buf, int size, const char *filename) {
int fd, n;
// 打开read_test
if ( (fd = open(filename, O_RDONLY)) < 0) {
perror("open file_read_test error:");
return -1;
}
// 读取文件内容
n = read(fd, buf, size);
if (n < 0) {
perror("read file error:");
return -1;
}
// 关闭文件
close(fd);
return n;
}
//share
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096
int write_file(const char *buf, int size, const char *filename);
int main() {
int shm_id;
char buf[MAXBUFSIZE] = {0};
char *shm_p = NULL;
/*
获取共享内存,如果不存在则创建
*/
if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644)) < 0) {
perror("shmget error");
return 1;
}
/*
把共享内存链接到当前进程的地址空间
*/
shm_p = (char *)shmat(shm_id, NULL, 0);
if (shm_p == (void *) -1) {
perror("shmat error:");
return 1;
}
// 拷贝共享内存中的数据
memcpy(buf, shm_p, MAXBUFSIZE);
// 将共享内存中的内容写入write_text文件中
write_file(buf, strlen(buf), "write_text");
printf("write to file: %s\n", buf);
// 把共享内存从当前进程分离
shmdt(shm_p);
//删除共享内存
if (shmctl(shm_id, IPC_RMID, 0) == -1) {
printf("shmctl failed\n");
return -1;
}
return 0;
}
// 将buf中的数据写入文件
int write_file(const char *buf, int size, const char *filename) {
int fd, n;
// 打开文件,不存在则创建
if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("open file error:");
return -1;
}
// 写入内容
n = write(fd, buf, size);
if (n < 0) {
perror("write file error");
return -1;
}
close(fd);
return n;
}
[cch@aubin ipc]$ gcc share.c -o share
[cch@aubin ipc]$ gcc share2.c -o share2
[cch@aubin ipc]$ ./share2
read from file: this is read text
[cch@aubin ipc]$ ./share
write to file: this is read text
[cch@aubin ipc]$
两者在信号量上的优缺点:
POSIX在无竞争条件下是不会陷入内核的,而System-V IPC则是无论何时都要陷入内核,因此性能稍差。
POSIX的sem_wait函数成功获取信号量后,进程如果意外终止,将无法释放信号量,而System V则提供了SEM_UNDO选项来解决这个问题。因此,相比而言,后者更加可靠。
管道:linux进程管道通信
信号:信号处理signal函数
互斥锁:互斥锁
内存映射:文件内存映射
使用代码展示信号量、共享内存和消息队列的典型应用场景。
信号量:
#include
#include
#include
#include
#include
int main(){
//创建一个信号量集,并且设置信号量集中信号量的值为1
key_t key=12345;
int sem_id=semget(key,1,0666|IPC_CREAT);//不存在就创建
if(sem_id==-1)
{
perror("semget error");
exit(1);
}
//对信号了p操作
struct sembuf sops;
sops.sem_num=0;
sops.sem_op=-1;//p
sop.sem_flg=0;
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//互斥区
printf("互斥区操作\n");
sops.sem_op=1;//v操作
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//删除信号量
if(semctl(sem_id,0,IPC_RMID)<0)
{
perror("semctl error");
return -1;
}
/*
union semun{
int val;
struct semid_ds *buf;
ushort *array;
}argument;
argument.val=1;
if(semctl(sem_id,0,SETVAL,argument)==-1){//设置信号量的值
perror("semctl error");
exit(1);
}
*/
return 0;
}
共享内存:
//share2.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096
int read_file(char *buf, int size, const char *filename);
int main() {
int n, shm_id;
char buf[MAXBUFSIZE] = {0};
char *shm_p = NULL;
/*
获取共享内存,如果不存在则创建
*/
if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644 | IPC_CREAT)) < 0) {
perror("shmget error");
return 1;
}
/*
把共享内存链接到当前进程的地址空间
*/
shm_p = (char *)shmat(shm_id, NULL, 0);
if (shm_p == (void *) -1) {
perror("shmat error:");
return 1;
}
// 从文件中读取数据
n = read_file(buf, MAXBUFSIZE, "./read_text");
if (n == -1) {
return 1;
}
printf("read from file: %s\n", buf);
// 将数据拷贝到共享内存
memcpy(shm_p, buf, strlen(buf));
// 把共享内存从当前进程分离
shmdt(shm_p);
return 0;
}
// 读文件内容到buf
int read_file(char *buf, int size, const char *filename) {
int fd, n;
// 打开read_test
if ( (fd = open(filename, O_RDONLY)) < 0) {
perror("open file_read_test error:");
return -1;
}
// 读取文件内容
n = read(fd, buf, size);
if (n < 0) {
perror("read file error:");
return -1;
}
// 关闭文件
close(fd);
return n;
}
//share
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096
int write_file(const char *buf, int size, const char *filename);
int main() {
int shm_id;
char buf[MAXBUFSIZE] = {0};
char *shm_p = NULL;
/*
获取共享内存,如果不存在则创建
*/
if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644)) < 0) {
perror("shmget error");
return 1;
}
/*
把共享内存链接到当前进程的地址空间
*/
shm_p = (char *)shmat(shm_id, NULL, 0);
if (shm_p == (void *) -1) {
perror("shmat error:");
return 1;
}
// 拷贝共享内存中的数据
memcpy(buf, shm_p, MAXBUFSIZE);
// 将共享内存中的内容写入write_text文件中
write_file(buf, strlen(buf), "write_text");
printf("write to file: %s\n", buf);
// 把共享内存从当前进程分离
shmdt(shm_p);
//删除共享内存
if (shmctl(shm_id, IPC_RMID, 0) == -1) {
printf("shmctl failed\n");
return -1;
}
return 0;
}
// 将buf中的数据写入文件
int write_file(const char *buf, int size, const char *filename) {
int fd, n;
// 打开文件,不存在则创建
if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("open file error:");
return -1;
}
// 写入内容
n = write(fd, buf, size);
if (n < 0) {
perror("write file error");
return -1;
}
close(fd);
return n;
}
[cch@aubin ipc]$ gcc share.c -o share
[cch@aubin ipc]$ gcc share2.c -o share2
[cch@aubin ipc]$ ./share2
read from file: this is read text
[cch@aubin ipc]$ ./share
write to file: this is read text
[cch@aubin ipc]$
消息队列:
//running.c
#include
#include
#include
#include
#include
#include
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(){
int running=1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
while(running){
printf("Enter some text:");
fgets(buffer,BUFSIZ,stdin);
some_data.my_msg_type=1;
strcpy(some_data.some_text,buffer);
if(msgsnd(msgid,(void*)&some_data,MAX_TEXT,0)==-1){//send 发送消息到msgid中
fprintf(stderr,"msgsnd failed\n");
exit(EXIT_FAILURE);
}
if(strncmp(some_data.some_text,"end",3)==0) running=0;
}
exit(EXIT_SUCCESS);
}
//running2.c
#include
#include
#include
#include
#include
#include
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(){
int running=1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive=0;//接受消息类型设置,按照先进先出的顺序接收数据
//获取消息队列
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1){
fprintf(stderr,"msgget failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
while(running){
if(msgrcv(msgid,(void*)&some_data,BUFSIZ,msg_to_receive,0)==-1){
fprintf(stderr,"msgrcv failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
printf("You wrote:%s",some_data.some_text);
if(strncmp(some_data.some_text,"end",3)==0) running=0;
}
if(msgctl(msgid,IPC_RMID,0)==-1){//把消息队列删除
fprintf(stderr,"msgctl(IPC_RMID)failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
使用代码来显示POSIX信号量和IPC信号量之间的差异。
Posix信号量
#include
#include
#include
int main() {
// 创建和初始化POSIX信号量
sem_t *posix_sem = sem_open("/my_posix_sem", O_CREAT, 0666, 1);
// 在临界区使用信号量
sem_wait(posix_sem);
// 临界区操作
printf("临界区操作\n");
sem_post(posix_sem);
// 删除POSIX信号量
sem_close(posix_sem);
sem_unlink("/my_posix_sem");
return 0;
}
IPC信号量:
#include
#include
#include
#include
#include
int main(){
//创建一个信号量集,并且设置信号量集中信号量的值为1
key_t key=12345;
int sem_id=semget(key,1,0666|IPC_CREAT);//不存在就创建
if(sem_id==-1)
{
perror("semget error");
exit(1);
}
//对信号p操作
struct sembuf sops;
sops.sem_num=0;
sops.sem_op=-1;//p
sop.sem_flg=0;
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//互斥区
printf("互斥区操作\n");
sops.sem_op=1;//v操作
if(semop(sem_id,&sops,1)<0){
perror("semop error");
return -1;
}
//删除信号量
if(semctl(sem_id,0,IPC_RMID)<0)
{
perror("semctl error");
return -1;
}
return 0;
}
posix信号量:
System-V IPC信号量:
在文件一章中,我们讨论了mmap(),它可以用于在进程之间共享内存。您能否将此技术与IPC的共享内存进行比较,并说明哪种技术更适合在进程之间共享大数据。
mmap: mmap是一种将文件内存映射到进程的虚拟地址空间的技术,在这种机制下,文件可以被视为内存的一部分,可以实现程序直接对这部分的内存进行读写操作,实现像访问内存一样对文件进行读写,这种方法提高了处理的效率,简化了文件的操作。
共享内存:共享内存使用的是shmget和shmat;共享内存提供semop,可以用于共享内存的进程之间的同步
二者区别:
数据源和持久化:
mmap: 通过 mmap 映射的数据通常来自文件系统中的文件。这意味着数据是持久化的——即使程序终止,文件中的数据依然存在。当你通过映射的内存区域修改数据时,这些更改最终会反映到磁盘上的文件中。
共享内存:共享内存是一块匿名的(或者有时与特定文件关联的)内存区域,它可以被多个进程访问。与 mmap 映射的文件不同,共享内存通常是非持久的,即数据仅在计算机运行时存在,一旦系统关闭或重启,存储在共享内存中的数据就会丢失。
使用场景:
mmap:mmap 特别适合于需要频繁读写大文件的场景,因为它可以减少磁盘 I/O 操作的次数。它也允许文件的一部分被映射到内存中,这对于处理大型文件尤为有用。
共享内存:共享内存通常用于进程间通信(IPC),允许多个进程访问相同的内存区域,这样可以非常高效地在进程之间交换数据。
性能和效率:
mmap:映射文件到内存可以提高文件访问的效率,尤其是对于随机访问或频繁读写的场景。系统可以利用虚拟内存管理和页面缓存机制来优化访问。
共享内存:共享内存提供了一种非常快速的数据交换方式,因为所有的通信都在内存中进行,没有文件 I/O 操作。
同步和一致性:
mmap:使用 mmap 时,必须考虑到文件内容的同步问题。例如,使用 msync 调用来确保内存中的更改被同步到磁盘文件中。
共享内存:在共享内存的环境中,进程需要使用某种形式的同步机制(如信号量、互斥锁)来避免竞争条件和数据不一致。
简而言之,mmap 主要用于将文件映射到内存以提高文件操作的效率,而共享内存主要用于进程间的高效数据交换。二者虽有相似之处,但各自适用于不同的应用场景,如果文件非常大,以至于无法或不方便完全加载到内存中时,可以使用mmap映射整个文件,并像访问内存数组一样访问文件的任何部分,而无需加载整个文件;如果是进程之间需要快速共享大量数据,可以使用共享内存。