15-练习
64.10 练习
64-1. 运行程序清单64-3中的程序,当用户键入文件结尾符(通常是Ctrl-D)时,script程序的父子进程按照什么顺序退出?为什么?
64-2. 对程序清单64-3(script.c)中的程序做如下修改。
a)标准的script(1)程序会在输出文件的开始和结尾加上用来显示程序启动和结束时间的行。请加上这个功能。
b)如64.7节所述,增加能够处理终端窗口大小改变的代码。你会发现程序清单62-5(demo_SIGWINCH.c)的程序很适合来测试这个功能。
64-3. 修改程序清单64-3(script.c)中的程序,将select()替换为一对进程。其中一个处理从终端到伪终端主设备的数据传输,另一个处理相反方向上的数据传输。
64-4. 修改程序清单64-3(script.c)中的程序,为其增加一个记录时间戳的功能。每次该程序向typescript文件写入字符串时,它还应该写一个时间戳字符串到第二个文件中(比方说typescript.timed)。写入到第二个文件中的字符串应满足如下形式。
timestamp应该以文本形式记录下从script程序启动以来经历过的毫秒数。将时间戳以文本形式记录的好处是其结果容易阅读。在string中,真正的换行符需要进行转义。一种可能的方式是将一个换行符记录为2个字符的序列——\n,反斜线记为\。
再写一个程序script_replay.c,该程序读取时间戳文件并在标准输出上显示其内容,要求显示的进度同当初写入时的进度相同。将这两个程序结合起来就提供了一个简单的记录并回放shell会话的日志功能。
64-5. 实现客户端与服务器程序,提供简单的类似telnet风格的远程登录功能。服务器端要设计成能处理并发连接(见60.1节)。图64-3展示了为每个客户端建立登录服务的步骤。图中没有显示的是服务器端父进程,该进程处理从客户端发送来的套接字连接,并创建服务器端子进程来处理每个连接。注意,所有用来认证用户以及启动登录 shell 的工作都可以在每个服务器端子进程中通过调用ptyFork()进而在孙子进程中执行login(1)程序来完成。
64-6. 为上面的练习程序增加代码,使其能够在登录会话开始和结束时更新登录账户文件(见第40章)。
64-7. 假设我们执行了一个长时间运行的程序,该程序缓慢地产生输出,并将输出重定向到一个文件或管道上,比如:
上面的例子有个问题就是,默认情况下 stdio 只会在标准输入缓冲被填满后才会刷新到标准输出。这就意味着上面的 longrunner 程序的输出将以突发方式显示,且输出之间有较长的时间间隔。规避该问题的一种方法是写一个程序按照如下的步骤处理。
a)创建一个伪终端。
b)将标准文件描述符连接到伪终端从设备上,执行命令行参数中指定的程序。
c)从伪终端主设备端读取输出,并立刻写入到标准输出上(STDOUT_FILENO,文件描述符1)。同时,从终端读取输入并写入到伪终端主设备上,这样被执行的程序就能读取输入了。
这样的程序我们可以称之为unbuffer,可以像这样使用:
实现unbuffer程序。(这个程序的代码大部分都和程序清单64-3中的相似。)
64-8. 编写一个程序实现一种脚本语言,它可以在非交互式模式下驱动vi。由于vi需要运行在终端上,因此该程序要用到伪终端。