你正在使用哪个版本的Go( go version
)?
$ go version
go version go1.11 linux/amd64
这个问题在最新版本中是否会重现?
使用 fmt.Fscan
在 tracefs 文件上。例如/sys/kernel/debug/tracing/events/tcp/tcp_set_state/id
你正在使用什么操作系统和处理器架构( go env
)?
go env
输出
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/elazar/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/elazar/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build929178475=/tmp/go-build -gno-record-gcc-switches"
你做了什么?
https://play.golang.org/p/sKRnsBHZ6Gc
你期望看到什么?
当我 cat
该文件时看到的相同的事件ID。
你看到了什么?
只有第一个数字, 1
。
记录一下,tracefs中的"id"并不是由 seq_file
实现的:
static ssize_t
event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
int id = (long)event_file_data(filp);
char buf[32];
int len;
// Elazar: This is probably the culprit. Can be removed, simple_read_from_buf handles it.
if (*ppos)
return 0;
if (unlikely(!id))
return -ENODEV;
len = sprintf(buf, "%d\n", id);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
}
在 master
分支中仍然相关
似乎 simple_read_from_buffer
在某种程度上无法跟踪 ppos
,尽管它应该可以。
例如
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char **argv) {
if (argc < 2)
return 1;
int fd = open(argv[1], O_RDONLY);
char c;
read(fd, &c, 1);
printf("Got %c\n", c);
read(fd, &c, 1);
printf("Got %c\n", c);
}
// output:
// Got 1
// Got 1
虽然这可能是一个内核bug,但有人可能会考虑一个解决方法,因为它非常普遍。
5条答案
按热度按时间pkbketx91#
这不是一个内核bug,请参考我类似的关注here。
您希望用缓冲区 Package
fp
,所以:将起作用。
oipij1gg2#
这是个不错的解决方法,但我不想使用未知大小的缓冲区。(在我看来,这确实是个内核bug,但这不是主要问题。不跟踪seek位置有什么好处?)
dgiusagp3#
你可以使用合理的缓冲区来处理数字。我认为这是优化的,因为结果不应该是文本。
oymdgrw74#
/cc @ianlancetaylor
cyej8jka5#
我并不认为我们能在Go工具链中解决这个问题。如果Linux内核不支持在文件上连续调用
read
,那么你需要一次性读取整个文件。如果你想让fmt.Fscan
像C语言的fscanf
那样工作,那么你需要像@Gnouc建议的那样使用bufio.NewReader
。我们当然不会修改Go库,专门检查跟踪文件并以不同方式处理它们。