cmp
else
else cmp(fd1, fd2, start+1, stop);
else return cmp(fd1, fd2, start+1, stop);
另外,在
main
,
start/stop
只设置在
小孩
进程,因此它们在父进程中无效。此外,即使它们在父级中有效,它们也只能设置为
最后的
孩子分叉了。
此外,也不能保证
wait
将返回PIDS
整齐
. (例如,它可能会返回
pid[3]
之前
pid[2]
. 因此,如果不应该的话,您可以尝试终止进程。
waitpid
相反。或者只是做:
int retval = EXIT_SUCCESS;
while (1) {
term = wait(&status);
if (term < 0)
break;
if (WIFSIGNALED(status)) {
printf(...);
retval = EXIT_FAILURE;
}
}
exit(retval);
而且,
去除
这个
kill
因为它不是真正需要的,而且更容易引起问题(也就是说,它是一个bug的来源)。哎呀!我刚注意到你正在发送一个
杀死
信号值为零。这在很大程度上是不允许的。
更新:
通过在同一个文件上启动程序并打印比较的字符,程序将打印相同的字符,但最后两个字符按顺序颠倒,因此比较buf1!=buf2为真,cmp返回1。这可能是种族状况吗?
是的,没错。因为每个子级使用相同的文件描述符,
lseek
一个孩子会影响其他孩子的档案位置。
来自
fork
手册页:
子级继承父级打开的文件描述符集的副本。子级中的每个文件描述符都引用相同的打开
文件描述(见打开(2))作为相应的文件描述符
在父级中。这意味着两个文件描述符共享打开
文件状态标志,
文件偏移量
和信号驱动的I/O属性
以下是关于共享文件偏移量对
dup
手册页:
成功返回后,可以使用旧的和新的文件描述符
可互换。它们引用相同的打开文件描述(请参见
打开(2)),共享文件偏移量和文件状态标志;
例如,
如果文件偏移量是通过在其中一个文件上使用lseek(2)修改的
描述符中,另一个的偏移量也会改变。
(参见fcntl(2)中的f_setown和f_setsig说明)。
为了解决这个问题,让每个孩子都做它自己的/独特的
open
两个文件中的一个。然后,他们
不会
共享文件偏移量
不
比赛条件。额外开销
open/close
将是最小的。
还有几点…
信号通常是为特殊/意外情况(如分段故障等)保留的。因此,使用
abort
在子通信中,不匹配可以/应该用非零退出代码替换。
这是更清洁和提供更大程度的灵活性。
EXIT_SUCCESS/EXIT_FAILURE
是一组相对新的定义。做的时候
exit(code)
,允许孩子返回
任何
7位错误代码(包括0-127)。参见手册页
化学机械抛光
和
rsync
举个例子。
在这里,您可以使用您希望的任何子错误代码约定,例如:0=匹配、1=不匹配、2=一个文件的短读、3=I/O错误等。
正如阿加努指出的,使
化学机械抛光
函数递归将
非常
效率低下,导致堆栈崩溃。大多数堆栈默认为~8MB,因此这限制了可以处理的文件的大小。考虑8个流程的例子。如果文件大小大于64MB,即使文件大小相等,也会溢出堆栈并得到一个segfault。
另外,做一个
read
对于一个
单一的
字节非常慢,并且完全否定了并行进程获得的任何速度增益。
因此,即使您将工作拆分为多个流程,每个子流程仍然必须循环处理其自己的职责部分中较小的部分。
当读取最佳缓冲区大小时,并不总是最大的。对于某些文件系统(例如
ext4
)建议的大小IIRC为64KB。
我还建议使用一个自定义结构来跟踪
启动/停止
位置和各种其他每个子数据。
这里是您的代码的重构版本,说明了其中的大部分内容。它没有经过测试,仍然有点粗糙,但它应该能帮助您进一步(请原谅这种无偿的风格清理)。
注意/检查事项:
不同长度的文件不需要进行比较。他们可以
从未
匹配。此处
化学机械抛光
假设文件的长度相等。
应将进程数裁剪为文件大小(例如,如果您有8个进程,文件长度为4,则应将进程数裁剪为4)。
范围/限值/尺寸计算应加倍检查,因为可能存在“一次关闭”错误。
另外,一定要使用
off_t
(确保64位值正确
#define
设置在任何之前
#include
指令)允许程序正确处理文件>2GB
#include <...>
char *file1
char *file2;
typedef struct pidctl {
int chunknum;
off_t start;
off_t stop;
} pidctl_t;
#define CHUNKSIZE (64 * 1024)
int retcode = EXIT_SUCCESS;
int cmp(pidctl_t *ctl);
int
main(int argc, char *argv[])
{
off_t len;
int status;
int fd1;
int fd2;
int term;
int i,
j;
int opt,
num;
int start,
stop;
//[...] Various checks on files length and getopt()
num = atoi(optarg);
// process count should be clipped to number of bytes in file
if (num > filesize)
num = filesize;
pidctl_t pidlist[num];
for (i = 0; i < num; i++) {
ctl = &pidlist[i];
ctl->chunknum = i;
ctl->start = (len / num) * i;
ctl->stop = ((len / num) * (i + 1)) - 1;
// the last segment must be clipped to the file size
if (ctl->stop >= len)
ctl->stop = len - 1;
ctl->pid = fork();
if (ctl->pid == 0)
exit(cmp(ctl));
if (ctl->pid < 0) {
perror(NULL);
exit(EXIT_FAILURE);
}
}
while (1) {
term = wait(&status);
if (term < 0)
break;
for (i = 0; i < num; i++) {
ctl = &pidlist[i];
if (term == ctl->pid) {
if (WIFSIGNALED(status)) {
printf("PID-> %d START %lld STOP %lld -- signal %d\n",
ctl->pid, ctl->start, ctl->stop, WTERMSIG(status));
retcode = 2;
}
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
printf("PID-> %d START %lld STOP %lld -- code %d -- %s\n",
ctl->pid, ctl->start, ctl->stop,
code,code ? "FAIL" : "OK");
if (code)
retcode = 1;
}
continue;
}
}
}
exit(retcode);
}
int
cmp(pidctl_t *ctl)
{
int fd1 = -1;
char buf1[CHUNKSIZE];
int fd2 = -1;
char buf2[CHUNKSIZE];
off_t pos;
off_t remain;
int rlen1;
int rlen2;
int xlen;
int code = 0;
do {
if (ctl->start > ctl->stop)
break;
fd1 = open(file1,O_RDONLY);
if (fd1 < 0) {
code = 2;
break;
}
pos = lseek(fd1, ctl->start, SEEK_SET);
if (pos != ctl->start) {
code = 3;
break;
}
fd2 = open(file2,O_RDONLY);
if (fd2 < 0) {
code = 2;
break;
}
pos = lseek(fd2, ctl->start, SEEK_SET);
if (pos != ctl->start) {
code = 3;
break;
}
remain = (ctl->stop - ctl->start) + 1;
for (; remain > 0; remain -= xlen)
xlen = CHUNKSIZE;
if (xlen > remain)
xlen = remain;
rlen1 = read(fd1, buf1, xlen);
if (rlen1 != xlen) {
code = 4;
break;
}
rlen2 = read(fd2, buf2, xlen);
if (rlen2 != xlen) {
code = 5;
break;
}
if (memcmp(buf1,buf2,xlen) != 0) {
code = 1;
break;
}
}
} while (0);
if (fd1 >= 0)
close(fd1);
if (fd2 >= 0)
close(fd2);
return code;
}