上下文就是这个Redis问题.我们有一个wait3()
等待AOF重写子项在磁盘上创建新AOF版本的调用.当孩子完成后,通知父母wait3()
以便用新的AOF替换旧的AOF.
但是,在上述问题的上下文中,用户向我们通知了一个错误.我修改了一点Redis 3.0的实现,以便在wait3()
返回-1 时清楚地记录,而不是因为这种意外情况而崩溃.所以这就是显而易见的事情:
wait3()
当有待等待的孩子时,我们会打电话.SIGCHLD
应设置为SIG_DFL
,没有代码Redis的所有设置这个信号,所以它的默认行为.wait3()
按预期成功运行.wait3()
开始返回-1.AFAIK在我们调用的当前代码中wait3()
是不可能的,因为当没有挂起的子节点时,因为当创建AOF子节点时,我们设置server.aof_child_pid
为pid的值,并且我们仅在成功wait3()
调用之后重置它.
所以wait3()
应该没有理由失败-1 ECHILD
,但它确实如此,所以可能僵尸孩子不是出于某种意想不到的原因而创建的.
假设1:在某些奇怪的条件下Linux可能会丢弃僵尸孩子,例如因为内存压力?看起来不合理,因为僵尸只附加了元数据但谁知道.
请注意,我们打电话wait3()
给WNOHANG
.并且鉴于默认情况下SIGCHLD
设置为SIG_DFL
,唯一应该导致失败并返回-1的条件,并且ECHLD
应该没有可用于报告信息的僵尸.
假设2:可能发生的其他事情但是没有解释,如果它发生,是在第一个孩子死后,SIGCHLD
处理程序被设置为SIG_IGN
,导致wait3()
返回-1和ECHLD
.
假设3:有没有办法从外部移除僵尸儿童?也许这个用户有某种脚本可以在后台删除僵尸进程,以便信息不再可用wait3()
?据我所知,如果父母不等待它(使用或处理信号)并且如果没有被忽略,则永远不可能移除僵尸,但也许有一些特定于Linux的方式.waitpid
SIGCHLD
假设4:实际上有在Redis的代码一些bug,使我们成功wait3()
的孩子第一次不正确复位状态,后来我们叫wait3()
连连,但不再有僵尸,所以它返回-1.分析代码看起来不可能,但也许我错了.
另一件重要的事情:我们过去从未见过这一点.这显然只发生在这个特定的Linux系统中.
更新:Yossi Gottlieb提出 …
嗨,
I received multiple reports from a Redis user that experienced server crashes, using a Redis stable release (latest, 2.4.6). The bug is strange since the user is not doing esoteric things, just working a lot with the sorted set type, and only with the ZADD, ZREM, and ZREVRANK commands. However it is strange that a bug like that, causing crashes after a few billion operations executed, was only experienced by a single user. Fortunately the user in question …
一行背景:我是Redis的开发者,一个NoSQL数据库.我正在实现的一个新功能是虚拟内存,因为Redis将所有数据都存储在内存中.由于VM Redis能够将很少使用的对象从内存传输到磁盘,因此有很多原因可以解释为什么这样做比让OS为我们进行交换工作要好得多(redis对象是由非连续分配的许多小对象构建的)地方,当Redis序列化到磁盘时,它们占用的空间比它们所在的内存页面少10倍,等等.
现在我有一个完全在Linux上运行的alpha实现,但在Mac OS X Snow Leopard上运行得不是很好.有时,当Redis尝试将页面从内存移动到磁盘时,redis进程会进入不间断等待状态几分钟.我无法调试这个,但这发生在调用fseeko()
或fwrite()
.几分钟后,呼叫终于返回并且redis继续正常工作:没有崩溃.
传输的数据量非常小,类似于256字节.因此,它不应该是执行大量I/O的问题.
但是有一个关于交换文件的有趣细节,它是写操作的目标.这是一个大文件(26千兆字节)创建打开文件,fopen()
然后使用放大ftruncate()
.最后文件是unlink()
编辑,以便Redis继续引用它,但我们确信当Redis进程退出操作系统时,将真正释放交换文件.
好的,但我在这里有更多细节.顺便说一句,你甚至可以在Redis git中找到实际的代码,但鉴于这是一个相当复杂的系统,在五分钟内理解这一点并非易事.
非常感谢您的帮助.
我有以下程序启用x86处理器标志寄存器中的对齐检查(AC)位,以捕获未对齐的内存访问.然后程序声明两个volatile变量:
#include <assert.h>
int main(void)
{
#ifndef NOASM
__asm__(
"pushf\n"
"orl $(1<<18),(%esp)\n"
"popf\n"
);
#endif
volatile unsigned char foo[] = { 1, 2, 3, 4, 5, 6 };
volatile unsigned int bar = 0xaa;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我编译它,最初生成的代码会做一些显而易见的事情,比如设置堆栈并通过将值1,2,3,4,5,6移动到堆栈上来创建字符数组:
/tmp ? gcc test3.c -m32
/tmp ? gdb ./a.out
(gdb) disassemble main
0x0804843d <+0>: push %ebp
0x0804843e <+1>: mov %esp,%ebp
0x08048440 <+3>: and $0xfffffff0,%esp
0x08048443 <+6>: sub $0x20,%esp
0x08048446 <+9>: mov %gs:0x14,%eax
0x0804844c <+15>: mov %eax,0x1c(%esp)
0x08048450 <+19>: xor %eax,%eax
0x08048452 <+21>: …
Run Code Online (Sandbox Code Playgroud) 在Redis(http://code.google.com/p/redis)中有与元素相关联的分数,以便对这些元素进行排序.这个分数是双倍的,即使许多用户实际按整数排序(例如unix次).
保存数据库时,我们需要编写这个双打ok磁盘.这是目前使用的:
snprintf((char*)buf+1,sizeof(buf)-1,"%.17g",val);
Run Code Online (Sandbox Code Playgroud)
此外,还检查无穷大和非数字条件,以便在最终数据库文件中表示这一点.
不幸的是,将double转换为字符串表示非常慢.虽然我们在Redis中有一个函数,它以更快的方式将整数转换为字符串表示形式.所以我的想法是检查是否可以将double转换为整数而不丢失数据,然后使用该函数将整数转换为字符串(如果这是真的).
为了提供良好的加速,当然整数"等价"的测试必须快.所以我使用的技巧可能是未定义的行为,但在实践中效果很好.像这样的东西:
double x = ... some value ...
if (x == (double)((long long)x))
use_the_fast_integer_function((long long)x);
else
use_the_slow_snprintf(x);
Run Code Online (Sandbox Code Playgroud)
在我的推理中,上面的双重转换将double转换为long,然后转换为整数.如果范围适合,并且没有小数部分,则该数字将在转换后继续存在,并且与初始数字完全相同.
因为我想确保这不会破坏某些系统中的东西,我加入freenode上的#c并且我受到了很多侮辱;)所以我现在正在尝试这里.
有没有一种标准的方法来做我想要做的事情而不超出ANSI C?否则,上面的代码是否适用于当前Redis目标的所有Posix系统?那就是现在运行Linux/Mac OS X/*BSD/Solaris的拱门?
为了使代码更健全,我可以添加的是在尝试强制转换之前对double的范围进行显式检查.
感谢您的任何帮助.
我正在使用我自己实现的神经网络:它是一个简单的前向网络,使用RPROP作为学习算法,作为与基本设计相比唯一的"加".
当我针对MNIST进行测试时或者当我尝试图像压缩时,网络得分得体,但是当我尝试模拟像XOR函数一样简单的东西时,有时在学习过程中它会陷入局部最小值,并输出以下真值表:
0 XOR 0 = 1.4598413968251171e-171
1 XOR 0 = 0.9999999999999998
0 XOR 1 = 0.9999999999999998
1 XOR 1 = 0.5
Run Code Online (Sandbox Code Playgroud)
训练结果通常是正确的,但有时1 XOR 1输出0.5而不是1.它并不总是在XOR(1,1)中发生,但也与其他输入一起发生.作为反向传播文献中的"经典"的异或函数,我想知道这里发生了什么,特别是考虑到我的网络似乎学习更复杂(但可能更少非线性)的任务就好了.
我的猜测是偏见有问题.
任何提示?
注1:上面的网络布局是2 | 3 | 1,但是当我使用更多隐藏单元时,变化不大,某些学习尝试仍然出错.
注2:我将实现放入要点:https://gist.github.com/antirez/e45939b918868b91ec6fea1d1938db0d
我正在执行一系列活动,以确保Redis在一组嵌入式系统中运行良好,包括Raspberry PI.为了修复Redis的某些代码路径,其中执行了未对齐的内存访问(由于Redis 3.2中引入的更改),我试图强制PI在未对齐的内存访问上记录消息或者向进程发送信号有时候是这样的.通过这种方式,我可以确保Redis在未对齐访问是违规的情况下运行良好,并且它将在平台中运行得更快,而平台可以执行此类访问但速度较慢.在PI v1中使用的ARM v6显然能够处理未对齐的内存访问,因此如果我使用以下命令配置Linux以便向执行未对齐访问的进程发送信号:
echo 4 > /proc/cpu/alignment
Run Code Online (Sandbox Code Playgroud)
然后运行以下程序:
#include <stdio.h>
#include <stdint.h>
int main(int argc, char **argv) {
char *buf = "foobareklsjdfklsjdfslkjfskdljfskdfjdslkjfdslkjfsd";
uint32_t *l = (uint32_t*) (buf+1);
printf("%p\n", l);
printf("%d\n", (int)*l);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我看不到进程收到的任何信号,或者/proc/cpu/alignment
递增的计数器.
我的猜测是,这是由于ARM v6能够自动处理未对齐的地址,如果设置了给定的CPU配置标志.我的问题是,我的假设是否正确?如果是这样,如果未对齐访问,如何强制PI版本1实际引发异常,以便Linux内核可以捕获它并发送信号,记录访问权限等等,根据/ proc/cpu/alignment设置?
编辑:值得注意的是,即使在ARM v6中,并非所有指令都可以执行未对齐的访问.例如,STMDB,STMFD,LDMDB,LDMEA和类似的多个单词指令确实会引发异常并将被Linux内核捕获.