IPC - 如何将命令输出重定向到子进程中的共享内存段

Posted

技术标签:

【中文标题】IPC - 如何将命令输出重定向到子进程中的共享内存段【英文标题】:IPC - How to redirect a command output to a shared memory segment in child 【发布时间】:2014-03-19 04:20:53 【问题描述】:

我尝试将 Unix 命令输出重定向(写入)到子进程中的共享内存段, 然后让父进程从父进程中的同一共享内存段中读取输出。经过几次徒劳的尝试,我没有取得很大的成功。谁能告诉我一个方法? 提前致谢。

我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#define SHM_SIZE    1024

int main()

   key_t key; int  shmid; char* data;

   pid_t   cpid=fork();

   if (cpid<0) 
   
         fprintf(stderr,"Fork error!\n");
         exit (-1);
   
   else if (cpid==0)        // child process
   
    if ((key = ftok("mysh.c", 'R')) == -1)
         
            perror("ftok");
            exit(1);
        

    // Connect to shared memory 
    if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1)
    
       perror("shmget");
       exit(1);
    

    // Attach to the segment
    data = shmat(shmid, (void *) 0, 0);
    if (data == (char *) (-1))
    
       perror("shmat");
       exit(1);
    

        system("ls -l");
    // Stuck: How to redirect the output of "ls -l"
    // to a shared memmory segment "data", so that parent process
    // can retrieve it later?? Tried to 
    // do pipe and dup2 but none worked.

    // Attempt via read?, but only garbage
    read(STDIN_FILENO, data, SHM_SIZE);

   
   else 
    // parent process
    int st;

    wait(&st);
    printf("Output read from the child:\n");
        if ((write(STDOUT_FILENO, data, SHM_SIZE)) < 0 )
        
            perror("write 2");
            exit(1);
    
   

=======================

【问题讨论】:

为什么不使用管道让父进程读取子进程的输出? 我想学习/练习的练习是共享内存段。我想知道是否将它通过管道传输到文件描述符,然后再次将其从 fd 重定向到共享内存缓冲区,是否可以完成这项工作。这听起来多余,不确定。 如果使用管道,则不需要共享内存。这是一个更容易和正常的方法。 【参考方案1】:
    system("ls -l");
// Stuck: How to redirect the output of "ls -l"
// to a shared memmory segment "data", so that parent process
// can retrieve it later?? Tried to 
// do pipe and dup2 but none worked.

出于测试目的,我建议您阅读stdin,然后将它们写到data


这是一个使用 POSIX 共享内存的例子(POSIX IPC API 优于 SYSV IPC API),子进程从stdin 读取到共享内存区域,父进程将这个共享内存区域的内容写入stdout

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])

    const char *shm_name = "/dummy_cat_shm";
    int shm_fd;
    off_t shm_length;

    const char *read_sem_name = "/dummy_cat_read";
    const char *write_sem_name = "/dummy_cat_write";

    sem_t *read_sem, *write_sem;
    pid_t pid;
    int buf_length;
    char *write_ptr, *read_ptr;

    buf_length = 1024;
    shm_length = sizeof(buf_length) + buf_length;

    /* Create semaphore */
    read_sem = sem_open(read_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0);
    if (read_sem == SEM_FAILED) 
        perror("sem_open");
        goto clean_up3;
    
    write_sem = sem_open(write_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 1);
    if (write_sem == SEM_FAILED) 
        perror("sem_open");
        goto clean_up2;
    

    /* Create shared memory segment */
    shm_fd = shm_open(shm_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (shm_fd < 0) 
        perror("shm_open");
        goto clean_up1;
    

    if (ftruncate(shm_fd, shm_length) < 0) 
        perror("ftruncate");
        goto clean_up0;
    

    if ((pid = fork()) < 0) 
        perror("fork");
        goto clean_up0;
    
    else if (pid == 0) 
        write_ptr = mmap(NULL, shm_length, PROT_WRITE, MAP_SHARED, shm_fd, 0);
        if (write_ptr == MAP_FAILED) 
            perror("mmap");
            goto clean_up0;
        

        char *buf = write_ptr+sizeof(buf_length);

        while (sem_wait(write_sem) == 0) 
            if (fgets(buf, buf_length, stdin) != NULL) 
                *(int *)write_ptr = 1;
                sem_post(read_sem);
            
            else 
                *(int *)write_ptr = 0;
                sem_post(read_sem);
                break;
            
        

        munmap(write_ptr, shm_length);
    
    else 
        read_ptr = mmap(NULL, shm_length, PROT_READ, MAP_SHARED, shm_fd, 0);
        if (read_ptr == MAP_FAILED) 
            perror("mmap");
            goto clean_up0;
        

        char *buf = read_ptr + sizeof(buf_length);

        while (sem_wait(read_sem) == 0) 
            if (*(int *)read_ptr > 0) 
                printf("%s", buf);
                sem_post(write_sem);
            
            else 
                break;
            
        

        munmap(read_ptr, shm_length);
    

clean_up0:
    shm_unlink(shm_name);

clean_up1:
    sem_unlink(write_sem_name);

clean_up2:
    sem_unlink(read_sem_name);

clean_up3:
    exit(EXIT_FAILURE);

注意:在这种情况下,这两个mmap() 可以放在fork() 之前。

编译:

gcc shm_exp.c -pthread -lrt

跑步:

$ ls / | ./a.out 
bin/   home/        lib32/       mnt/   run/      sys/  vmlinuz@
boot/  initrd.img@  lib64/       opt/   sbin/     tmp/  vmlinuz.old@
dev/   initrd.img.old@  lost+found/  proc/  selinux/  usr@
etc/   lib/     media/       root/  srv/      var/

【讨论】:

【参考方案2】:

如何重定向 ls -l 的标准输出

我们必须更清楚地了解此代码中涉及的进程(父进程和子进程)。 您的程序在运行期间创建了多少个进程? 正确答案是 - 三个。 两个进程是父进程和显式分叉的子进程。 第三个是由 system("ls -l") 调用创建的。 这个函数隐式地派生了另一个执行(通过调用 exec 系列函数)“ls -l”sell 命令的进程。您需要重定向的是 system() 函数创建的子进程的输出。可悲的是,system() 并没有在参与者之间建立 IPC。如果您需要对输出进行操作,请不要使用 system()。

我同意@leeduhem,popen() 可能是最好的方法。 它的工作原理与 system() 完全相同,即分叉一个新进程并执行“ls -l”。 此外,它还在参与者之间建立了管道 IPC,因此很容易捕获子输出并随心所欲地处理它:

char buff[1024];
FILE *fd;

// instead of system("ls -l")
fd = popen("ls -l", "r");
// check for errors

while(fgets(buff, sizeof(buff), fd) != NULL)

  // write to the shared memory

 
pclose(fd); 

如果你不想使用 popen() 函数,你可以写一个类似的。 一般的做法是

    打开一个管道() fork() 一个新进程 使用 dup2 重定向标准输出 调用合适的exec()函数(可能是execl())执行“ls -l” 从 dup2 复制的描述符中读取。

【讨论】:

以上是关于IPC - 如何将命令输出重定向到子进程中的共享内存段的主要内容,如果未能解决你的问题,请参考以下文章

如何将 time 命令的输出重定向到 Linux 中的文件?

如何将 dos 命令的输出读取/重定向到 C/C++ 中的程序变量?

如何将父小部件焦点重定向到子小部件?

使用子进程的python脚本,将所有输出重定向到文件

C - 将标准输入/输出重定向到单个双向文件描述符

将命令提示符输出重定向到 Visual Studio 2012 中的输出窗口