共享内存和信号量

共享内存可以被多个进程操作,这是我们的目的,也可以被多个进程同时操作,这是我们不希望看到的,为了避免这种情况,我们使用共享内存时需要配合信号量同时使用。

共享内存的使用,主要有以下几个API: shmget()、shmat()、shmdt()及shmctl()。

1.      shmget()用来开辟/指向一块共享内存的函数,

应用说明:
shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。

函数原型:
    int shmget(key_t key, size_t size,int shmflg);

key_t key 是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。

int size 是这块内存的大小.
    int flag 是这块内存的模式(mode)以及权限标识。
    模式可取如下值:        
    IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
    IPC_EXCL   与IPC_CREAT结合使用,如果已创建则则返回错误
    然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。
    如:IPC_CREAT |IPC_EXCL | 0640   
    例子中的0640为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。
   这个函数成功时返回共享内存的ID,失败时返回-1。
   创建共享内存时,shmflg参数至少需要IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是k=0xffffffff,不能使用;
   获取已创建的共享内存时,shmflg不要用IPC_CREAT(只能用创建共享内存时的权限标识,如0640),否则在某些情况下,比如用ipcrm删除共享内存后,用该函数并用IPC_CREAT参数获取一次共享内存(当然,获取失败),则即使再次创建共享内存也不能成功,此时必须更改key来重建共享内存。

2.      shmat()将这个内存区映射到本进程的虚拟地址空间。

函数原型:
    void    *shmat( intshmid , char *shmaddr , int shmflag );

shmat()是用来允许本进程访问一块共享内存的函数。
    int shmid是那块共享内存的ID。
    char *shmaddr是共享内存的起始地址,如果shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置;如果shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。
    int shmflag是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式
    成功时,这个函数返回共享内存的起始地址。失败时返回-1。

3.       shmdt()函数删除本进程对这块内存的使用,shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。

函数原型:
    int shmdt( char *shmaddr );
    参数char *shmaddr是那块共享内存的起始地址。
    成功时返回0。失败时返回-1。

4.       shmctl() 控制对这块共享内存的使用

函数原型:
    int  shmctl( int shmid , int cmd, struct shmid_ds *buf );
    int shmid是共享内存的ID。
    int cmd是控制命令,可取值如下:
       IPC_STAT        得到共享内存的状态
       IPC_SET         改变共享内存的状态
       IPC_RMID        删除共享内存
    struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。
   返回值:   成功:0   失败:-1


Linux信号量工具:

int semctl(int sem_id, int sem_num, int command, ...);
int semget(key_tkey, int num_sems, int sem_flags);
int semop(intsem_id, struct sembuf *sem_ops, size_t num_sem_ops);

    注意,key的作用类似于一个文件名,因为他表示程序也许会使用或是合作所用的资源。相类似的,由semget所返回的并且为其他的共享内存函数所用的标识符与由fopen函数所返回的FILE *十分相似,因为他被进程用来访问共享文件。而且与文件类似,不同的进程会有不同的信号量标识符,尽管他们指向相同的信号量。key与标识符的用法对于在这里所讨论的所有IPC程序都是通用的,尽管每一个程序会使用独立的key与标识符。

semget :
    semget函数创建一个新的信号量或是获得一个已存在的信号量键值。
    int semget(key_t key, int num_sems, intsem_flags);
    第一个参数key是一个用来允许不相关的进程访问相同信号量的整数值。所有的信号量是为不同的程序通过提供一个key来间接访问的,对于每一个信号量系统生成一个信号量标识符。信号量键值只可以由semget获得,所有其他的信号量函数所用的信号量标识符都是由semget所返回的。
    还有一个特殊的信号量key值,IPC_PRIVATE(通常为0),其作用是创建一个只有创建进程可以访问的信号量。这通常并没有有用的目的,而幸运的是,因为在某些Linux系统上,手册页将IPC_PRIVATE并没有阻止其他的进程访问信号量作为一个bug列出。
    num_sems参数是所需要的信号量数目。这个值通常总是1。
    sem_flags参数是一个标记集合,与open函数的标记十分类似。低九位是信号的权限,其作用与文件权限类似。另外,这些标记可以与 IPC_CREAT进行或操作来创建新的信号量。设置IPC_CREAT标记并且指定一个已经存在的信号量键值并不是一个错误。如果不需要,IPC_CREAT标记只是被简单的忽略。我们可以使用IPC_CREAT与IPC_EXCL的组合来保证我们可以获得一个新的,唯一的信号量。如果这个信号量已经存在,则会返回一个错误。
    如果成功,semget函数会返回一个正数;这是用于其他信号量函数的标识符。如果失败,则会返回-1。

semop :
    函数semop用来改变信号量的值:
    int semop(int sem_id, struct sembuf*sem_ops, size_t num_sem_ops);
    第一个参数,sem_id,是由semget函数所返回的信号量标识符。第二个参数,sem_ops,是一个指向结构数组的指针,其中的每一个结构至少包含下列成员:
struct sembuf {
   short sem_num;
   short sem_op;
   short sem_flg;
}
    第一个成员,sem_num,是信号量数目,通常为0,除非我们正在使用一个信号量数组。sem_op成员是信号量的变化量值。(我们可以以任何量改变信号量值,而不只是1)通常情况下中使用两个值,-1是我们的P操作,用来等待一个信号量变得可用,而+1是我们的V操作,用来通知一个信号量可用。
    最后一个成员,sem_flg,通常设置为SEM_UNDO。这会使得操作系统跟踪当前进程对信号量所做的改变,而且如果进程终止而没有释放这个信号量,如果信号量为这个进程所占有,这个标记可以使得操作系统自动释放这个信号量。将sem_flg设置为SEM_UNDO是一个好习惯,除非我们需要不同的行为。如果我们确实变我们需要一个不同的值而不是SEM_UNDO,一致性是十分重要的,否则我们就会变得十分迷惑,当我们的进程退出时,内核是否会尝试清理我们的信号量。

semctl :
    semctl函数允许信号量信息的直接控制:
    int semctl(int sem_id, int sem_num,int command, ...);
    第一个参数,sem_id,是由semget所获得的信号量标识符。sem_num参数是信号量数目。当我们使用信号量数组时会用到这个参数。通常,如果这是第一个且是唯一的一个信号量,这个值为0。command参数是要执行的动作,而如果提供了额外的参数,则是union semun,根据X/OPEN规范,这个参数至少包括下列参数:
union semun {
   int val;
   struct semid_ds *buf;
   unsigned short *array;
}
    许多版本的Linux在头文件(通常为sem.h)中定义了semun联合,尽管X/Open确认说我们必须定义我们自己的联合。如果我们发现我们确实需 要定义我们自己的联合,我们可以查看semctl手册页了解定义。如果有这样的情况,建议使用手册页中提供的定义,尽管这个定义与上面的有区别。
    有多个不同的command值可以用于semctl。在这里我们描述两个会经常用到的值。要了解semctl功能的详细信息,我们应该查看手册页。
这两个通常的command值为:
SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
IPC_RMID:当信号量不再需要时用于删除一个信号量标识。
    semctl函数依据command参数会返回不同的值。对于SETVAL与IPC_RMID,如果成功则会返回0,否则会返回-1。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值