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 中的文件?