<?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=856&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / Getnoo 之 process_vm_readv/writev syscalls]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=856</link>
		<description><![CDATA[Getnoo 之 process_vm_readv/writev syscalls 最近发表的帖子。]]></description>
		<lastBuildDate>Sat, 06 Apr 2024 13:32:12 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[Getnoo 之 process_vm_readv/writev syscalls]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=975#p975</link>
			<description><![CDATA[<p>多进程之间需要传输大量数据的时候，比如多进程 RPC 框架的进程之间通信，常用共享内存队列。</p><p>但是共享内存队列难免会有 入队+出队 2次 memcpy 。</p><p>而且要变长共享内存队列，如果支持多生产者进程+多消费者进程 ，就要处理线程安全方面的问题， 比较麻烦。</p><p>process_vm_readv() ,&#160; process_vm_writev() 是 Linux 3.2 新增的 syscall，用于在多个进程的地址空间之间，高效传输大块数据。</p><p><a href="https://www.man7.org/linux/man-pages/man2/process_vm_readv.2.html" rel="nofollow">https://www.man7.org/linux/man-pages/ma … adv.2.html</a></p><p><a href="https://github.com/open-mpi/ompi/blob/master/opal/mca/btl/sm/btl_sm_get.c#L96" rel="nofollow">https://github.com/open-mpi/ompi/blob/m … _get.c#L96</a></p><p>在此， 我提个设想，可以用&#160; process_vm_readv 实现一个多进程内存队列，相比之下，优势是：<br />&gt;在处理 多线程/多进程 并发时，更简单<br />&gt;省掉一次 memcpy。</p><p>函数声明</p><div class="codebox"><pre><code>#include &lt;sys/uio.h&gt;
ssize_t process_vm_readv(pid_t pid,
                         const struct iovec *local_iov,
                         unsigned long liovcnt,
                         const struct iovec *remote_iov,
                         unsigned long riovcnt,
                         unsigned long flags);
ssize_t process_vm_writev(pid_t pid,
                          const struct iovec *local_iov,
                          unsigned long liovcnt,
                          const struct iovec *remote_iov,
                          unsigned long riovcnt,
                          unsigned long flags);</code></pre></div><p>参数说明<br />&gt;pid&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; 进程pid号<br />&gt;struct iovec *local_iov&#160; &#160; &#160; &#160; 结构体local进程指向一个数组基地址<br />&gt;liovcnt&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; local进程数组大小<br />&gt;struct iovec *remote_iov&#160; &#160; 结构体remote进程指向一个数组基地址<br />&gt;riovcnt&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; remote进程数组大小<br />&gt;flags&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; 默认0</p><p>介绍</p><p>这些系统调用在不同进程地址空间之间传输数据。调用进程：“local进程”以及“remote进程”。数据直接在两个进程的地址空间传输，无需通过内核空间。前提是必须知道传输数据的大小。</p><p>process_vm_readv()从remote进程传送数据到local进程。要传输的数据由remote_iov和riovcnt标识：remote_iov指向一个数组，用于描述remote进程的地址范围，而riovcnt指定remote_iov中的元素数。数据传输到由local_iov和liovcnt指定的位置：local_iov是指向描述地址范围的数组的指针。并且liovcnt指定local_iov中的元素数。</p><p>process_vm_writev()系统调用是process_vm_readv()的逆过程。它从local进程传送数据到remote进程。除了转移的方向，参数liovcnt，local_iov，riovcnt和remote_iov具有相同的参数含义，与process_vm_readv()相同。</p><p>local_iov和remote_iov参数指向iovec结构的数组，在&lt;sys / uio.h&gt;中定义为：</p><div class="codebox"><pre><code>&lt;sys/uio.h&gt;
   struct iovec {
               void  *iov_base;    /* 地址基址 */
               size_t iov_len;     /* 数据传输字节数 */
           };</code></pre></div><p>缓冲区以数组顺序处理。 这意味着process_vm_readv()在进行到local_iov [1]之前会完全填充local_iov [0]，依此类推。 同样，在进行remote_iov [1]之前，将完全读取remote_iov[0]，依此类推。</p><p>同样，process_vm_writev()在local_iov [1]之前写出local_iov [0]的全部内容，并在remote_iov [1]之前完全填充remote_iov [0]。</p><div class="codebox"><pre><code>remote_iov[i].iov_len</code></pre></div><p>和</p><div class="codebox"><pre><code>local_iov[i].iov_len</code></pre></div><p>的长度不必相同。 因此，可以将单个本地缓冲区拆分为多个远程缓冲区，反之亦然。</p><p>flags参数当前未使用，必须设置为0。<br />返回值</p><p>成功后，process_vm_readv()返回读取的字节数，process_vm_writev()返回写入的字节数。 如果发生部分读/写，则此返回值可能小于请求的字节总数。 调用方应检查返回值以确定是否发生了部分读/写。</p><p>错误时，返回-1并正确设置errno。</p><p>示例</p><p>以下代码示例演示了process_vm_readv()的用法，它从具有PID的进程中读取地址上的19个字节，并将前10个字节写入buf1，并将后10个字节写入buf2。</p><div class="codebox"><pre class="vscroll"><code>#include &lt;sys/uio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
#include &lt;iostream&gt;

using namespace std;

int main(void) {
    struct iovec local[2];
    struct iovec remote[1];
    char buf1[10];
    char buf2[10];
    char remote_addr[]={&quot;abc1234567890defABC&quot;};
    long data_len = strlen(remote_addr);

    ssize_t nread;
    pid_t pid = getpid();             //PID of remote process

//读remotedata_len个字节，buf1 ：10 ； buf2 :10
    local[0].iov_base = buf1;
    local[0].iov_len = 10;
    local[1].iov_base = buf2;
    local[1].iov_len = 10;
    remote[0].iov_base = remote_addr;
    remote[0].iov_len = data_len;


    nread = process_vm_readv(pid, local, 2, remote, 1, 0);
    cout&lt;&lt;&quot;cout nread:&quot;&lt;&lt;nread&lt;&lt;endl;
    fprintf(stderr,&quot;read in CreateProcess %s, Process ID %d \n&quot;,strerror(errno),pid);

    printf(&quot;buf1: %s\n&quot;,buf1);
    printf(&quot;buf2: %s\n&quot;,buf2);

}</code></pre></div><p>相关的系统调用还有readv,writev,preadv,pwritev,preadv2,pwrite2</p><div class="codebox"><pre><code>#include &lt;sys/uio.h&gt;

       ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

       ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

       ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,
                      off_t offset);

       ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,
                       off_t offset);

       ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt,
                       off_t offset, int flags);

       ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt,
                        off_t offset, int flags);</code></pre></div><p>示例如下：</p><div class="codebox"><pre><code>int main(){
    char *str0 = &quot;hello &quot;;
    char *str1 = &quot;world\n&quot;;
    struct iovec iov[2];
    ssize_t nwritten;

    iov[0].iov_base = str0;
    iov[0].iov_len = strlen(str0);
    iov[1].iov_base = str1;
    iov[1].iov_len = strlen(str1);

    nwritten = writev(STDOUT_FILENO, iov, 2);

    printf(&quot;nwritten: %d\n&quot;,nwritten);
}</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Sat, 06 Apr 2024 13:32:12 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=975#p975</guid>
		</item>
	</channel>
</rss>
