Linux内核IPC机制
在Linux内核中IPC全名称之为进程间通信机制,共分为多种,如下:
- 信号量
- 共享内存
- 消息队列
- 管道
- 信号
- 套接字通信
每一种实现都有其独特特性,一下博客中我们会讲解其中的分类,作用以及优缺点。
分类:
1.分类
按照实现机制分类:
信号量,共享内存,消息队列是在内核中使用了IPC命名空间实现,由统一的键值管理(之前博客中有讲解过IDR机制管理键值),实现起来比较简单。
管道是基于文件系统实现的,信号是内核专门实现信号的task_struct结构体来实现的,套接字通信是基于网络通信来实现的。
按照功能分类:
共享内存, 消息队列,管道和套接字通信是可以传输比较大的数据的,信号量是基于共享内存来实现互斥机制的,信号虽然也可以传输数据,但是其主要作用是给特定进程发送特定信号标记的。
上述数据传输比较大的几个IPC通信机制中,最有效的传输当属共享内存传输了,其传输原理是在内核中共享内核指向的物理空间,导致数据即可以从进程1写入也可以从进程2写入。但是写入的时机和读取的时机要严格实现互斥,否则可能导致如下场景:数据从进程2读取过程中进程1也要写入数据,导致数据传输不同步。这就需要引入我们今天讨论的信号量机制了。
想像一下上述场景,如下图所示:
可以看到在CPU时间1无锁操作共享内存时候,进程1和2同时能够操作共享内存,结果对于两个进程来说都是未知。
在有锁情况下操作共享内存的情况下谁先获取锁谁就可以获取共享内存,后获取锁就会后操作共享内存,会保证数据的有效性和完整性。
信号量的原理明白了对于后一步的实际原理就会更加清晰,信号量本质上就是多个进程间的互斥机制,对于进程间的共享的数据的一种加锁机制。
加锁的流程如下:
1. semget获取信号量ID号;
2. semctl设置信号量值(本例程测试情况下设置1),本函数在创建锁的情况下或者需要修改锁的情况下使用,其他情况下不使用。
3. 使用semop获取锁;
创建锁例程:
#include <linux/types.h>
#include <linux/ipc.h>
#include <linux/sem.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define sem_key 0x1234
#define PERMS 0666
static struct sembuf sem_down[1] = {0, -1, 0};
static struct sembuf sem_up[1] = {0, 1, 0};
int semid = 0;
void init_sema(void)
{
int ret = 0;
semid = semget(sem_key, 1, IPC_CREAT | PERMS);
if(semid < 0){
perror("create sema failed:\n");
exit(1);
}
ret = semctl(semid , 0, SETVAL , 1);
if(ret){
perror("set sema val falied: ");
exit(2);
}
}
void down(void)
{
int ret = semop(semid , sem_down, 1);
if(ret){
perror("down sema : ");
exit(3);
}
}
void up(void)
{
int ret = semop(semid , sem_up, 1);
if(ret){
perror("up sema: ");
exit(4);
}
}
int main(void)
{
init_sema();
down();
sleep(100);
up();
return 0;
}
获取已经床架的锁的例程:
#include <linux/types.h>
#include <linux/ipc.h>
#include <linux/sem.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define sem_key 0x1234
#define PERMS 0666
static struct sembuf sem_down[1] = {0, -1, 0};
static struct sembuf sem_up[1] = {0, 1, 0};
int semid = 0;
void init_sema(void)
{
int ret = 0;
semid = semget(sem_key, 0, IPC_CREAT | PERMS);
if(semid < 0){
perror("create sema failed:\n");
exit(1);
}
}
void down(void)
{
int ret = semop(semid , sem_down, 1);
if(ret){
perror("down sema : ");
exit(3);
}
}
void up(void)
{
int ret = semop(semid , sem_up, 1);
if(ret){
perror("up sema: ");
exit(4);
}
}
int main(void)
{
init_sema();
down();
printf("success get\n");
sleep(10);
up();
printf("success up\n");
return 0;
}
以上过程是先创建锁然后再获取已经创建的锁,从而实现两个进程之间的互斥关系。