进程间通信
进程间通信
- 前言
- 使用文件实现进程间的通信
- 使用管道实现进程间的通信
- 共享内存
- 以上三种通信方式的区别
- 信号量
- 信号
- 消息队列
1. 前言
进程间通信的方式可谓是面试热点,面试的时候也曾有幸被问到。当时呢是背的面试题,对进程间通信的原理也不是很了解。今天呢就深入了解进程间的通信。
首先捋一捋进程间的通信有哪几种方式:
- socket
- 文件
- 管道
- 共享内存
- 信号
- 信号量
- 消息队列
2. 使用文件实现进程间的通信
文件:以磁盘作为信息的载体。
示例:
- 服务器:提供日期时间服务。
客户端:读取服务器提供的数据或信息。
基本思想:
服务器进程将数据写入文件
客户端进程从文件中读出数据。
// server.c
include
include
include
include
include
include
int main(int argc, char * argv[])
{int fd;
time_t now;
char *message;
if (argc!=2)
{
printf("errror usage ! \nusage: server filename\n") ;
exit(1);
}
if((fd=open(argv[1],O_CREAT|O_WRONLY|O_TRUNC , 0644))==-1)
{
perror("open");
exit(1);
}
while(1)
{
time (&now);
message=ctime (&now) ;
// lseek 改变文件读写指针的位置
// SEEK_SET 文件的开始
// 因为这里 time 的字节数始终相等,所以覆盖掉之前写的数据,每次文件中只会保存最新的数据
if( (lseek(fd,0,SEEK_SET))==-1)
{
perror("lseek") ;
exit(1) ;
}
if(write (fd, message, strlen(message))==-1)
{
perror("write");
exit(1) ;
}
sleep(1);
}
}
// client.c
include
include
include
include
int main(int argc, char *argv[])
{int fd, len;
char buf[128] ;
if(argc!=2)
{
printf("error usage!\nusage: client filename") ;
exit(1);
}
if((fd=open(argv[1],O_RDONLY))==-1)
{
perror("open");
exit(1);
}
while((len=read(fd, buf, 128) )>0)
{
write(1, buf ,len) ;
}
close(fd) ;
}
3. 使用管道实现进程间的通信
管道:以内核空间存放信息。
管道又分为有名管道和匿名管道,有名管道用于无亲缘关系的进程间通信,而匿名管道则用于有亲缘关系进程间通信。
示例:
- 服务器:提供日期时间服务。
客户端:读取服务器提供的数据或信息。
基本思想:
服务器进程建立命名管道,将数据写入管道。
客户端进程从管道中读出数据。
// server.c
include
include
include
include
include
include
include
int main(int argc, char * argv[])
{int fd;
time_t now ;
char * message;
if(argc!=2)
{
printf("errror usage! \nusage: server fifoname\n");
exit(1);
}
unlink(argv[1]);
// 创建有名管道
if(mkfifo(argv[1], 0644)==-1)
{
perror("mkfifo") ;
exit(1) ;
}
if((fd=open(argv[1],O_WRONLY))==-1)
{
perror("open");
exit(1) ;
}
while(1)
{
time (&now);
message=ctime(&now) ;
if(write (fd, message, strlen(message))==-1)
{
perror("write") ;
exit(1) ;
}
sleep(1);
}
}
// client.c
include
include
include
include
int main(int argc, char *argv[])
{int fd, len;
char buf[128] ;
if(argc!=2)
{
printf("error usage!\nusage: client filename") ;
exit(1);
}
if((fd=open(argv[1],O_RDONLY))==-1)
{
perror("open");
exit(1);
}
while((len=read(fd, buf, 128) )>0)
{
write(1, buf ,len) ;
}
close(fd) ;
}
4. 共享内存
共享内存是`System V IPC`的一种,不依赖于进程的存在而存在。
以下三种类型的进程通信方式(`IPC`)合称为`System V IPC`:
- System V 消息队列
- System V 信号量
System V 共享内存区
共享内存的通信效率比管道的效率更高。
共享内存的通信原理图如下所示:
共享内存的使用
- 创建(
shmget
) - 连接(
shmat
) - 读写(
strcpy
、memcpy
) - 解开连接(
shmdt
) 删除(
shmctl
)使用共享内存通信调用上方的函数即可实现。
5. 以上三种通信方式的区别
**文件**
- 权限:文件由服务器创建,客户端进程必须具有读文件的权限。.
- 多个用户:多个客户端程序可以从文件中读取数据。
读写冲突:存在,读写操作需要互斥进行。
**管道**
权限:服务器创建管道并写入数据;客户端读取管道中的数据。
- 多个用户:命名管道是一个内核中的队列,排队首的第-个读进程会将数据取走。
读写冲突: 管道传输数据需要读写双方同时打开管道。多个读进程会排队读取数据;读写进程在管道的不同端,不存在冲突。
**共享内存**
权限:共享内存为属主、组用户和其他用户设置了权限,服务器端拥有共享内存,客户端进程要能够读取共享内存。
- 多用户:多个客户端可以同时从共享内存段中读取数据。
- 读写冲突:存在。客户端读取数据时,有可能服务器端在写数据,因此需要辅助其他控制手段,保证读写共享内存段互斥进行。
访问速度:
文件:外存介入
命名管道:用户态和内核态的转换
共享内存:外存有可能介入使用范围:
命名管道、共享内存:本机上的进程
文件:不同机器上的进程
6. 信号量
信号量是一个内存变量,可以被系统中的任何进程所访问。
多个进程使用信号量来协调对临界资源的访问。
`Linux`中信号量是以集合的形式存在的,一个集合中存在着多个信号量。
信号量的使用:
- 创建信号量集(
semget
) - 执行
PV
操作(semop
) 删除信号量集合(
semctl
)信号量机制是为了协调多个进程对资源的使用,
semop
操作对应于操作系统中的PV原语。但与操作系统中的信号量有所不同:信号量机制中最小单位是信号量集,并非单个整型数,信号量集中的信号量数目在创建该集合时指定。
- 创建信号量集与对其赋初值这两个操作是分开的,并非合在一起的原子操作,这是一个致命的弱点。
- 有些程序在终止时并没有释放已经分配给它的信号量集,
SEM_ UNDO
标识就是用来处理这些情况的。
7. 信号
信号是内核发送给某一进程的一种消息。
如一个正在运行的程序,我们按下`Ctrl + C`就可以终止该程序。
信号机制`Linux`系统中用于进程之间相互通信或操作的一种机制。
8. 消息队列
消费队列,生产者、消费者的模式。如`kafka`、`RabbitMQ`等,一个进程复杂生产消息,另一个进程复杂消费消息。
还没有评论,来说两句吧...