UNIX/Linux 是多任务的操作系统,通过多个进程分别处理不同事务来实现,如果多个进程要进行协同工作或者争用同一个资源时,互相之间的通讯就很有必要了
进程间通信,Inter process communication,简称 IPC,在 UNIX/Linux 下主要有以下几种方式:
这里分享一下我在学习进程通讯过程中的笔记和心得
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。
Tip: 引自 《Linux进程间通信——使用信号量》
信号量与已经介绍过的IPC机构(管道、FIFO以及消息列队)不同,它是一个计数器,用于为多个进程提供对共享数据对象的访问
为了获得共享资源,进程需要执行下列操作
当进程不再使用由一个信号量控制的共享资源时,该信号量值增1,如果有进程正在休眠等待此信号量,则唤醒它们
为了正确地实现信号量,信号量值的测试及减1操作应该是原子操作,为此信号量通常是在内核中实现的
常用的信号量形式被称为 二元信号量(binary semaphore) ,它控制单个资源,其初始值为1,但是一般而言,信号量的初值可以是任意一个正值,该值表明有多少个共享资源单位可供共享应用。
Tip: 引自 《UNIX环境高级编程》
顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。
Tip: 引自 《Linux进程间通信——使用共享内存》
共享存储允许两个或多个进程共享一个给定的存储区,因为数据不需要在客户进程和服务进程之间复制,所以这是最快的一种IPC,使用共享存储时要掌握的唯一窍门是,在多个进程之间同步访问一个给定的存储区时,若服务器进程正在将数据放入共享存储区,则它在做完这一操作之前,客户进程不应当去取这些数据,通常,信号量用于同步共享存储访问(也可以用记录锁或互斥量)
Tip: 引自 《UNIX环境高级编程》
系统层面有一些内核参数限制了信号量的大小
root@ubuntu:~# ipcs -ls
------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767
root@ubuntu:~# sysctl -a 2>/dev/null | grep sem
kernel.sem = 250 32000 32 128
root@ubuntu:~#
表示系统信号量集的最大个数为128个
一个信号量集中最大可以有250个信号量
系统中最多总共有32000个信号量
semop系统调用允许的信号量最大个数为32个
系统层面有一些内核参数限制了共享内存的大小
root@ubuntu:~# ipcs -lm
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1
root@ubuntu:~# sysctl -a 2>/dev/null | grep shm
kernel.shmmax = 33554432
kernel.shmall = 2097152
kernel.shmmni = 4096
vm.hugetlb_shm_group = 0
root@ubuntu:~#
Tip:
/etc/sysctl.conf
中可以进行内核配置
注意:
IPC_CREAT|0666
提示:
shmsemA.c
#include <stdio.h>
#include <sys/shm.h> //shmget,shmat,shmdt,shmctl 相关声明都在这里
#include <sys/sem.h> //sembuf,SEM_UNDO,SETALL 相关声明和宏定义都在这里
#include <string.h> //memset,strcmp 相关函数声明都在这里
#define SHMSIZE 1024
typedef struct sembuf SB; // 将sembuf重命名
union semun //定义semun共用体作为参数
{
int val; // value for SETVAL 设定一个值可以用
struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET 获取状态可以使用
unsigned short *array; // array for GETALL & SETALL 设定所有值或获取所有值可以使用
struct seminfo *__buf; // buffer for IPC_INFO 获取信息可以用
void *__pad; //万能指针
};
int main()
{
int res=-1,shmid=0,semid=0;
key_t key=IPC_PRIVATE;
char *shmaddr=NULL; //定义一个共享内存的指针
SB sem_p0={0,-1,SEM_UNDO}, //构建对第一个信号量进行P操作的参数
sem_v0={0,1,SEM_UNDO}, //构建对第一个信号量进行V操作的参数
sem_v1={1,1,SEM_UNDO}; //构建对第二个信号量进行V操作的参数
union semun sem_args;
unsigned short array[2]={0,0}; //构建数组
sem_args.array=array; //构建出给两个信号量一起赋值的共用体参数
if(-1 == (key=ftok("/",888))) //生成key
{
perror("ftok");
return res;
}
if (0 > (shmid=shmget(key,SHMSIZE,IPC_CREAT|0600))) //使用shmget 创建或获取共享内存ID , 大小为1024个字节 , or use getpagesize() instead
{
perror("shmget");
return res;
}
else printf("created shared memory :%d\n",shmid); //显示出获取的共享内存ID
if ((char *)0 > (shmaddr=shmat(shmid,0,0))) //通过ID获取地址,并且使用shmaddr进行指向
{
perror("shmat");
return res;
}
else printf("attached shared memory:%p\n",shmaddr); //将内存地址打印出来
if (0 > (semid=semget(key,2,IPC_CREAT|0600))) //通过key获取两个信号量的ID
{
perror("semget");
return res;
}
else printf("created a sem set with two sems which id is :%d\n",semid); //将信号量ID打印出来
if (0 > semctl(semid,0,SETALL,sem_args)) //将两个信号量一起赋值为0,设置值存于sem_args中
{
perror("semctl");
return res;
}
else printf("semset has been initialized\n");
if (0 > semop(semid,&sem_v0,1)) //对第一个信号量进行V操作
{
perror("semop");
return res;
}
memset(shmaddr,0,SHMSIZE); //将共享内存置0
do
{
if (0 > semop(semid,&sem_p0,1)) //对第一个信号量进行P操作
{
perror("semop");
return res;
}
puts("please enter the message to shm:\n('quit' to exit)");
if(NULL == (fgets(shmaddr,SHMSIZE,stdin))) //将输入内容写到共享内存中
{
perror("fgets");
return res;
}
if (0 > semop(semid,&sem_v1,1)) //对第二个信号量进行V操作
{
perror("semop");
return res;
}
}while(strcmp(shmaddr,"quit\n")); //如果输入为quit就退出
if (0 > (shmdt(shmaddr))) //分离共享内存
{
perror("shmdt");
return res;
}
else printf("Deattach shared-memory\n");
if (0 > semop(semid,&sem_p0,1)) //对第一个信号量进行P操作
{
perror("semop");
return res;
}
if (0 > shmctl(shmid, IPC_RMID, NULL)) //删除和释放共享内存
{
perror("shmctl(IPC_RMID)\n");
return res;
}
else printf("Delete shared-memory\n");
res=0;
return res;
}
本文系转载,前往查看
如有侵权,请联系?cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系?cloudcommunity@tencent.com 删除。