Linux进程间通信之信号量(semaphore)、消息队列(Message Queue)和共享内存(Share Memory)

System V 进程通信方式:信号量(semaphore)、消息队列(Message Queue)和共享内存(Share Memory)

 

信号量

信号量(semaphore)实际是一个整数,它的值由多个进程进行测试(test)和设置(set)。就每个进程所关心的测试和设置操作而言,这两个操作是不可中断的,或称“原子”操作,即一旦开始直到两个操作全部完成。测试和设置操作的结果是:信号量的当前值和设置值相加,其和或者是正或者为负。根据测试和设置操作的结果,一个进程可能必须睡眠,直到有另一个进程改变信号量的值。

信号量可用来实现所谓的“临界区”的互斥使用,临界区指同一时刻只能有一个进程执行其中代码的代码段。为了进一步理解信号量的使用,下面我们举例说明。

假设你有很多相互协作的进程,它们正在读或写一个数据文件中的记录。你可能希望严格协调对这个文件的存取,于是你使用初始值为1的信号量,在这个信号量上实施两个操作,首先测试并且给信号量的值减1,然后测试并给信号量的值加1。当第一个进程存取文件时,它把信号量的值减1,并获得成功,信号量的值现在变为0,这个进程可以继续执行并存取数据文件。但是,如果另外一个进程也希望存取这个文件,那么它也把信号量的值减1,结果是不能存取这个文件,因为信号量的值变为-1。这个进程将被挂起,直到第一个进程完成对数据文件的存取。当第一个进程完成对数据文件的存取,它将增加信号量的值,使它重新变为1,现在,等待的进程被唤醒,它对信号量的减1操作将获得成功。

详细介绍见 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html

 

消息队列

消息队列也称为报文队列,消息队列是随内核持续的,只有在内核重起或显示删除一个消息队列时,该消息队列才会真正删除 系统中记录消息队列的数据结构struct ipc_ids msg_ids位于内核中,系统中所有消息队列都可以在结构msg_ids中找到访问入口

消息队列其实就是一个消息的链表,每个消息队列有一个队列头,称为struct msg_queue,这个队列头描述了消息队列的key值,用户ID,组ID等信息,但它存于内核中而结构体struct msqid_ds能够返回或设置消息队列的信息,这个结构体位于用户空间中,与msg_queue结构相似消息队列允许一个或多个进程向它写入或读取消息,消息队列是消息的链表。

消息是按消息类型访问,进程必须指定消息类型来读取消息,同样,当向消息队列中写入消息时也必须给出消息的类型,如果读队列使用的消息类型为0,则读取队列中的第一条消息。

内核空间的结构体msg_queue描述了对应key值消息队列的情况,而对应于用户空间的msqid_ds这个结构体,因此,可以操作msgid_ds这个结构体来操作消息队列。

具体详见 http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2623323.html

 

共享内存

共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。共享内存往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

函数原型说明见:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2623323.html

程序实例:参见http://blog.csdn.net/yangzhongxuan/article/details/7925750

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>

#define SIZE 1024

int main()
{
    int shmid ;
    char *shmaddr ;
    struct shmid_ds buf ;
    int flag = 0 ;
    int pid ;

    //0(IPC_PRIVATE):会建立新共享内存对象,IPC_CREAT与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
    shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;   
    if ( shmid < 0 )
    {
        perror("get shm  ipc_id error") ;
        return -1 ;
    }

    pid = fork() ;
    if ( pid == 0 )
    {
        shmaddr = (char *)shmat( shmid, NULL, 0 ) ;      //直接指定为NULL让内核自己决定一个合适的地址位置
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }
        strcpy( shmaddr, "Hi, I am child process!\n") ;

        //与shmat函数相反,shmdt是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
        shmdt( shmaddr ) ;                               

        return  0;
    } else if ( pid > 0) {
        sleep(3 ) ;

        //完成对共享内存的控制,得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
        flag = shmctl( shmid, IPC_STAT, &buf) ;      
        if ( flag == -1 )
        {
            perror("shmctl shm error") ;
            return -1 ;
        }
        printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
        printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
        printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;

        shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }

        printf("%s", shmaddr) ;
        shmdt( shmaddr ) ;
        shmctl(shmid, IPC_RMID, NULL) ;   //IPC_RMID:删除这片共享内存

    }
    else
    {
        perror("fork error") ;
        shmctl(shmid, IPC_RMID, NULL) ;
    }

    return 0 ;
}


PS:

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值 。通常情况下,该id值通过ftok函数得到
ftok原型如下:

key_t ftok( char * fname, int id )

参数说明:

                fname就时您指定的文档名            id是子序号。

返回值:

                在一般的UNIX实现中,是将文档的索引节点号取出,前面加上子序号得到key_t的返回值

如指定文档的索引节点号为65538,换算成16进制为0x010002,而您指定的ID值为38,换算成16进制   为 0x26,则最后的key_t返回值为0x26010002。

(查询文档索引节点号的方法是: ls -i)

 

 

 

posted on 2012-11-04 15:15  as_  阅读(14976)  评论(0编辑  收藏  举报

导航