学习如何在子进程中运行一个与其父进程完全不同的另外一个程序,而不是仅仅运行一个相同程序。我们使用exec调用来完成。
难点:通过exec调。用的进需要知道访问哪个文件描述符。
前面的例子中,因为子进程本身有file_pipes 数据的一份副本,所以这并不成为问题。经过了exec调用后,情况就会不一样了,因为原先的进程已经被新的子进程替换了。为了解决这个问题,我们可以将文件描述符(实际上只是一个数字)作为参数传递给用exec启动的程序。
为了演示它是如何工作的,我们需要两个程序。第一个程序是数据的产生者,它负责创建管道和启动子进程,后者是数据消费者。
上面的代码是为了实现pipe3调用pipe4的代码;但是结果并没有出现预期的那样;
pipe3 程序的开始部分和前面的例子是一样的,用pipe调用创建一个管道,然后用fork调用创创建一个新进程。接下来,用sprintf把读取的管道数据的文件描述符存到一个缓存区中,该缓存区中的内容将构成pipe程序中的一个参数。
通过execl调用来启动pipe4程序,execl的参数如下所示。
(1)要启动的程序
(2)argv[0]: 程序名
(3)argv[1] : 包含我们想让被调用程序去读取的文件描述符
(4)(char*)0: 这个参数的作用是终止被调用程序的参数列表。
pipe4程序从参数字符中提取出文件描述符数字,然后读取该文件描述符来获取数据。
13.5.1 管道关闭后的读操作
以前:我们一般是采取的是让进程读取一些数据然后直接退出的方式,并假设Linux会把清理文件当作是在进程结束的时应该做的工作的一部分。
实际: 从标准输入的方法会很不同。通常不知道有多少数据,而采用循环方法: 读取收——处理数据—— 读取更多的文件的数据直到没有数据可读为止。
当没有数据可读的时候,read通常会阻塞。它将暂停进程来等待知道有数据到达为止。如果管道的另外一端已经被关闭(也就是说,没有进程打开这个管道并向它写数据了)这时候read调用就会阻塞。这就使读进程能够检测文件结束一样,对管道进程检测并作出相应的动作。
有数据就是读取,没有数据就会循环检查等动作,阻塞等待。
。。。 这与读取一个无效的文件描述符不同,read把无效的文案描述符看作一个错误并返回-1。
若是跨越fork调用使用管道,就会有父子两个进程向管道中写入数据。这个时候父子进程对管道的写文件描述符都关闭了,管道才会被认为是关闭了。对管道read调用才会失败。
13.5.2 把管道作为标准输入和标准输出####
下面是关于用管道来连接两个进程更加简洁的方法。我们把其他一个管道文件描述符设置为一个已知的值,一般是会标准输入0或标准输出1.在父进程中设置较为复杂,在子进程中设置比较简单。 头文件: unistd.h
int dup(in file_descriptor);
int dup2(int file_descriptor_one, int file_descriptor_two);
dup 调用的目的是打开一个新的文件描述符,与open有点类似。
不同之处是,dup调用创建的新文件描述符与作为它的参数的那个已有文件描述符指向了同一个文件(管道)。对于dup函数来说,新文件描述符总是取最小的可用值。对于dup2函数来说,它所创建的新文件描述符或者与参数file_descriptor_two相同,或者是第一个大于该参数的可用值。
可以使用通用的fcntl调用(cmmand参数设置为F_DUPFD)来达到与调用dup和dup2相同的效果。但是dup更加容易使用。
dup如何帮助我们在进程之间传递数据的呢?
因为标准输入的文件描述符总是0,而dup返回的新的文件描述符又总是0,而dup返回的新的文件描述符有总是小于可用的数字。因为,如果我们首先关闭文件描述符0然后调用dup,那么新的文件描述符就将是数字0.因为新的文件描述符是复制一个已有的文件描述符,所以,标准输入就会改为指向一个我们传递给dup函数的文件描述符所对应的文件或管道。我们创建了两个文件描述符,他们指向了一个文件或管道,而且其中之一是标准输入。