分享

进程间通信

 书永夜 2013-11-17
一.进程间通信——消息队列

System V IPC包括三种进程通信的方式:消息队列、信号量、共享内存,是一种较老的方式。

管道和FIFO是基于文件系统的,而System V IPC是基于系统内核的,用ipcs可以查看系统当前的IPC对象的状态。

消息队列是一个存放在内核中的消息链表,由消息队列标识符标识。它克服了信号传递信息量少、管道所传送的是无格式字节流以及缓冲区大小受到限制的缺点。由于消息队列存放在内核中,所以只有在内核重启(OS重启)的时候或者显式的删除一个消息队列时,它才能够真正的被删除。

消息队列与管道的区别:最主要的区别是管道通信是要求两个进程之间要有亲缘关系,
而消息队列当中,通信的两个进程之间可以是完全无关的进程。

操作消息队列时,用到的重要的数据结构:

(1)消息缓冲结构(定义在linux/msg.h中)

struct messagebuf{

    long messageType; //消息类型,正整数

    char messageText; //消息内容,可以为任意类型,由用户自定义

}消息的大小是受限制的,由<linux/msg.h>中的MSGMAX限制;

(2)msqid_ds 此结构体保存消息队列当前的状态信息;

(3)ipc_perm 保存消息队列的重要信息(包括键值、用户id、组id等)。



操作消息队列的一些函数:

(1)key_t ftok(const char *pathname,int proj_id); //获取键值(每个消息队列在系统范围内对应唯一的键值)proj_id 的取值范围是1-255。

(2)int msgget(key_t key,int msgflg);       //创建消息队列

key 调用ftok获得,msgflg 可能的取值为IPC_CREAT,IPC_EXCL

(3)int msgsnd(int msgid,struct msgbuf *msgbuf,size_t msgsize,int msgflag); //发送

msgid 消息队列标识符                msgbuf 指向要发送的消息

msgsize 消息的大小                  msgflag 操作标志位

(4)int msgrcv(int msgid,struct msgbuf *msgbuf,size_t msgsize,long int msgtype,int msgflag);//接收    接收消息时需要一个同类型的消息缓冲结构、要接收消息队列的标识符、要接收消息的类型

msgtype用来指定要接收的消息,分三种情况:

等于 0 返回消息队列中的第一个消息

大于0  返回消息队列中类型为msgtype的第一个消息

小于0 返回消息队列中类型值小于等于msgtype绝对值的消息中类型值最小的第一条消息

(5)获取和设置消息队列的属性

int msgctl(int msgid,int cmd,struct msqid_ds *buf);

cmd 有三种操作:IPC_STAT 获取消息队列当前的状态信息,保存到buf指向的空间

IPC_SET  设置消息队列的属性

IPC_RMID 从内核中删除msgid标识的消息队列

二.进程间通信——信号量
Linux信号量(semaphore)是一种互斥机制。即对某个互斥资源的访问会收到信号量的保护,在访问之前需要获得信号量。
在操作完共享资源后,需释放信号量,以便另外的进程来获得资源。获得和释放应该成对出现。
获得信号量集,需要注意的是,获得的是一个集合,而不是一个单一的信号量。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1:       int semget(key_t key,int nsems,int semflg);
key:系统根据这个值来获取信号量集。
nsems:此信号集包括几个信号量。
semflg:创建此信号量的属性。 (IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)
成功则返回该信号量集的ID。
注:
既指定IPC_CREAT又指定IPC_EXCL时,如果系统中该信号量集已经存在,则马上返回。
    如果需要获得存在的信号量,则将此参数置0.
2:      int semctl(int semid,int senum,int cmd....)
    semid:信号量ID。
    senum:对信号量集中的第几个信号量进行控制。(从0开始)
    cmd:需要进行的操作。(SETVAL是其中的一个)。
    根据cmd的不同可能存在第四个参数,cmd=SETVAL时,表示信号量同时可以被获得几次,如第四个参数
    num=1表示只能被获得一次,既被信号量保护的资源只能同时被一个程序使用。该系统调用,是在对信号量初始化时用的。    
    3:        int semop(int semid,struct sembuf *sem,int num_elements);
    struct sembuf {
        unsigned short sem_num;    //该信号量集中的第几个信号量。
        int sem_op;//需要获得还是释放信号量
        int sem_flg;//相关动作
    };
num_elements:需要对该信号量集中的多少个信号量进行处理。
获得信号量时,将sembuf结构提初始化为:
sem_num = 0; //该信号量集中的首个信号量
sem_op = -1; //获得信号量
sem_flag = IPC_NOWAIT;   //如果不能获得信号量,马上返回。
semop(semid,_sem,1);
同理释放信号量时,将sem_op设为1.

三.进程间通信——共享内存
使用共享内存的目的:
共享内存共享内存是进程间通信中最简单的方式之一。
共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。
当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
使用共享内存的流程:
1.进程必须首先分配它。
2.随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。
3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。
分配:
int segment_id = shmget (shm_key, int size , shmflag );
1.进程通过调用shmget(Shared Memory GET,获取共享内存)来分配一个共享内存块。 该函数的第一个参数是一个用来标识共享内存块的键值。
彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。
不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。
用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。
2.该函数的第二个参数指定了所申请的内存块的大小。
因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。
3.第三个参数是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:
IPC_CREAT:
这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:
这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。
也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相通键值的共享内存块,
shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
模式标志:
这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。其中表示执行权限的位将被忽略。
指明访问权限的一个简单办法是利用<sys/stat.h>中指定,并且在手册页第二节stat条目中说明了的常量指定。
例如,
S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,
S_IROTH和S_IWOTH则指定了其它用户的读写权限。
绑定和脱离:
pst= shmat(iShm_id, NULL, 0)
    一个进程获取对一块共享内存的访问,这个进程必须先调用 shmat(SHared Memory Attach,绑定到共享内存)。
    将 shmget 返回的共享内存标识符 SHMID 传递给这个函数作为第一个参数。
    第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL则Linux会自动选择一个合适的地址用于映射。
    第三个参数是一个标志位,包含了以下选项:   
    SHM_RND
    表示第二个参数指定的地址应被向下靠拢到内存页面大小的整数倍。
    如果您不指定这个标志,您将不得不在调用shmat的时候手工将共享内存块的大小按页面大小对齐。
    SHM_RDONLY
    表示这个内存块将仅允许读取操作而禁止写入。 如果这个函数调用成功则会返回绑定的共享内存块对应的地址。
    通过 fork 函数创建的子进程同时继承这些共享内存块;
    如果需要,它们可以主动脱离这些共享内存块。
    当一个进程不再使用一个共享内存块的时候应通过调用 shmdt(Shared Memory Detach,脱离共享内存块)
    函数与该共享内存块脱离。将由 shmat 函数返回的地址传递给这个函数。
    如果当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。
    对 exit 或任何exec族函数的调用都会自动使进程脱离共享内存块。
    控制和释放共享内存块:
    shmctl(iShm_id,IPC_RMID,0)<0
    调用 shmctl("Shared Memory Control",控制共享内存)函数会返回一个共享内存块的相关信息。同时 shmctl 允许程序修改这些信息。
    该函数的第一个参数是一个共享内存块标识。要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个 struct shmid_ds 对象的指针作为第三个参数。
    要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。
    应当在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块,
    但不会删除这个内存块。 要查看其它有关共享内存块的操作的描述,请参考shmctl函数的手册页。

    共享内存的总体大小是有限制的,这个大小通过SHMMAX参数来定义(以字节为单位),
    您可以通过执行以下命令来确定 SHMMAX 的值:
    cat /proc/sys/kernel/shmmax

    修改共享内存:
    设置 SHMMAX
# >echo "2147483648" > /proc/sys/kernel/shmmax
    您还可以使用 sysctl 命令来更改 SHMMAX 的值:
# sysctl -w kernel.shmmax=2147483648
    最后,通过将该内核参数插入到 /etc/sysctl.conf 启动文件中,您可以使这种更改永久有效:
# echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多