从timerfd调用read()时出现无效参数错误的技术求助
搞定timerfd read()返回EINVAL(errno=22)的问题
嗨,我碰到过类似的内核版本差异导致的问题,刚好能帮你分析下:
问题根源
你树莓派上用的Linux 4.9.80属于比较老的内核版本,而笔记本的4.15.15是新内核——两者对timerfd的read()调用规则不一样。在4.11之前的内核里,read()读取timerfd时有个硬要求:你给的缓冲区必须能装下一个uint64_t(也就是8字节)。如果缓冲区比这个小,直接就返回EINVAL(错误码22)。
而新内核(4.11及以后)放宽了这个限制,哪怕缓冲区不够大,也会尝试读取(不过这其实不符合man手册里的规范,因为timerfd本来就该返回8字节的到期计数)。
你要检查的两个关键点
缓冲区大小是否正确
大概率是你用了太小的变量来接收到期计数。比如用int(4字节)而不是uint64_t(8字节)。正确的写法应该是这样:uint64_t exp_count; ssize_t ret = read(timer_fd, &exp_count, sizeof(exp_count)); if (ret == -1) { perror("read timerfd failed"); }要是你之前用的是
int,那缓冲区只有4字节,老内核直接就报错了。确认定时器是否正常触发
虽然epoll应该是触发了才会调用read,但还是要确认下你有没有正确调用timerfd_settime()把定时器“武装”起来,而且epoll的事件监听有没有配置对(比如有没有设对EPOLLIN事件)。不过这个可能性比缓冲区问题小很多。
快速验证的小技巧
你可以在树莓派的代码里加一行打印,看看传给read()的缓冲区大小是多少:
printf("Buffer size for read: %zu\n", sizeof(exp_count));
如果输出是4,那就是变量类型的问题,改成uint64_t就搞定了。
最后总结
这个问题就是新旧内核对参数的严格程度不一样导致的,只要把接收计数的变量改成uint64_t,保证缓冲区够8字节,树莓派上的错误就会消失啦。
内容的提问来源于stack exchange,提问作者lyfeng
相关产品推荐
相关产品推荐

