IPC - How to redirect a command output to a shared memory segment in child

StackOverflow https://stackoverflow.com/questions/22496130

  •  17-06-2023
  •  | 
  •  

문제

I tried to redirect (write) a Unix command output to a shared memory segment in the child, and then have the parent read the output back out from the same shared memory segment in the parent process. I don't have a lot of success after few futile attempts. Can anyone show me a way? thanks in advance.

My code:

#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);
    }
   }
}

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

도움이 되었습니까?

해결책 2

How to redirect stdout of the ls -l

We must shed more light on the processes (parent and children) involved into this code. How many processes your program creates during its run? The correct answer is - three. Two processes are the parent and the explicitly forked child. The third one is created by the system("ls -l") call. This function implicitly forks another process that executes (by calling an exec family function) the "ls -l" sell command. What you need to redirect is the output of the child process created by the system() function. It is sad, but the system() does not establish IPC between the participators. If you need to manipulate with the output, do not use system().

I agree with @leeduhem, popen() could be the best approach. It works exactly as the system(), i.e. forks a new process and executes "ls -l". In addition, it also establishes a pipe IPC between the participators, so it is easy to catch the child output and to do with it whatever you want:

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); 

If you do not want to use the popen() function, you may write a similar one. The general approach is

  1. open a pipe()
  2. fork() a new process
  3. redirect stdout using dup2
  4. call a suitable exec() function (probably execl()) executing "ls -l"
  5. read from the descriptor you are duplicating by dup2.

다른 팁

    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.

For test purpose, I suggest you read from stdin, then write them to data.


Here is an example using POSIX shared memory (POSIX IPC API is better than SYSV IPC API), which child read from stdin to a shared memory region, and parent write the content of this shared memory region to 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);
}

Note: these two mmap() could be put before fork() in this case.

Compiling:

gcc shm_exp.c -pthread -lrt

Running:

$ 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/
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top