Tsort:拓扑排序

来自泡泡学习笔记
跳到导航 跳到搜索

tsort对给定的文件执行拓扑排序,如果没有给定输入文件或者输入文件为’-’,则对标准输入进行操作。

tsort [选项] [文件]

tsort将其输入作为由空格分隔的字符串对读取,表示部分顺序。输出是与给定部分顺序相对应的总顺序。


例如

tsort <<EOF
a b c
d
e f
b c d e
EOF


将产生以下输出

a
b
c
d
e
f


考虑一个更实际的例子。你有一个大型函数集,它们都在同一个文件中,但除了一个之外,它们都是静态声明的。目前那个(例如main)是文件中第一个定义的函数,它直接调用的函数紧随其后,然后是它调用的其他函数等。假设你决定利用原型,因此你必须在声明所有这些函数(这意味着从定义中复制大量信息)和重新排列函数之间做出选择,以便尽可能多地在使用时定义它们。自动化后者过程的一种方法是为每个函数获取它直接调用的函数列表。许多程序可以生成这样的列表。它们描述了调用图。考虑以下列表,其中给定的行表示左侧的函数调用右侧的函数。

main parse_options
main tail_file
main tail_forever
tail_file pretty_name
tail_file write_header
tail_file tail
tail_forever recheck
tail_forever pretty_name
tail_forever write_header
tail_forever dump_remainder
tail tail_lines
tail tail_bytes
tail_lines start_lines
tail_lines dump_remainder
tail_lines file_lines
tail_lines pipe_lines
tail_bytes xlseek
tail_bytes start_bytes
tail_bytes dump_remainder
tail_bytes pipe_bytes
file_lines dump_remainder
recheck pretty_name


然后你可以使用tsort生成满足你要求的函数顺序。

example$ tsort call-graph | tac
dump_remainder
start_lines
file_lines
pipe_lines
xlseek
start_bytes
pipe_bytes
tail_lines
tail_bytes
pretty_name
write_header
tail
recheck
parse_options
tail_file
tail_forever
main


tsort检测输入中的任何循环,并将遇到的第一个循环写入标准错误。

注意,对于给定的部分顺序,通常没有唯一的总顺序。在上面的调用图中,函数parse_options可以在列表中的任何地方放置,只要它位于main之前。

唯一的选项是–help和–version。


背景

tsort存在的原因是早期的Unix链接器会一次性且按顺序处理归档文件。当ld读取归档中的每个对象时,它会根据该对象是否定义了链接时未定义的任何符号来决定是否需要在程序中使用该对象。


这意味着归档中的依赖关系需要特别处理。例如,scanf可能调用read。这意味着在通过归档进行一次遍历时,scanf.o必须在read.o之前出现,否则调用scanf但不调用read的程序可能会以对read的意外未解析引用结束。


解决这个问题的方法是首先生成一个对象文件与其他对象文件之间的依赖关系集。这是通过名为lorder的shell脚本完成的。据我所知,GNU工具没有提供lorder的版本,但你仍然可以在BSD发行版中找到它。


然后,你运行tsort来处理lorder的输出,并使用生成的排序来确定将对象添加到归档中的顺序。


自大约1980年以来,整个过程已经过时,因为Unix归档现在包含一个符号表(传统上由ranlib构建,现在通常由ar本身构建),并且Unix链接器使用符号表有效地对归档文件进行多次遍历。


无论如何,这就是tsort的来源。为了解决链接器处理归档文件的方式的老问题,这个问题后来以不同的方式得到了解决。