杰瑞是对的,如果您担心的只是 Ctrl-C,您可以一次忽略 SIGINT 一段时间。如果你想证明一般的进程死亡,你需要某种最小的日志。为了交换两个元素:
1) 在文件末尾或单独的文件中向控制结构中添加一条记录,指示要交换文件的哪两个元素,A 和 B。
2)将A复制到暂存空间,记录你已经这样做了,刷新。
3)将B复制到A上,然后在您这样做的暂存空间中记录,刷新
4) 从 B 上的暂存空间复制。
5) 删除记录。
对于所有实际目的,这是 O(1) 额外空间,因此在大多数定义下仍然算作就地。理论上,如果 n 可以任意大,则记录索引是 O(log n):实际上它是一个非常小的 log n,并且合理的硬件/运行时间将其限制在 64 以上。
在所有情况下,当我说“刷新”时,我的意思是提交“足够远”的更改。有时,您的基本刷新操作仅刷新进程内的缓冲区,但它实际上并不同步物理介质,因为它不会一直刷新缓冲区到操作系统/设备驱动程序/硬件级别。当您担心的只是进程死亡时,这就足够了,但是如果您担心突然的媒体卸载,那么您必须冲过驱动程序。如果您担心电源故障,则必须同步硬件,但事实并非如此。使用 UPS,或者如果您认为停电非常罕见,您不介意丢失数据,那很好。
启动时,检查暂存空间中是否有任何“正在进行的交换”记录。如果您找到了,请计算出您走了多远并从那里完成交换以使数据恢复到正常状态。然后重新开始你的排序。
显然,这里存在性能问题,因为您的记录写入量是以前的两倍,并且刷新/同步可能非常昂贵。在实践中,您的就地排序可能有一些复合的移动内容操作,涉及许多交换,但您可以对其进行优化以避免每个元素都触及暂存空间。您只需要确保在覆盖任何数据之前,您在某处有一份安全的副本,并记录了该副本的去向,以便让您的文件恢复到包含每个元素的一个副本的状态。
Jerry 也说对了,对于大多数实际目的而言,真正的就地排序太难且太慢了。如果您可以将原始文件大小的一些线性部分作为暂存空间,那么使用合并排序您将获得更好的时间。
根据您的说明,即使使用就地排序,您也不需要任何刷新操作。您需要内存中以相同方式工作的暂存空间,并且您的 SIGINT 处理程序可以访问以便在退出之前获得数据安全,而不是在异常退出后在启动时恢复,并且您需要在信号中访问该内存 -安全方式(从技术上讲,这意味着使用 asig_atomic_t
来标记已进行的更改)。即便如此,与真正的就地排序相比,使用归并排序可能会更好。