seq_file 在下一个 returns NULL 之后无法正常工作
seq_file not working properly after next returns NULL
这段代码有什么问题(我猜是关于与不同内核版本的兼容性)?
#define BUFFER_SIZE 2048
#define BUFFER_STEP 128
static char buffer[BUFFER_SIZE] = { 0 }; // this is filled with repeating letters a-z in module init
static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
if (*pos >= BUFFER_SIZE) {
return NULL;
}
return buffer + *pos;
}
static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
*pos += BUFFER_STEP;
if (*pos >= BUFFER_SIZE) {
return NULL;
}
return buffer + *pos;
}
static int seqf_ex_show (struct seq_file* m, void* v)
{
seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);
return 0;
}
当运行 qemuarm 中的内核模块(内核v5.0.19
)时,我得到正确的输出:
$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length([=13=]) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128
但是当 运行 在内核 v4.15.0
上时,我得到这个:
$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length([=14=]) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128
line 17: 127
line 18: 126
line 19: 125
line 20: 124
line 21: 123
line 22: 122
line 23: 121
line 24: 120
line 25: 119
line 26: 118
line 27: 117
line 28: 116
line 29: 115
line 30: 114
line 31: 113
line 32: 112
line 33: 111
line 34: 110
line 35: 109
line 36: 108
line 37: 107
line 38: 106
line 39: 105
line 40: 104
line 41: 103
line 42: 102
line 43: 101
line 44: 100
line 45: 99
line 46: 98
line 47: 97
line 48: 96
line 49: 95
line 50: 94
line 51: 93
line 52: 92
line 53: 91
line 54: 90
line 55: 89
line 56: 88
line 57: 87
line 58: 86
line 59: 85
line 60: 84
line 61: 83
line 62: 82
line 63: 81
line 64: 80
line 65: 79
line 66: 78
line 67: 77
line 68: 76
line 69: 75
line 70: 74
line 71: 73
line 72: 72
line 73: 71
line 74: 70
line 75: 69
line 76: 68
line 77: 67
line 78: 66
line 79: 65
line 80: 64
line 81: 63
line 82: 62
line 83: 61
line 84: 60
line 85: 59
line 86: 58
line 87: 57
line 88: 56
line 89: 55
line 90: 54
line 91: 53
line 92: 52
line 93: 51
line 94: 50
line 95: 49
line 96: 48
line 97: 47
line 98: 46
line 99: 45
line 100: 44
line 101: 43
line 102: 42
line 103: 41
line 104: 40
line 105: 39
line 106: 38
line 107: 37
line 108: 36
line 109: 35
line 110: 34
line 111: 33
line 112: 32
line 113: 31
line 114: 30
line 115: 29
line 116: 28
line 117: 27
line 118: 26
line 119: 25
line 120: 24
line 121: 23
line 122: 22
line 123: 21
line 124: 20
line 125: 19
line 126: 18
line 127: 17
line 128: 16
line 129: 15
line 130: 14
line 131: 13
line 132: 12
line 133: 11
line 134: 10
line 135: 9
line 136: 8
line 137: 7
line 138: 6
line 139: 5
line 140: 4
line 141: 3
line 142: 2
line 143: 1
似乎seqf_start
函数在seqf_next
returnsNULL
之后被重复调用。为什么会这样?
在 Linux 内核 4.x 和 5.x 版本之间存在一些 不兼容性 关于处理传递给 [=] 的 *pos
参数12=]回调函数。
Linux 内核 5.x 仅将 *pos
设置为 0 并将其他修改留给 seq_file
回调(.start
和 .next
函数在 struct seq_operations
).
但是,在 Linux 内核中 4.x *pos
可以在回调 之外递增 1 。如果最后一个 .show
调用产生 恰好 字节数来满足 read
系统调用的请求,则会完成此操作。
因为您的代码仅准备好可以被 BUFFER_STEP
整除的 *pos
值,所以当 *pos
在您的函数之外递增时它无法正常工作,因此它可以被 [整除=22=] 不再。
我不知道 Linux 4.x 的行为是否正确,但是如果你想让你的代码处理这样的行为,你需要准备好 [=11= 的所有值], 不仅是你的代码生成的。
您的代码的简单修复是将 *pos
解释为 块索引 而不是 字节索引 :
static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}
return buffer + (*pos * BUFFER_STEP);
}
static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
*pos += 1;
if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}
return buffer + (*pos * BUFFER_STEP);
}
static int seqf_ex_show (struct seq_file* m, void* v)
{
seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);
return 0;
}
这样您的代码将适用于 *pos
的所有值。 (那些太高的会被检查 if (*pos >= (BUFFER_SIZE/BUFFER_STEP))
切断)。在您的代码之外执行的 *pos
增加 1 相当于调用您的 .next
回调。
这段代码有什么问题(我猜是关于与不同内核版本的兼容性)?
#define BUFFER_SIZE 2048
#define BUFFER_STEP 128
static char buffer[BUFFER_SIZE] = { 0 }; // this is filled with repeating letters a-z in module init
static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
if (*pos >= BUFFER_SIZE) {
return NULL;
}
return buffer + *pos;
}
static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
*pos += BUFFER_STEP;
if (*pos >= BUFFER_SIZE) {
return NULL;
}
return buffer + *pos;
}
static int seqf_ex_show (struct seq_file* m, void* v)
{
seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);
return 0;
}
当运行 qemuarm 中的内核模块(内核v5.0.19
)时,我得到正确的输出:
$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length([=13=]) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128
但是当 运行 在内核 v4.15.0
上时,我得到这个:
$ cat /proc/seqf-ex | awk '{ print "line" OFS NR ":" OFS length([=14=]) }'
line 1: 128
line 2: 128
line 3: 128
line 4: 128
line 5: 128
line 6: 128
line 7: 128
line 8: 128
line 9: 128
line 10: 128
line 11: 128
line 12: 128
line 13: 128
line 14: 128
line 15: 128
line 16: 128
line 17: 127
line 18: 126
line 19: 125
line 20: 124
line 21: 123
line 22: 122
line 23: 121
line 24: 120
line 25: 119
line 26: 118
line 27: 117
line 28: 116
line 29: 115
line 30: 114
line 31: 113
line 32: 112
line 33: 111
line 34: 110
line 35: 109
line 36: 108
line 37: 107
line 38: 106
line 39: 105
line 40: 104
line 41: 103
line 42: 102
line 43: 101
line 44: 100
line 45: 99
line 46: 98
line 47: 97
line 48: 96
line 49: 95
line 50: 94
line 51: 93
line 52: 92
line 53: 91
line 54: 90
line 55: 89
line 56: 88
line 57: 87
line 58: 86
line 59: 85
line 60: 84
line 61: 83
line 62: 82
line 63: 81
line 64: 80
line 65: 79
line 66: 78
line 67: 77
line 68: 76
line 69: 75
line 70: 74
line 71: 73
line 72: 72
line 73: 71
line 74: 70
line 75: 69
line 76: 68
line 77: 67
line 78: 66
line 79: 65
line 80: 64
line 81: 63
line 82: 62
line 83: 61
line 84: 60
line 85: 59
line 86: 58
line 87: 57
line 88: 56
line 89: 55
line 90: 54
line 91: 53
line 92: 52
line 93: 51
line 94: 50
line 95: 49
line 96: 48
line 97: 47
line 98: 46
line 99: 45
line 100: 44
line 101: 43
line 102: 42
line 103: 41
line 104: 40
line 105: 39
line 106: 38
line 107: 37
line 108: 36
line 109: 35
line 110: 34
line 111: 33
line 112: 32
line 113: 31
line 114: 30
line 115: 29
line 116: 28
line 117: 27
line 118: 26
line 119: 25
line 120: 24
line 121: 23
line 122: 22
line 123: 21
line 124: 20
line 125: 19
line 126: 18
line 127: 17
line 128: 16
line 129: 15
line 130: 14
line 131: 13
line 132: 12
line 133: 11
line 134: 10
line 135: 9
line 136: 8
line 137: 7
line 138: 6
line 139: 5
line 140: 4
line 141: 3
line 142: 2
line 143: 1
似乎seqf_start
函数在seqf_next
returnsNULL
之后被重复调用。为什么会这样?
在 Linux 内核 4.x 和 5.x 版本之间存在一些 不兼容性 关于处理传递给 [=] 的 *pos
参数12=]回调函数。
Linux 内核 5.x 仅将 *pos
设置为 0 并将其他修改留给 seq_file
回调(.start
和 .next
函数在 struct seq_operations
).
但是,在 Linux 内核中 4.x *pos
可以在回调 之外递增 1 。如果最后一个 .show
调用产生 恰好 字节数来满足 read
系统调用的请求,则会完成此操作。
因为您的代码仅准备好可以被 BUFFER_STEP
整除的 *pos
值,所以当 *pos
在您的函数之外递增时它无法正常工作,因此它可以被 [整除=22=] 不再。
我不知道 Linux 4.x 的行为是否正确,但是如果你想让你的代码处理这样的行为,你需要准备好 [=11= 的所有值], 不仅是你的代码生成的。
您的代码的简单修复是将 *pos
解释为 块索引 而不是 字节索引 :
static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}
return buffer + (*pos * BUFFER_STEP);
}
static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
*pos += 1;
if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}
return buffer + (*pos * BUFFER_STEP);
}
static int seqf_ex_show (struct seq_file* m, void* v)
{
seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);
return 0;
}
这样您的代码将适用于 *pos
的所有值。 (那些太高的会被检查 if (*pos >= (BUFFER_SIZE/BUFFER_STEP))
切断)。在您的代码之外执行的 *pos
增加 1 相当于调用您的 .next
回调。