下面的内容是通过阅读 at-3.14 的来源得出的。对于任何版本,puts job id 的方式和运行到文件名的时间都应该相似,但我没有检查过。
首先将作业 id 和特定作业应该运行的时间编码到描述作业的文件名中。文件名有 format aJJJJJTTTTTTTT
,其中JJJJJ
5 个字符的十六进制字符串,作业 id,并且TTTTTTTT
是 8 个字符的十六进制字符串,作业应该运行的时间。时间存储为从纪元开始的秒数。
通过将作业描述文件作为标准输入提供给sh -c
. 幸运的是,Linux 内核提供了一个符号链接 ,/proc/self/fd/0
它将指向当前正在执行的进程的标准输入(ls -l /proc/self/fd/0
如果您需要确保自己确实如此,请尝试一下)。
作业运行时,描述作业的文件已被删除。但是,该文件仍然可用于内核,因为它dup(2)
在用作作业的标准输入之前已被复制。所以,实际上我们正在解析一个不再可见的文件名的符号链接。在最后的 perl 脚本中,我们需要考虑到这一点,因为readlink
将返回类似/foo/bar/baz (deleted)
而不是/foo/bar/baz
. 我们只对包含我们需要的所有信息的文件名感兴趣。
符号链接指向已删除文件的原因是 at daemon 在执行作业之前取消链接原始文件。=
只有在创建一个以代替开头的硬链接(硬链接)后才能取消链接a
。有了这个, at 守护进程试图确保只有一份作业正在运行:守护进程不会execle(2)
,即。如果失败,它会解救link(2)
。因为原始文件已经受制于open(2)
并且dup(2)
inode 仍然存在供内核使用,因为它仍然有指向它的硬链接。
经过相当长且可能令人困惑的介绍,这里是如何将它们放在一起:
#!/usr/bin/perl
use strict;
use warnings;
my $job_file = readlink("/proc/self/fd/0");
if (index($job_file, " ") > 0) {
$job_file = substr($job_file, 0, index($job_file, " ") - 1);
}
my $tmp = substr($job_file, rindex($job_file, "/") + 1);
$tmp =~ s/^a([0-9a-f]{5})[0-9a-f]+/$1/;
my $job_id = hex($tmp);
if ($job_id > 0) {
printf("My AT job id is %d.\n", $job_id);
}
# end of file.