Chinaunix首页 | 论坛 | 博客
  • 博客访问: 306854
  • 博文数量: 79
  • 博客积分: 1480
  • 博客等级: 上尉
  • 技术积分: 848
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-11 15:29
文章分类

全部博文(79)

文章存档

2012年(1)

2011年(5)

2010年(19)

2009年(54)

我的朋友

分类: C/C++

2010-08-05 10:59:40

1、XSI IPC
   message queues
   semaphores
   share memory

(1)它们在内核都与一个非负整数对应,这个数通常很大;当IPC结构体移除时,这个数会增加到最大值并最终变成0.
(2)这个整数是内部IDENTIFIERS,而进程之间必须用外部IDENTIFI ERS,这就是KEY;而KEY转化成内部IDENTIFIERS由KERNEL完成
(3)SERVER和client的KEY的创建:
     IPV_PRIVATE KEY:server使用 IPC_PRIVATE创建,并存储起来(比如一个文件)让client去获取;也可用于父子进程(作为一个参数传递给EXEC)
    在一个共同的HEADER文件定义KEY:必须防止KEY已经关联一个IPC结构体
    使用pathname和PID,用ftok函数转变成一个KEY:

#include <sys/ipc.h>

key_t ftok(const char *pathname, int id);
         key if OK, (key_t)-1 on error

(pathname必须对应1个文件,只有id的低8位用来生成key;)
注1:不同的pathname形同的PID可能产生相同的KEY
注2:想关联到一个已经存在的queue(一般是由client),key必须与queue刚创建的key相等,并且必须设定为IPC_CREAT;
(4)Permission Structure

struct ipc_perm {
    uid_t uid; // owner's effective user id

    gid_t gid; // owner's effective group id

    uid_t cuid; // creator's effective user id

    gid_t cgid; // creator's effective group id

    mode_t mode; // access mode

    .
    .
    .
}

可以使用msgctl, semctl或shmctl修改上述值
 Permission  Bit
   
 user-read
user-write(alter)
0400
0200 
 group-read
group-write(alter)
0040
0020 
 other-read
other-write(alter)
0004
0002 
(5)Advantages and Disadvantages
    所有的XSI IPC结构体都是systemwide并且没有reference计数;
    它们不是以名字显示在文件系统里,所以无法用read write 之类的文件操作函数;
    它们不使用文件描述符,所以不能使用multiplexed I/O 函数比如 select poll;
    它们可以使用1个函数msgsnd调用完成一个进程的传递1个消息到message queue,而其他形式的IPC一般需要open,write,和close;
    它们是reliable, flow controlled, record oriented,可以被processed in(除了FIFO顺序)
 IPC type Connectionless?  Reliable?  Flow Control?  Records?  Message types or priority? 
           
 message queue no  yes  yes  yes  yes 
STREAMS  no  yes  yes  yes  yes 
UNIX domain stream socket  no  yes  yes  no  no 
UNIX domain datagram socket  yes  yes  no   yes  no 
FIFOs(non-STREAMS)  no yes  yes  no  no 
注1:Connectionless意味着发送信息不用调用open之类的函数
注2:Flow Control意味着如果资源(buffer)短缺或receiver无法接受更多信息,sender会进入sleep;当flow control条件达到,sender自动awaken.

2、MESSAGE QUEUE
(1)message queue是1个内核中的message链表,有1个message queue identifier(queue ID)
(2)msgget:创建或者打开1个已经存在的queue
   msgsnd:添加新message加到queue的末尾
(注:包括message的长整数,非负长度,实际数据)
   msgrcv:从queue上获取messages,按下面的type field获取

struct msquid_ds {
    struct ipc_perm msg_perm; //权限


    msgqnum_t msg_qnum; // # of messages on queue


    msglen_t msg_qbytes; // max # of bytes on queue

    pid_t msg_lspid; // pid of last msgsnd()

    pid_t msg_lrpid; // pid of last msgrcv()

    time_t msg_stime; // last-msgsnd() time

    time_t msg_rtime; // last-msgrcv() time

    time_t msg_ctime; // last change time

    .
    .
    .
}

(3)system limits(linux)
   message最大byte数: 8192
   queue最大byte数:     16384
   最大queue数:         16
   最大message数:       16384*16
(4)msgget(打开一个已经存在的queue或者创建一个新queue)

#include <sys/msg.h>
int msgget(key_t key, int flag);
       Returns: message queue ID if OK, -1 on error

创建一个新queue,struct msqid_ds的初始化
    .ipc_perm相应
    .msg_qnum,msg_lspid, msg_lrpid, msg_stime和msg_rtime全部设置为0
    .msgctime设置为当前时间
    .msg_qbytes设置为system limit
(5)msgctl(在queue上执行各种操作,同semaphores的semctl以及shared memory的shmctl)

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
           Returns: 0 if OK, -1 on error

 cmd(这三个命令也用于semaphores和shared memory):
    .IPC_STAT:获取struct msqid_ds并把指针赋给buf;
    .IPC_SET:拷贝buf指向的相关数据到queue关联的struct msqid_ds,包括下面数据,msg_perm.uid, msg_perm.gid, msg_pern.mode, msg_perm_qbytes(只有euid与msg_perm.cuid或msg_perm.uid相等 或者 具有superuser权限的进程才能执行命令,只有superuser可以增加msg_qbytes)
    .IPC_RMID:移除message queue以及相关数据(权限同IPC_SET)这个移除是立即性的,任何使用该queue的进程会返回1个EIDRM的错误
(6)msgsnd(把数据放到1个message queue)

#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
             Returns: 0 if OK, -1 on error

 新加的message一般都放在queue的最后面
ptr:包含message类型(正整数)和message数据,例如

struct mymesg{
    long mtype; // positive message type
    char mtext[512]; // message data, of length nbytes

};

flag:可以设置为IPC_NOWAIT,跟I/O flag的nonblock类似;如果message queue满了,立即返回错误EAGAIN;
     如果没设置IPC_NOWAIT,必须等到有空闲空间,或者系统移除这个queue(返回EIDRM“identifier removed”),或者获取到一个signal并且signal handler返回(返回EINTR);
注:msgsnd成功后,与message queue关联的struct msqid_ds会更新调用者相关的msg_lspid,msg_stime,msg_qnum。
(7)msgrcv(从queue上获取mssage)

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
      Returns: size of data portion of message if OK, -1 on error

ptr:同msgsnd
nbytes:data buffer的大小(如果message大于data buffer,flag会被设置成MSG_NOERROR, message会被truncated,如果message太大并且flag的值未设置,会返回E2BIG的错误)
type: ==0 返回queue上的第一个message
      > 0 返回第一个type相等的message
      < 0 返回第一个type值最低的但得小于等于type的绝对值
flag: IPC_NOWAIT,没有立即返回-1并把error设置为ENOMSG,
      否则一直等到有,或者系统移除queue(返回EIDRM),或者
      捕获到signal并且signal handler返回(返回EINTR)
注:msgrcv成功后,与message queue关联的struct msqid_ds会更新调用者相关的msg_lrpid,msg_rtime,msg_qnum。

3、SEMAPHORES
(1)sempahores是一个计数器,用于提供多进程之间共享数据的访问
   获取一个共享资源步骤:
     .测试semaphore从而控制资源
     .如果semaphore是正值,那么进程可以使用该资源,并把semaphore值-1
     .如果semaphore值为0,进程进入sleep直到semaphore值大于0;当进程wake up,返回第一步

注:当进程完成对共享资源使用,semaphore值+1
(2)system limits(linux)
semaphore最大值                  32767
semaphore的adjust_on_exit最大值  32767
semaphore sets最大数量           128
semaphore最大数量                32000
每个semaphore set最大semaphore数 250
undo struct最大数量              32000
每个undo struct最大entry数量      32
每个semop调用的最大操作数          32
(3)semget(获取一个semaphore ID)

#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);
           Returns: semaphore ID if OK,-1 on error

将KEY转换为一个identifier,初始化struct semid_ds

struct semid_ds{
    struct ipc_perm sem_perm;
    unsigned short sem_nsems; // # of semaphores in set
    time_t sem_otime; // last-semop() time
    time_t sem_ctime; // last-change time
    .
    .
    .
}

(sem_otime设置为0
sem_ctime设置为当前时间
sem_nsems设置为nsems,如果是reference已存在的set,则为0)
(4)semctl(各种semaphore操作的集合)

#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ... /*union semun arg */);
          Returns: see following

注:第四个参数是可选的,根据cmd扩展:

union semun {
    int val; //for SETVAL
    struct semid_ds *buf; // for IPC_STAT和IPC_SET
    unsigned short *array//for GETALL和SETALL 

};

cmd:(0
    .IPC_STAT:为set获取struct semid_ds并存储在arg.buf中
    .IPC_SET:设置sem_perm.uid sem_perm.gid和sem_perm.mode但进程必须有权限
    .IPC_RMID:从系统中移除semaphore set,这个移除是立即性的,接下来跟这个set相关的操作会返回EIDRM,移除必须有相应权限
注:每一个semaphore都会被一个匿名的struct表示

struct {
   unsigned short semval; // semaphore value, always >= 0
   pid_t sempid; // pid for last operation
   unsigned short semncnt; // #processes awaiting semval>curval
   unsigned short semzcnt
// #processes awaiting semval == 0

   .

   .

   .

};

   .GETVAL:返回semval值(for the member semnum)
   .SETVAL:设置semval值(同上),值由arg.val指定
   .GETPID:返回sempid值(同上)
   .GETNCNT:返回semncnt值(同上)
   .GETZCNT:返回semzcnt值(同上)
   .GETALL:返回set里所有semaphore值,存储在arg.arry指针中
   .SETALL:设置所有semaphore值,由arg.array指向
注:除了GETALL,其他get命令返回相应值,其他命令返回0
(5)semop(atomically performs an array of operations on a semaphore set)

#include <sys/sem.h>
int semop(int semid, struct sembuf semoparray[], size_t nops);
       Returns: 0 if OK, -1 on error

sembuf结构体

struct sembuf {
    unsigned short sem_num; // member # in set (0, 1,... , nsems -1)
    short sem_op; // operation (negative, 0, or positive )
    short sem_flg; // IPC_NOWAIT, SEM_UNDO
};

nops:设置array操作号
sem_op:
      .positive: semaphore值会加上这个值,如果flag为undo,则减去这个值
      .negative: 如果semaphore值>=|sem_op|,那么semaphore值减去|sem_op|,若flag为undo,则增加相应值
                 如果semaphore值<|sem_op|:
  •                    如果设置了IPC_NOWAIT,则返回EAGAIN错误
  •                    如果没有IPC_NOWAIT,semaphore的semncnt值增加,调用者进入sleep直到下面三个有1个发生
                                     【1】semaphore值 >= |sem_op|,semncnt值减小
                                     【2】系统移除semaphore,函数返回EIDRM
                                     【3】进程捕获signal且signal handler返回,semncnt值减小,函数返回EINTR
           .0:       调用进程想等待semaphore值变成0
                 如果sempahore值为0,立即返回
                 如果semaphore值不是0
                    
  •                    如果设置了IPC_NOWAIT,则返回EAGAIN错误
  •                    如果没有IPC_NOWAIT,semaphore的semzcnt值增加,调用者进入sleep直到下面三个有1个发生
                                     【1】semaphore值 >= |sem_op|,semncnt值减小
                                     【2】系统移除semaphore,函数返回EIDRM
                                     【3】进程捕获signal且signal handler返回,semzcnt值减小,函数返回EINTR
4、SHARED MEMORY
(1)最快形式的IPC,一般semaphore用于同步共享内存访问,内核都有一个结构体与每个shared memeory segment对应

struct shmid_ds {
    struct ipc_perm shm_perm;
    size_t shm_segsz; // size of segment in bytes

    pid_t shm_lpid; // pid of last shmop()

    pid_t shm_cpid; // pid of creator

    shmatt_t shm_nattch;// number of current attaches
 
    time_t shm_atime; // last-attach time

    time_t shm_dtime; // last-detach time

    time_t shm_ctime; // last-change time

    .
    .
    .
};

shmatt_t 大小一般等同于 unsigned short
(2)system limit(linux)
1个shared memory segment最大值: 33,554,332bytes
1个shared memory segment最小值: 1byte
system wide,shared memory segment最大数量:  4096
每个进程 shared memory segment最大数量:      4096
(3)shmget(获取1个shared memory identifier)

#include <sys/shm.h>
    int shmget(key_t key, size_t size, int flag);
         Returns: shared memory ID if OK, -1 on error

新segment初始化:
    .ipc_perm初始化到相应的权限
    .shm_lpid, shm_nattach, shm_atime, shm_dtime为0
    .shm_ctime为当前时间
    .shm_segsz为size大小
size:shared memory segment大小(单位byte),一般是page size的倍数,否者,最后剩余的空间无法利用;如果reference已存在的segment,size为0;如果是新segment,则指定大小,并初始化内容为0
(4)shmctl(各种shared memory操作集合)

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
          Returns: 0 if OK, -1 on error

cmd:
   .IPC_STAT:获取shmid_ds结构体,并存储在buf中
   .IPC_SET:根据buf内容,设置shm_perm.uid, shm_perm.gid, shm_perm.mode,必须要有相应权限
   .IPC_RMID:移除shared memory segment,直到最后一个进程使用完segment,这个segment才会被移除,但是segment的identifier立即移除,shmat()无法attach segment;移除必须有相应权限。
   .SHM_LOCK:锁住shared memory segment中的内存区域,只能由superuser发起
   .SHM_UNLOCK:对应上面的操作,也只能是superuser
(5)shmatt(进程用这个函数跟segment的地址空间attach)

#include <sys/shm.h>

void *shmat(int shmid, const void *addr, int flag);
     Returns: pointer to shared memroy segment if OK, -1 on error

addr:
   .如果addr==0,segment attach到内核所选的第一个可用地址,这是推荐用法
   .如果addr!=0,flag!=SHM_RND,segment attach到addr指定的地址
   .如果addr!=0,flag==SHM_RND,segment attach到(addr-(addr modulus SHMLBA));SHM_RND代表"round",SHMLBA代表"low boundary address multiple",总是2的次方;算法是round地址到下一个SHMLBA的倍数
flag:
    SHM_RDONLY,segment只读,否则是可读可写的
注:若shmat成功,shm_nattch值增加
(6)shmdt(与shmat对应,detach segment)

#include <sys/shm.h>
int shmdt(void *addr);
     Returns: 0 if OK, -1 on error

注:并没有移除identifier和相关数据结构,直到一些进程调用shmctl使用命令IPC_RMID。成功后,shm_nattch值减小
阅读(799) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~