linux高级编程基础系列:System V进程间通信(信号量、共享内存)

linux 继承了System V的3种进程间通信的机制,分别是消息队列、信号量和共享内存。这3种机制,用于同一主机间两个进程信息的传递或同步。本文涉及第二和第三种种通信机制,即信号量和共享内存。必须明确一点,信号量和信号是不同的概念的东西,不能混淆。


信号量IPC原理:
信号量通信机制主要用来实现进程间的同步。信号量值用来标识系统可用资源的个数。在实际应用中,两个进程通信可能会使用多个信号量,因此,Linux在管理时以信号量集合的概念来管理。
通常说创建一个信号量指的是创建了一个信号量集合,在这个信号量集合中,可能有多个信号量。整个信号量集合由以下部分组成:
1】信号量集合数据结构:在此数据结构中定义了整个信号量集合的基本属性,如访问权限,指针,最近修改的时间和队列中信号量队列信息。结构体用struct  semid_ds表示,其原型为:
#include    <linux/sem.h>
struct    semid_ds
{
struct  ipc_perm    sem_perm;//权限
__kernel_time_t    sem_otime;//最近semop()信号量操作时间
__kernel_time_t    sem_ctime;//最近修改时间
struct  sem    *sem_base;//队列第一个信号量
struct  sem_queue   *sem_pending;//阻塞信号量
struct   sem_queue  **sem_pending_last;//最后一个阻塞信号量
struct   sem_undo     *undo;//undo队列
unsigned  short    sem_nsems;
};
2】每个信号量结构。在一个信号量集合中可能有多个信号量,每个信号量的数据结构中成员变量主要为该信号量的当前值。
#include   <linux/sem.h>
struct   sem
{
int   semval;//信号量的值
int   sempid;//最近一个操作的进程号PID
};


信号量管理操作
创建信号量集合:semget()
1、头文件:#include   <sys/sem.h>
2、函数原型:extern  int  semget(key_t  __key,int  __nsems,int  __semflg);
3、参数说明:
1】第一个参数为key_t类型的参数key,由ftok()函数产生
2】第二个参数为创建的信号量的个数,各信号量用数组方式存储,这个数组用于初始化数组的对象。
3】第三个参数用来标识信号量集合的权限,如0770为文件的访问权限类型,此外还可以附加参数值,和基本权限以或的方式一起使用。(与msgget()函数相似)
a、IPC_CREAT:如果key不存在,则创建,存在,则返回ID值。00001000
b、IPC_EXCL:如果key存在,返回失败。00002000
c、IPC_NOWAIT:如果需要等待,直接返回错误。00004000

控制信号量集合&信号量:semctl()
1、头文件:#include   <sys/sem.h>
2、函数原型:extern  int  semctl(int  __semid,int   __semnum,int   __cmd,...);
3、参数说明:该函数最多有四个参数,有可能只有三个参数。
1】第一个参数为要操作的信号集合标识符,该值一般由semget()函数返回。
2】第二个参数为集合中信号量的编号。如果标识某个信号量,此值为该信号量的下标(从0到n-1);如果标识整个信号量的集合,则设置为0。
3】第三个参数为要执行的操作。
a、如果要对整个信号量集合进行操作,这些操作在/usr/include/linux/ipc.h文件中定义。其操作包括IPC_RMID、IPC_SET、IPC_STAT、IPC_INFO。
#define    IPC_RMID   0     // 删除
#define    IPC_SET   1    // 设置ipc_perm的参数,此时第四个参数为struct  semid_ds结构体
#define    IPC_STAT    2   // 获取ipc_perm的参数 此时第四个参数为struct  semid_ds结构体
#define    IPC_INFO    3   // 获取系统信息 此时第四个参数为struct  seminfo结构体
b、如果要对信号量集合中的某个或某些信号量操作,操作在 /usr/include/linux/sem.h中定义
#define   GETPID   11   // 获取信号量拥有者的pid值 。若使用该参数,则第二个参数为0。第四个参数无效。如果执行成功,semctl()将返回该进程的pid值,否则返回-1。
#define   GETVAL  12  // 获取信号量的值 ,函数返回信号的值。如果执行此操作,第二个参数为信号量编号。如果执行成功,semctl()将返回当前信号量的值,否则返回-1。
#define   GETALL   13  // 获取所有信号量的值 第二个参数为0,第四个参数为存储所有信号量内存空间首地址,为一个数组的地址。如果执行成功,semctl()返回0。否则返回-1。
#define   GETNCNT  14   // 获取等待信号量的值递增的进程数 。如果执行此操作,第二个参数为0,如果执行成功,semctl()将返回等待信号量值的递增进程数,否则返回-1。
#define   GETZCNT   15   // 获取等待信号量的值递减的进程数 。如果执行此操作,第二个参数为0,如果执行成功,semctl()将返回等待信号量值的递减进程数,否则返回-1。
#define   SETVAL   16   // 设置信号量的值,设置的值在第四个参数中 。如果执行此操作,第二个参数为信号量编号,第四个参数为预设置的值。如果执行成功,semctl()返回0。否则返回-1。
#define   SETALL    17   // 设置所有信号量的值 。如果使用此操作,第二个参数为0,第四个参数为欲设置的信号量值所在数组的首地址。如果执行成功,semctl()返回0,否则返回-1。
4】第四个参数:第四个参数是个联合体,根据第三个参数的不同,第四个参数的类型不同。
union   semun
{
int   val;
struct   semid_ds   *buf;
unsigned   short   *array;
struct   seminfo   *__buf;
void   *__pad;
};
a、如果操作为SETVAL,第四个参数为val,是相应信号量的值。
b、如果操作是IPC_STAT & IPC_SEC,则第四个参数为struct  semid_ds的结构体变量(见上文)。
c、如果操作为GETALL & SETALL,第四个参数为数组地址。
d、如果操作为IPC_INFO,则第四个参数为struct  seminfo变量。

信号量操作:semop()
1、函数声明:extern  int  semop(int  __semid,struct  sembuf  *__sops,size_t   __nsops);
2、参数说明:
1】第一个参数为要操作的信号量集合标示符,该值一般由semget()函数返回。
2】第二个参数为struct  sembuf结构的变量,在头文件#include   <linux/sem.h>中
struct   sembuf
{
unsigned  short    sem_num;//信号量下标,表示操作的信号量编号
short    sem_op;//信号量操作,为作用于信号量的操作:该值如果是 正整数表示增加信号量的值(如果为1,表示在原来基础上加1,如果是3,表示在原来基础上加3),如果为 负数表示减少信号量的值,如果为 0表示对信号量的当前值进行是否为0的测试。
short    sem_flg;//操作标识。如果为 IPC_NOWAIT,对信号量集合的操作不能执行的情况下,调用立即返回,对某信号量操作,即使一个操作失败,也不会导致修改集合中的其他信号量。如果为 SEM_UNDO,当进程退出后,该进程对sem进行的操作将被撤销。
};
共享内存的IPC原理:
共享内存进程间通信主要用于实现进程间大量的数据传输。 共享内存空中单独开辟的一段内存空间,这段空间有自己特有的数据结构,包括访问权限,大小和最近访问的时间等。该数据空 间在内存据结构用结构体shmid_ds表示,在头文件 #include   <bit/shm.h>中,其原型如下:
struct   shmid_ds
{
struct  ipc_perm   shm_perm;//操作权限
int   shm_segsz;//段长度大小
__kernel_time_t     shm_atime;//最近attach时间
__kernel_time_t     shm_dtime;//最近detach时间
__kernel_time_t  shm_ctime;//最近change时间
__kernel_ipc_pid_t     shm_cpid;//创建者pid
__kernel_ipc_pid_t     shm_lpid;//最近操作pid
unsigned    short    shm_nattch;//
unsigned    short    shm_unused;//
void   *shm_unused2;//
void   *shm_unused3;//
};
使用共享内存有些限制如下:
1】两个进程使用共享内存之前,需要在进程和进程共享空间之间建立联系,即将共享内存空间挂载到进程中。在进程中使用一个执政指向该共享内存空间。此时,被挂载的内存空间就可以被该进程访问了。
2】在使用共享内存进行数据存取时,一般都是配合二元信号量使用。一般情况下,对于一段共享内存的写操作不允许几个进程同时进行。
3】由于共享内存需要占用大量的内存空间,系统对共享内存做了如下限制:
头文件:#include    <linux/shm.h>
#define   SHMMAX      0x2000000// 最大共享段大小(字节)
#define   SHMMIN        1//最小共享段大小(字节)
#define   SHMMNI 4096//
#define   SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16))//
#define   SHMSEG SHMMNI//
4】共享内存比管道在文件传输效率上优势明显。管道需要4次(服务器到server临时缓冲区,从server临时缓冲区到pipe或FIFO,从pipe或FIFO到client临时缓冲区,再从临时缓冲区将信息写到输出文件)。共享内存只需要2次(server将数据写到共享内存,client取共享内存数据)

共享内存管理
1、创建共享内存:shmget()
1】头文件:#include   <sys/shm.h>
2】函数原型:extern  int   shmget(key_t  __key,size_t   __size,int   __shmflg);
3】参数说明:
第一个参数为key_t类型的key值,一般由ftok()函数产生
第二个参数size为欲创建的共享内存段的大小(单位为字节)
第三个参数shmflg用来标识共享内存段的创建标识,包括:
在头文件#include   <linux/ipc.h>中
#define    IPC_CREAT     01000   //如果不存在,就创建
#define    IPC_EXCL     02000   //如果存在就返回失败
#define    IPC_NOWAIT 04000   //不等待直接返回
另外,在头文件#include   <linux/shm.h>中,还定义了两个选项,这两个选项与上面的选项用" | "(或)连接使用
#define   SHM_R 0400// 可读(也可以用S_IRUGO(#include  <linux/stat.h>中))
#define   SHM_W 0200//可写(也可以用S_IWUGO(#include  <linux/stat.h>中))
2、共享内存的控制(读取状态,设置状态,删除操作):shmctl()
1】头文件:#include   <sys/shm.h>
2】函数原型:extern  int   shmctl(int  __shmid,int   __cmd,struct  shmid_ds   *__buf)
3】参数说明:
第一个参数为要操作的共享内存标识符,由shmget()函数返回。
第二个参数为要执行的操作,这些操作定义在头文件#include   <linux/ipc.h>中。其操作包含IPC_RMID(删除)、IPC_SET(设置ipc_perm参数)、IPC_STAT(获取ipc_perm参数)、IPC_INFO(如ipcs)四项。具体含义同msgctl()类似,查看消息队列那部分。这里不再重复这四个参数。
如果是超级用户,还可以执行以下两个命令:
#define SHM_LOCK 11 //锁定共享内存段
#define   SHM_UNLOCK 12 //解锁共享内存段
第三个参数为struct   shmid_ds结构体变量,根据第二个参数的不同而改变。
3、映射共享内存:shmat()
在进程使用一段共享内存前,需要将该共享内存与当前进程建立联系,即将该进程映射(挂接)到当前进程。系统调用shmat()函数来实现将一个共享内存段映射到调用内存的数据段。函数声明如下:
1】函数声明:extern   void   *shmat(int  __shmid,__const   void  *__shmaddr,int   __shmflg);
2】返回:如果执行成功,返回共享内存段首地址
3】参数说明:
第一个参数shmid为要操作的共享内存标识符,由shmget()函数返回。
第二个参数shmaddr为指定共享内存的映射地址。如果该值为非零,则将用此值作为映射内存的地址。如果此值为0,则由系统来选择映射的地址。一般都将此值设置为0。
第三个参数用来指定共享内存段的访问权限和映射条件。如果设置为0,表示有读写权限。
头文件:#include   <linux/shm.h>
#define    SHM_RDONLY 010000 //只读
#define    SHM_RND 020000
#define    SHM_REMAP 040000
#define    SHM_EXEC 0100000
4、分离共享内存对象:shmdt()
1】函数原型:extern   int   shmdt(__const   void   *__shmaddr);
2】参数说明:与当前进程分离的共享内存标识ID
3】注意:
a、使用fork()创建子进程后,该进程继承父进程挂载的共享内存。
b、如果调用execX()执行一个新的程序,则所有挂载的共享内存将被自动卸载。
c、如果某个进程调用了exit(),那么所有挂载的共享内存将与当前进程脱离关系。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值