Linux—— Posix IPC

来源:互联网 发布:日本战国身高知乎 编辑:程序博客网 时间:2024/04/20 03:27

最好的参考资料:
1.师从互联网。

2.UNP v2 Posix IPC的相关章节2、5、10、13。

3.Linux man 命令。

      先缅怀下 Stevens 大师。好那么开始~~~~  说点不打紧的,虽说Posix IPC 是标准的IPC,是以后趋势,但是,现在大多数应用程序仍然在使用 System V IPC机制。这里从APUE和《深入理解Linux内核架构》一点都没介绍,ULK也只是介绍了Posix 消息队列。

第一条:Posix IPC都使用 “Posix IPC 名字”进行标志。
 Posix.1是这么描述Posix IPC名字的:它可能是某个文件系统中的一个真正的路径名,也可能不是。mq_open、sem_open和shm_open这三个函数的第一个参数就是这样的一个名字。这是UNPv2 中2.2节的描述。不是很详细,书中很多已和现在的linux 不符~~

            sem_open原型:sem_t *sem_open(const char *name, int oflag,...);

           Linux下,sem_open都是创建在/dev/shm目录下的(Linux的/dev/shm是一个tmpfs类型的特殊fs,就像/proc, /sys一样,用mount命令就可以看到),而且会加上sem.这样的prefix,所以,如果name是“hehe”,这样创建出来的文件就是/dev/shm/sem.hehe了。

           参看glibc中nptl/sem_open.c源码,name参数若是以‘/’开头将被忽略掉                             

1./* Construct the filename.  */
2.  while (name[0] == '/')
3.    ++name;
         结论:

          1.sem_open中

             name参数可以是以大于等于0个‘/’开头(都被忽略了)的字符串,且在除了开头不能再含有‘/’字符了。(例如name参数是hehe/hehe,即便/dev/shm/hehe/这个路径存在,也不对,为什么不能,看了源码也没懂,希望懂的人回一下,:-));

          2.shm_open

             和sem_open中不同的就是name可以是个路径即 参数中除了开头可以含有‘/’,但前提是路径中的目录必须存在。

          3.mq_open

             要求很严格,只能以”/+字符串“这种作为参数,即必须以‘/’开头,且字符串不能在含有‘/’;

第二条:运行前提
信号量运行时要加 -lrt。
消息队列则要手动挂载 mqueue类型的文件系统,具体命令如下:(详细参看 man 7 mq_overview)
 mkdir /dev/mqueue mount -t

 mqueue none /dev/mqueue

 第三条:  运行结果
消息队列在/dev/mqueue目录下产生文件。

信号量和共享内存产生文件在/dev/shm 目录下。

 第四条:Posix 消息队列机制
Posix 消息队列的实现
可以参见《ULK》第十九章。基本的原理是:消息队列描述符mqd通过VFS的fget函数---->对应的/dev/mqueue/上的消息队列文件----->该文件的i节点号------>找到包含这个节点的mqueue_inode_info。每个消息队列对应一个mqueue_inode_info,他包含inode对象,而inode又与/dev/mqueue/特殊文件系统的一个文件相对应。而队列中的消息被放在mqueue_inode_info中的单向链表中。消息的为msg_msg,与System VIPC 消息描述符完全一样。

 #include <mqueue.h>
mqd_t mq_open (const char *name, int oflag, .../*mode_t mode ,struct mq_attr_t *attr */);//在/dev/mqueue 下创建名为name的文件

int mq_close (mqd_t mqdes);

int mq_unlink (const char *name);//name索引的文件的目录项链接stat.nlink减1;

struct mq_attr

{

  long int mq_flags; /* Message queue flags.  */

  long int mq_maxmsg; /* Maximum number of messages.  */

  long int mq_msgsize; /* Maximum message size of a message.  */

  long int mq_curmsgs; /* Number of messages currently queued.  */

  long int __pad[4];

};//做mq_open的第四个参数 自动忽略 flags和curmsgs的值;

int mq_getattr (mqd_t  mqdes, struct mq_attr * mqstat);

int mq_setattr (mqd_t __mqdes, const struct mq_attr * restrict  mqstat, struct mq_attr * restrict  omqstat);

 


/* Receive the oldest from highest priority   messages in message queue MQDES.  */

ssize_t mq_receive (mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio)  ;

 


/* Add message pointed by MSG_PTR to message queue MQDES.  */

int mq_send (mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio)  ;

以上这几个函数的原理是:进程甲通过mq_open在/dev/mqueue/下创建一个消息队列文件 ,之后进程甲、乙、丙、丁等通过mq_open打开他,再通过mq_send 和mq_receive以这个文件为媒介(消息队列) 收发信息。mq_getatt、mq_setatt获得和设置消息队列属性。

union sigval {          /* Data passed with notification */

               int     sival_int;         /* Integer value */

               void   *sival_ptr;         /* Pointer value */

           };

           struct sigevent {

               int          sigev_notify; /* Notification method */

               int          sigev_signo;  /* Notification signal */

               union sigval sigev_value;  /* Data passed with notification */                                         

               void       (*sigev_notify_function) (union sigval);/* Function for thread notification */                                                                                   

               void        *sigev_notify_attributes;  /* Thread function attributes */                                       

           };

/* Register notification issued upon message arrival to an empty message queue MQDES.  */

int mq_notify (mqd_t  mqdes,  const struct sigevent * notification);//Ubuntu 10.10测试这个函数在原来队列中存在消息后注册,新产生的数据,注册的进程无反映。只有当消息队列为空时注册,当有新消息产生,注册进程会有反映~~~

mq_notify函数当参数notification->sigev_notify==SIGEV_SIGNAL 时的原理是通过参数mqd,notification->sigev_signo以及本调用进程本身,将消息队列通过一个信号和进程联系起来,在队列为空时,放置一个消息向注册进程发送以信号,收到信号的进程递送这个信号(即调用信号处理函数处理之,:-) 。)。

mq_notify函数当参数notification->sigev_notify==SIGEV_THREAD时的原理是通过参数mqd,notification->sigev_notify_function和这个线程函数的参数notification->sigev_value以及新建的这个线程的属性notification->sigev_notify_attributes,将消息队列通过一个信号和一个线程联系起来,在队列为空时,放置一个消息时就调用这个线程处理函数处理消息。

无论是信号版还是线程版,一次消息接受后,都需要重新注册。

(乱入:int sigwait(const sigset_t *set,int *sig);//同步的等待一个异步事件:我们是在使用信号,但是却没有涉及异步信号处理程序!)

第五条:Posix 共享内存机制
 Psoix共享内存区的实现
是构筑在mmap系统调用函数之上的。  

#include <sys/mman.h>
int shm_open ( const char * name, int  oflag, mode_t  mode);//shm_open  的mode参数必须总是指定,可以设为0.

int shm_unlink ( const char * name);

#include <unistd.h>

#include <sys/types.h>

int ftruncate (int fd, off_t length) ;

//If the file previously was larger than this size,  the  extra  data  is  lost.   If  the  file  previously  was shorter, it is extended, and the extended part reads as null bytes ('/0'). (man ~~~)

#include <sys/types.h>

#include <sys/stat.h>

int fstat(int fd, struct stat *buf);      

Posix 共享内存的基本原理是:进程甲通过shm_open在/dev/shm/目录下创建一个以参数name为名子的文件,进程甲、乙、丙、丁都可以通过调用shm_open以相同的name参数打开在/dev/shm/下的这个文件,而且可选的是可以用ftruncate函数改变这个文件的大小。并通过fstat获得stat.st_size获得这个文件的整体大小用来做mmap的参数。用mmap函数把这个文件映射 的调用进程的内存空间。获得一个在调用进程自身内部的一个地址,进行读写操作。

第六条:Posix 信号量
Posix信号量的实现
                  Posix信号量常用于线程同步(无名信号量);system v信号量常用于进程同步。Posix信号量有两类:有名信号量和无名信号量(即基于内存的信号量)。一个进程内多个线程之间共享、使用基于内存(就是进程的内存空间^_^)的信号量。多个进程之间共享、使用处于共享内存区的有名信号量。UNPv2给出了Posix有名信号量基于FIFO、内存映射I/O+互斥锁+条件变量、System V信号量的三种实现方式。看了下nptl/sem_open.c ,linux上Posix信号量也是基于mmap实现的。而Posix无名信号量的实现简单说来就是在进程空间分配了一个sem_t 类型的内存空间调用sem_init初始化后使用。

                  信号量还可以分为二值信号量——把信号量初始化为1,计数信号量——通常初始化为某个代表资源数的N值。但实现信号量的代码中并无这种区分。

                   Stevens大师在UNPv2中列出了信号量、互斥锁、和条件变量之间的差异:

                    1.互斥锁总是由给它上锁的线程解锁,信号量的P操作却不必由执行过它的V操作的同一线程执行。

                    2.互斥锁要么被锁住,要么被解开。(二值状态,类似二值信号量)。

                    3.由于信号量中有一个状态值(N),所以信号量的P操作总是留下了他们的回忆(就是被记住了——N编程N+1了)。然而当向一                        个条件变量发送信号时,如果没有线程等在这个条件变量上,那么信号就丢失了。

#include <semaphore.h>
//基于内存的信号量使用的函数

int sem_init (sem_t * sem, int  pshared, unsigned int  value);

int sem_destroy (sem_t *__sem);

//有名信号量使用的函数

sem_t *sem_open (const char *name, int oflag, ...) ;

int sem_close (sem_t *sem);

int sem_unlink (const char *name);

//共用的函数

int sem_wait (sem_t * sem);

int sem_trywait (sem_t * sem);

int sem_post (sem_t *sem) ;//此函数为信号安全函数(UNPv2)

int sem_getvalue (sem_t *restrict sem, int *restrict  sval);

int sem_timedwait (sem_t *restrict sem,const struct timespec *restrict abstime);

Posix 信号量的基本原理是:P、V操作!

 

参看:

http://wangcong.org/blog/?p=591

http://www.cnblogs.com/super119/archive/2010/12/13/1904392.html

原创粉丝点击