13.5 父进程和子进程
这节将介绍如何在子进程中运行一个与父进程完全不同的另外一个程序,而不是仅仅运行一个相同程序.使用exec调用来完成这一项工作.这里的一个难点是,通过exec调用的进程需要知道应该访问哪个文件描述符.在前面的例子中,因为子进程本身有file_pipes数据的一份副本 (点击打开"fork复制进程映像"链接),所以这并不成为问题.但经过exec调用后,情况就不一样了,因为原来的进程已经被新的进程替换了.为解决这个问题,可以将文件描述符(实际上它只是一个数字)作为一个参数传递给用exec启动的程序.为了演示它是如何工作的,需要使用两个程序. 第一个程序是数据生产者,它负责创建管道和启用子进程,而后者是数据消费者.
编写程序pipe3,c,它是从pipe2.c修改而来, 实现在两个完全不同的进程之间传递数据
/*************************************************************************
> File Name: pipe3.c
> Description: pipe3.c程序在两个完全不同的进程之间传递数据
> Author: Liubingbing
> Created Time: 2015年07月11日 星期六 22时28分14秒
> Other: pipe3.c程序为了实现<strong>在两个完全不同的进程之间传递数据</strong>,它先创建一个管道,接着使用fork函数在父进程中创建一个子进程,然后在子进程中调用execl函数使用新进程替换子进程.
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int data_processed;
int file_pipes[2];
const char some_data[] = "123";
char buffer[BUFSIZ + 1];
pid_t fork_result;
memset(buffer, '\0', sizeof(buffer));
if (pipe(file_pipes) == 0) {
fork_result = fork();
if (fork_result == (pid_t) - 1) {
fprintf(stderr, "Fork failure");
exit(EXIT_FAILURE);
}
if (fork_result == 0) {
/* sprintf函数将格式化的file_pipe[0]数据写入到buffer指向的内存 */
sprintf(buffer, "%d", file_pipes[0]);
/* execl函数把当前进程替换为一个新进程,新进程由path或file参数指定. 此时,将父进程fork调用之后创建的子进程替换为指定的新进程pipe4
* 第一个参数"pipe4"为要启动的程序
* 第二个参数"pipe4"为程序名
* 第三个参数buffer包含让被调用程序(即pipe4)去读取的文件描述符(即file_pipe[0])
* 第四个参数(char *)0的作用是终止被调用程序的参数列表 */
(void)execl("pipe4", "pipe4", buffer, (char *)0);
exit(EXIT_FAILURE);
} else {
/* write函数从some_data指向的内存中写入stlen(some_data)个字节的数据到file_pipes[1]指向的文件中 */
data_processed = write(file_pipes[1], some_data, strlen(some_data));
printf("%d - wrote %d bytes\n", getpid(), data_processed);
}
}
exit(EXIT_SUCCESS);
}
接下来编写程序pipe4.c,它将被pipe3.c调用.
/*************************************************************************
> File Name: pipe4.c
> Description: pipe4.c程序从参数字符串中提取处文件描述符数字,然后读取文件描述符来获取数据
> Author: Liubingbing
> Created Time: 2015年07月11日 星期六 22时34分05秒
> Other: pipe4.c
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int data_processed;
char buffer[BUFSIZ + 1];
int file_descriptor;
memset(buffer, '\0', sizeof(buffer));
/* sscanf函数从argv[1]中读取数据,按照"%d"的格式写入到file_descriptor里 */
sscanf(argv[1], "%d", &file_descriptor);
/* read函数从file_descriptor指向的文件中读取BUFSIZ个字节的数据到buffer指向的内存中 */
data_processed = read(file_descriptor, buffer, BUFSIZ);
printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer);
exit(EXIT_SUCCESS);
}
运行pipe3时,将看到如下所示的输出结果:
pipe程序与之前的一样,用pipe函数创建一个管道,然后用fork调用创建一个新进程.接下来,它用sprintf把读取管道数据的文件描述符保存到一个缓存区中,该缓存区的内存将构成pipe4程序的一个参数.
int execlp(const char *file, const char *arg0, ..., (char *)0);
通过execl调用(在这里
点击打开"替换进程映像"的链接查看execl函数)来启动pipe4程序,
execl的参数如下所示:要调用的程序
argv[0]:程序名
argv[1]:包含想让被调用程序去读取的文件描述符
(char *)0:这个参数的作用是终止被调用程序的参数列表
pipe4程序从参数字符中提取出文件描述符数字,然后读取该文件描述符来获取数据.