Linux系统编程——进程间通信(System V IPC 对象)
基本查看命令
ipcs -m查看共享内存 ipcs -s查看信号量 ipcs -q查看消息队列
ipcrm -m id 删除共享内存 -M+key值
ipcrm -s id 删除信号量
ipcrm -q id 删除消息队列
(1)共享内存。为了在多个进程间进行信息交换,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。共享内存允许两个或者更多进程共享一给定的存储区,是一种效率最高的进程间通信方式,因为数据不需要再服务端和客户端之间进行复制,进程可以直接读写内存。
由于多个进程共享一段内存,因此也要依靠某种同步机制,如互斥锁和信号量等。第一个运行的进程创建共享内存,多个进程进行映射访问,最后一个访问的进程关闭共享内存。
两个不同进程A、B共享内存的意思是:同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <errno.h>
#include <sys/shm.h>
typedef struct
{
pid_t pid;
];
}SHM;
void func(int sig_no)
{
}
int main(int argc, const char *argv[])
{
int shmid;
SHM *p;
key_t key;
pid_t peerpid;
signal(SIGUSR1,func);
key = ftok(".",'s');
//shmget(,,0666);
//shmget(,,0666 | IPC_CREAT);
//shmget(,,0666 | IPC_CREAT | IPC_EXCL);
,IPC_CREAT | | IPC_EXCL)) < )
{
if(errno == EEXIST)
{
shmid = shmget(key,,);
)) < (SHM *))
{
perror("fail to shmat");
;
}
peerpid = p -> pid;
p->pid = getpid();
kill(peerpid,SIGUSR1);
}
else
{
perror("fail to shmget");
;
}
}
else
{
)) < (SHM *))
{
perror("fail to shmat");
;
}
p->pid = getpid();
pause();
peerpid = p -> pid;
}
/**********************write.c***************************/
)
{
fgets(p->text,,stdin);
kill(peerpid,SIGUSR1);
) == )break;
pause();
}
/*************************read.c************************/
)
{
pause();
printf("read from shm:%s",p->text);
) == ) break;
kill(peerpid,SIGUSR1);
}
/*************************************************/
)
{
perror("fail to shmdt");
;
}
)
{
perror("fail to shmctl");
;
}
;
共享内存的使用步骤:
创建打开共享内存;
映射共享内存,即把指定的内存空间映射到进程的地址空间;
撤销共享内存映射;
删除共享内存对象;
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。
(2)消息队列。消息队列是一个消息的列表,存放在内核中并且由消息队列标识符标识,用户可以在消息队列中添加消息和读取消息等操作。
特点:是双向数据流,并且当没有读取到队列中的消息时,会阻塞等待。
消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define TYPEA 100
#define TYPEB 200
struct msgbuf
{
long mtype;
];
};
int main(int argc, const char *argv[])
{
pid_t pid;
int len;
struct msgbuf msg;
key_t key;
int msgid;
len = sizeof(msg) - sizeof(long);
key = ftok(".",'a');
msgid = msgget(key, | IPC_CREAT);
pid = fork();
)
{
perror("fail to fork");
;
}
)
{
)
{
fgets(msg.mtext,,stdin);
msg.mtype = TYPEB;
msgsnd(msgid,&msg,len,);
) == )
{
kill(getppid(),SIGKILL);
exit();
}
}
}
else
{
)
{
msgrcv(msgid,&msg,len,TYPEA,);
printf("read from msg:%s",msg.mtext);
) == )
{
kill(pid,SIGKILL);
wait(NULL);
msgctl(msgid,IPC_RMID,NULL);
exit();
}
}
}
;
}
(3)信号灯集:是不同进程间或者是同一进程间不同线程间同步的机制。
共享资源的获取:
1.测试控制资源的信号量;
2.若信号量的值为正,则进程可以使用该资源。进程将信号量减一,表示它使用了一个资源单位。
3.若信号量的值为负,进程进入休眠状态,直到信号量值大于0 。进程被唤醒后,返回第一步。
当进程不再使用由一个信号量控制的共享资源时,该信号量减1.如果有进程正在休眠等待此信号,则唤醒他们。
为了正确的实现信号量,信号量的测试操作以及减1 操作都是原子操作。为此,信号量通常是在内核中实现。
常用的信号量形式被称为二元信号量或者是双态信号量。它控制单个资源,初始值为1 。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>
union semun
{
int val;
};
//0:write信号灯 1:read信号灯
int main(int argc, const char *argv[])
{
key_t key;
int semid,shmid;
union semun myun;
char * p;
struct sembuf buf;
key = ftok(".",'b');
,IPC_CREAT | IPC_EXCL | )) < )
{
if(errno == EEXIST)
{
semid = semget(key,,);
}
else
{
perror("fail to semget");
;
}
}
else
{//先运行,对信号灯集进行初始化
myun.val = ;//write
semctl(semid,,SETVAL,myun);
myun.val = ;//read
semctl(semid,,SETVAL,myun);
}
shmid = shmget(key,,IPC_CREAT | );
p = ();
)
{
//对read进行p操作(申请读资源)
buf.sem_num = ;
buf.sem_op = -;
buf.sem_flg = ;
semop(semid,&buf,);
printf("read from shm :%s",p);
//对write进行v操作(释放写资源)
buf.sem_num = ;
buf.sem_op = ;
buf.sem_flg = ;
semop(semid,&buf,);
}
;
}
由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间的信息传递不可能通过变量或其它数据结构直接进行,只能通过进程间通信来完成。
根据进程通信时信息量大小的不同,可以将进程通信划分为两大类型:控制信息的通信和大批数据信息的通信.前者称为低级通信,后者称为高级通信。
低级通信主要用于进程之间的同步、互斥、终止、挂起等等控制信息的传递。
高级通信主要用于进程间数据块的交换和共享 常见的高级通信有管道(PIPE)、消息队列(MESSAGE)、共享内存(SHARED MEM0RY)等。
这里主要比较一下高级通信的这三种方式的特点。
管道通信(PIPE)
两个进程利用管道进行通信时.发送信息的进程称为写进程.接收信息的进程称为读进程。管道通信方式的中间介质就是文件.通常称这种文件为管道文件.它就像管道一样将一个写进程和一个读进程连接在一起,实现两个进程之间的通信。写进程通过写入端(发送端)往管道文件中写入信息;读进程通过读出端(接收端)从管道文件中读取信息。两个进程协调不断地进行写和读,便会构成双方通过管道传递信息的流水线。
利用系统调用PIPE()可以创建一个无名管道文件,通常称为无名管道或PIPE;利用系统调用MKNOD()可以创建一个有名管道文件.通常称为有名管道或FIFO。无名管道是一种非永
久性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错.所以操作系统将命名管道的管理权交由系统来加以控制管道文件被创建后,可以通过系统调用WRITE()和READ()来实现对管道的读写操作;通信完后,可用CLOSE()将管道文件关闭。
消息缓冲通信(MESSAGE)
多个独立的进程之间可以通过消息缓冲机制来相互通信.这种通信的实现是以消息缓冲区为中间介质.通信双方的发送和接收操作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称之为消息队列。消息队列一旦创建后即可由多进程共享.发送消息的进程可以在任意时刻发送任意个消息到指定的消息队列上,并检查是否有接收进程在等待它所发送的消息。若有则唤醒它:而接收消息的进程可以在需要消息的时候到指定的消息队列上获取消息.如果消息还没有到来.则转入睡眠状态等待。
共享内存通信(SHARED MEMORY)
针对消息缓冲需要占用CPU进行消息复制的缺点.OS提供了一种进程间直接进行数据交换的通信方式一共享内存 顾名思义.这种通信方式允许多个进程在外部通信协议或同步,互斥机制的支持下使用同一个内存段(作为中间介质)进行通信.它是一种最有效的数据通信方式,其特点是没有中间环节.直接将共享的内存页面通过附接.映射到相互通信的进程各自的虚拟地址空间中.从而使多个进程可以直接访问同一个物理内存页面.如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。因此这种进程间通信方式是在同一个计算机系统中的诸进程间实现通信的最快捷的方法.而它的局限性也在于此.即共享内存的诸进程必须共处同一个计算机系统.有物理内存可以共享才行。
三种方式的特点(优缺点):
1.无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错。
2.消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的复制需要额外消耗CPU的时间.不适宜于信息量大或操作频繁的场合。
3.共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。
Linux系统编程——进程间通信(System V IPC 对象)的更多相关文章
- 四十九、进程间通信——System V IPC 之消息队列
49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...
- linux系统编程--进程间通信
IPC方法 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问, 要交换数据必须通过内核,在内核中开 ...
- linux c编程:System V消息队列一
消息队列可以认为是一个消息链表,System V 消息队列使用消息队列标识符标识.具有足 够特权的任何进程都可以往一个队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息.在某个进 ...
- Linux系统编程——进程间通信:信号中断处理
什么是信号? 信号是 Linux 进程间通信的最古老的方式.信号是url=474nN303T2Oe2ehYZjkrggeXCaJPDSrmM5Unoh4TTuty4wSgS0nl4-vl43AGMFb ...
- linux进程间通讯-System V IPC 信号量
进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...
- Linux系统编程——进程间通信:共享内存
概述 url=MdyPihmS_tWLwgWL5CMzaTrwDFHu6euAJJUAjKvlzbJmRw7RfhmkBWwAloo7Y65hLY-kQdHsbqWYP2wc2fk8yq"& ...
- Linux系统编程——进程间通信:管道(pipe)
管道的概述 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,全部的 UNIX 系统都支持这样的通信机制. 无名管道有例如以下特点: 1.半双工,数据在同一时刻仅仅能在一个 ...
- Linux系统编程——进程间通信(一)
基本操作命令: ps -ajx/-aux/-ef 查看进程间状态/的相互关系 top 动态显示系统中的进程 nice 按照指定的优先级运行 /renice 改变正在运行的进程的优先级 kill -9杀 ...
- Linux系统编程——进程间通信:命名管道(FIFO)
命名管道的概述 无名管道,因为没有名字,仅仅能用于亲缘关系的进程间通信(很多其它详情.请看<无名管道>).为了克服这个缺点.提出了命名管道(FIFO).也叫有名管道.FIFO 文件. 命名 ...
随机推荐
- [洛谷P5048][Ynoi2019模拟赛]Yuno loves sqrt technology III
题目大意:有$n(n\leqslant5\times10^5)$个数,$m(m\leqslant5\times10^5)$个询问,每个询问问区间$[l,r]$中众数的出现次数 题解:分块,设块大小为$ ...
- BZOJ5312:冒险——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5312 Kaiser终于成为冒险协会的一员,这次冒险协会派他去冒险,他来到一处古墓,却被大门上的守护 ...
- JavaScript中的valueOf与toString方法
基本上,所有JS数据类型都拥有valueOf和toString这两个方法,null除外.它们俩解决javascript值运算与显示的问题. JavaScript 的 valueOf() 方法 valu ...
- 用CSS3实现的addidas阿迪达斯标志LOGO
<!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>用CSS3实现的ad ...
- noip模拟赛 大芳的逆行板载
题目背景 大芳有一个不太好的习惯:在车里养青蛙.青蛙在一个n厘米(11n毫米s)的Van♂杆子上跳来跳去.她时常盯着青蛙看,以至于突然逆行不得不开始躲交叉弹.有一天他突发奇想,在杆子上每1厘米为一个单 ...
- 微服务学习一:idea中springboot集成mybatis
一直都想学习微服务,这段时间在琢磨这块的内容,个人之前使用eclipse,现在用intellij idea来进行微服务的开发,个人感觉intellij idea比eclipse更简洁更方便,因为int ...
- 【题解】Radio stations Codeforces 762E CDQ分治
虽然说好像这题有其他做法,但是在问题转化之后,使用CDQ分治是显而易见的 并且如果CDQ打的熟练的话,码量也不算大,打的也很快,思维难度也很小 没学过CDQ分治的话,可以去看看我的另一篇博客,是CDQ ...
- STM32 - 软件设置单片机重启
__set_FAULTMASK();//关闭总中断 NVIC_SystemReset();//请求单片机重启 执行NVIC_SystemReset()函数不允许被打断,所以关总中断
- 通过psexec实现远程安装软件包
1.在管理机上下载和安装psexec https://docs.microsoft.com/en-us/sysinternals/downloads/psexec 2.在管理机上编写bat脚本,存放在 ...
- [LeetCode] 8. String to Integer (atoi) ☆
Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input cases. ...