<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=821&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo-zh / Gentoo 之 System V IPC+消息队列]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=821</link>
		<description><![CDATA[Gentoo 之 System V IPC+消息队列 最近发表的帖子。]]></description>
		<lastBuildDate>Fri, 01 Mar 2024 04:16:16 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[Gentoo 之 System V IPC+消息队列]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=937#p937</link>
			<description><![CDATA[<p>System V消息队列是传统的Linux消息队列机制，它使用一组系统调用来创建、发送和接收消息。它的特点是可以在不同进程之间共享消息队列，但是在使用时需要手动管理消息队列的创建和删除。</p><p>优点：</p><p>&#160; &#160; 可以实现异步通信：发送进程将消息放入消息队列后即可继续执行，不需要等待接收进程的响应，接收进程可以在合适的时候去读取消息。<br />&#160; &#160; 支持多对多通信：多个进程可以同时向同一个消息队列发送消息，多个进程也可以同时从同一个消息队列接收消息。<br />&#160; &#160; 可以实现进程解耦：发送进程和接收进程之间通过消息队列通信，不需要直接的共享内存或者使用管道等方式，从而实现了进程解耦。<br />&#160; &#160; 消息队列中的消息可以按照优先级进行处理，这样可以实现一些特殊的消息处理逻辑。</p><p>缺点：</p><p>&#160; &#160; 消息队列是基于内核的，因此涉及到用户态和内核态的切换，可能会引入一定的性能开销。<br />&#160; &#160; 消息队列的容量有限，当消息队列满了之后，发送进程将无法再发送消息，接收进程也无法再接收消息，可能会引发消息丢失的问题。<br />&#160; &#160; System V消息队列是Linux特有的IPC机制，因此在不同的操作系统上可能不具备可移植性。</p><p>多进程与多线程</p><p>使用有名管道实现双向通信时，由于读管道是阻塞读的，为了不让“读操作”阻塞“写操作”，使用了父子进程来多线操作，<br />1）父进程这条线：读管道1<br />2）子进程这条线：写管道2</p><p>&#160; &#160; 线程和进程都是并发运行的，但是线程和进程各自的使用的场合有所不同</p><p>&#160; &#160; &#160; &#160; 多线使用多线程更省计算机cpu和内存的开销</p><p>&#160; &#160; &#160; &#160; &#160; &#160; 创建出并发运行的线程目的-----------多线操作</p><p>&#160; &#160; 程序必须要去运行一个新程序时，此时必须涉及到多进程</p><p>&#160; &#160; &#160; &#160; 这里并发运行的主要目的并不是为了多线操作，而是为了单独的去执行新程序</p><p>&#160; &#160; &#160; &#160; 执行新程序时，我们只能使用多进程来操作,因为线程是不可能去执行一个新程序的</p><p>System V IPC</p><p>&#160; &#160; 无名管道和有名管道都是UNIX系统早期提供的比较原始的一种进程间通信方式</p><br /><br /><p>&#160; &#160; 后来Unix系统升级到第5版本时，又提供了三种新的IPC通信方式</p><p>&#160; &#160; &#160; &#160; 消息队列<br />&#160; &#160; &#160; &#160; 信号量<br />&#160; &#160; &#160; &#160; 共享内存</p><p>&#160; &#160; &#160; &#160; System V就是系统第5版本的意思</p><p>&#160; &#160; &#160; &#160; 后来的Linux也继承了unix的这三个通信方式</p><p>管道的机制</p><p>&#160; &#160; 管道的本质就是一段缓存<br />&#160; &#160; Linux OS内核是以文件的形式来管理管道</p><p>&#160; &#160; 我们都是使用文件描述符以文件的形式来操作:无名管道和有名管道</p><p>操作管道时:<br />除了pipe和mkfifo这两个函数外，其它的像read、write、open都是文件io函数</p><br /><p>System V IPC</p><p>&#160; &#160; System V IPC与管道有所不同:</p><p>&#160; &#160; 它完全使用了不同的实现机制，与文件没任何的关系</p><p>&#160; &#160; &#160; &#160; 也就是说内核不再以文件的形式来管理System V IPC</p><p>&#160; &#160; 对于System V IPC，OS内核提供了全新的API<br />&#160; &#160; System V IPC时，不存在亲缘进程一说,任何进程之间都可以使用System V IPC来通信</p><p>System V IPC标识符</p><p>&#160; &#160; &#160; &#160; 这个“标识符”就是文件描述符的替代者，但是它是专门给System V IPC使用的<br />&#160; &#160; &#160; &#160; 不能使用文件IO函数来操作“标识符”，只能使用System V IPC的特有API才能操作</p><p>如何得到ipc标识符</p><p>调用某API创建好某个“通信结构”以后，API就会返回一个唯一的“标识符”</p><p>&#160; &#160; 比如创建好了一个“消息队列”后，创建的API就会返回一个唯一标识消息队列的“标识符”</p><p>ipc标识符作用</p><p>如果创建的是消息队列的话：</p><p>&#160; &#160; 进程通过消息队列唯一的标识符，就能找到创建好的“消息队列”</p><p>&#160; &#160; 使用这个消息队列，进程就能读写数据，从而实现进程间通信</p><br /><br /><p>&#160; &#160; 可以读写数据就是实现了通信</p><p>&#160; &#160; &#160; &#160; 也就是标识符就是可是识别传建好的system V IPC</p><p>消息队列<br />本质</p><p>消息队列的本质：由内核创建的用于存放消息的链表</p><p>&#160; &#160; 由于是存放消息的，所以把这个链表称为了消息队列</p><p>如何存放消息</p><p>消息队列这个链表有很多的节点，链表上的每一个节点就是一个消息</p><p>&#160; &#160; 注意是一个双向链表<br />&#160; &#160; 每个消息由两部分组成<br />&#160; &#160; &#160; &#160; 1）消息编号：识别消息用<br />&#160; &#160; &#160; &#160; 2）消息正文：真正的信息内容</p><p>发送接收消息过程<br />1.发送消息</p><p>（a）进程先封装一个消息包</p><p>（b）调用相应的API发送消息</p><p>这个消息包其实就是如下类型的一个结构体变量:<br />----封包时将消息编号和消息正文写到结构体的成员中</p><br /><div class="codebox"><pre><code>struct msgbuf{
				long mtype;         /* 放消息编号，必须&gt; 0 */
				char mtext[msgsz];   /* 消息内容（消息正文） */
			};	</code></pre></div><p>b过程：<br />1.调用API时通过“消息队列的标识符”找到对应的消息队列<br />2.将消息包发送给消息队列，消息包会被作为一个链表节点插入链表</p><p>2.接收消息</p><p>调用API接收消息时，必须传递两个重要的信息</p><p>&#160; &#160; 消息队列标识符<br />&#160; &#160; 你要接收消息的编号</p><p>&#160; &#160; 有了这两个信息:</p><p>&#160; &#160; API就可以找到对应的消息队列，然后从消息队列中取出你所要编号的消息</p><p>&#160; &#160; &#160; &#160; 收到了别人所发送的信息,实现了通信</p><p>“消息队列”有点像信息公告牌:<br />--------发送信息的人把某编号的消息挂到公告牌上<br />--------接收消息的人自己到公告牌上去取对应编号的消息<br />如此，发送者和接受者之间就实现了通信</p><p>----使用消息队列实现网状交叉通信很容易</p><p>----消息队列作为媒介，一个往双向链表发数据，一个根据编号取出数据<br />消息队列使用步骤</p><p>&#160; &#160; 使用msgget函数</p><p>&#160; &#160; &#160; &#160; 消息队列不存在：创建新的消息队列</p><p>&#160; &#160; &#160; &#160; 消息队列存在：获取已存在的某个消息队列，并返回唯一标识消息队列的标识符（msqID）</p><p>&#160; &#160; &#160; &#160; &#160; &#160; 后续收发消息就是使用msqID这个标识符来实现的</p><p>&#160; &#160; 收发消息<br />&#160; &#160; &#160; &#160; 发送消息：使用msgsnd函数，利用消息队列标识符发送某编号的消息<br />&#160; &#160; &#160; &#160; 接收消息：使用msgrcv函数，利用消息队列标识符接收某编号的消息</p><p>&#160; &#160; 使用msgctl函数，利用消息队列标识符删除消息队列</p><p>&#160; &#160; 对于使用消息队列来通信的多个进程来说:<br />&#160; &#160; &#160; &#160; &#160; &#160; 只需要一个进程来创建消息队列就可以了<br />&#160; &#160; 对于其它要参与通信的进程来说:<br />&#160; &#160; &#160; &#160; &#160; &#160; 直接使用这个创建好的消息队列即可</p><br /><br /><p>&#160; &#160; 为了保证消息队列的创建，让每一个进程都包含创建消息队列的代码，谁先运行就由谁创建</p><p>&#160; &#160; 后运行的进程如果发现它想用的那个消息队列已经创建好了，就直接使用</p><p>&#160; &#160; 当众多进程共享操作同一个消息队列时，即可实现进程间的通信。</p><p>消息队列API</p><div class="codebox"><pre><code> #include &lt;sys/types.h&gt;
#include &lt;sys/ipc.h&gt;
#include &lt;sys/msg.h&gt;
//功能：利用key值创建、或者获取一个消息队列
/*
1.如果key没有对应任何消息队列，那就创建一个新的消息队列
2.如果key已经对应了某个消息队列，说明消息队列已经存在了，那就获取这个消息队列来使用
*/
//msgflg:指定创建时的原始权限，比如0664
int msgget(key_t key, int msgflg);</code></pre></div><p>key值--------用于为消息队列生成（计算出）唯一的消息队列ID</p><p>&#160; &#160; 第一种：指定为IPC_PRIVATE宏</p><p>&#160; &#160; &#160; &#160; 指定这个宏后，每次调用msgget时都会创建一个新的消息队列</p><p>&#160; &#160; 第二种：可以自己指定一个整形数，但是容易重复指定</p><p>&#160; &#160; 第三种：使用ftok函数来生成key</p><div class="codebox"><pre><code>#include &lt;sys/types.h&gt;
#include &lt;sys/ipc.h&gt;
/*
ftok通过指定路径名和一个整形数，就可以计算并返回一个唯一对应的key值，
---------只要路径名和整形数不变，所对应的key值就唯一不变的
*/
//ftok只会使用整形数（proj_id）的低8位，因此我们往往会指定为一个ASCII码值
key_t ftok(const char *pathname, int proj_id);</code></pre></div><br /><br /><p>&#160; &#160; 创建一个新的消息队列时，除了原始权限，还需要指定IPC_CREAT选项</p><p>&#160; &#160; msgid = msgget(key, 0664|IPC_CREAT);<br />&#160; &#160; &#160; 创建一个新的消息队列，此时就会用到msgflg参数</p><p>多个进程如何共享同一个消息队列</p><p>&#160; &#160; 创建进程</p><p>&#160; &#160; &#160; &#160; 创建者使用&quot;./file&quot;, &#039;a’生成一个key值<br />&#160; &#160; &#160; &#160; 然后调用msgget创建了一个消息队列</p><p>&#160; &#160; key = ftok(&quot;./file&quot;, &#039;a&#039;);<br />&#160; &#160; msgid = msgget(key, 0664|IPC_CREAT);</p><p>&#160; &#160; &#160; &#160; 当创建者得到msgid后，即可操作消息队列</p><p>&#160; &#160; 实现共享</p><p>只要能拿到别人创建好的消息队列的ID，即可共享操作同一个消息队列，实现进程间通信</p><p>&#160; &#160; 获取别人创建好的消息队列的ID，有两个方法：</p><p>&#160; &#160; a）创建者把ID保存到某文件，共享进程读出ID即可</p><p>&#160; &#160; &#160; &#160; 这种情况下，共享进程根本不需要调用msgget函数来返回ID</p><p>&#160; &#160; b）调用msgget获取已在消息队列的ID</p><p>&#160; &#160; &#160; &#160; &#160; &#160; 使用ftok函数，利用与创建者相同的“路径名”和8位整形数，生成相同的key值<br />&#160; &#160; &#160; &#160; &#160; &#160; 调用msgget函数，利用key找到别人创建好的消息队列，返回ID</p><p>&#160; &#160; &#160; &#160; key = ftok(&quot;./file&quot;, &#039;a&#039;);<br />&#160; &#160; &#160; &#160; msgid = msgget(key, 0664|IPC_CREAT);&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; </p><p>&#160; &#160; 这种方法是最常用的方法，因为ftok所用到的“路径名”和“8位的整形数”比较好记忆</p><p>代码演示<br />创建一个消息队列</p><div class="codebox"><pre class="vscroll"><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/ipc.h&gt;
#include &lt;sys/msg.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;
#define MSG_FIFE &quot;./msgfile&quot;
int  create_or_get_msgque(){
    //创建消息队列，返回msgid
     int msgId=-1;
     key_t key=-1;
     int fd=0;
     //为了用到文件路径名，用open创建
     fd=open(MSG_FIFE,O_RDWR|O_CREAT,0664);
     //用生成的路径+asc码生成唯一整数
     key=ftok(MSG_FIFE,&#039;a&#039;);
     msgId=msgget(key,0664|IPC_CREAT);
     return msgId;
}
int main(void){
    int msgID=-1;
    int ret=-1;
    msgID=create_or_get_msgque();
    /*父子进程收发消息*/
    ret=fork();
    if(ret&gt;0){//父进程发送消息

    }
    else if(ret==0){//子进程接收消息

    }
    return 0;
}
 </code></pre></div><p>转载:https://blog.csdn.net/weixin_47173597/article/details/127970145?spm=1001.2101.3001.6650.14&amp;utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-14-127970145-blog-9269561.235%5Ev43%5Epc_blog_bottom_relevance_base6&amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-14-127970145-blog-9269561.235%5Ev43%5Epc_blog_bottom_relevance_base6&amp;utm_relevant_index=24</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Fri, 01 Mar 2024 04:16:16 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=937#p937</guid>
		</item>
	</channel>
</rss>
