我在一组pid
s中存储了一系列long
s(即Linux进程ID)。我意识到pid
不是long
,但我在使用不同的变量类型方面别无选择。
当我尝试使用pid
打印printf
时出现的问题。如果我打印使用long
存储pid
的{{1}},我会得到错误的pid:
%ld
但是,如果我使用8435315771308 process_ancesto
8358006359962 bash
8353711392665 login
4294967297 init
0 swapper/0
打印(生成编译器警告),我会得到正确的结果(即通过在终端中输入%d
返回的结果):
ps
导致此行为的原因是什么? pid - > long cast是一种不断扩大的转换,不会引起任何问题。
这是我用来进行系统调用的程序,它返回pids:
1969 process_ancesto
1946 bash
1945 login
1 init
0 swapper/0
如果我将int main(int argc, char *argv[])
{
struct process_info arr[10];
long n=0;
int result = syscall(_CS300_PROCESS_ANCESTORS_, arr,10,&n);
assert(result==0);
for(int i=0;i<n;i++) {
printf("%d %s\n",arr[i].pid,arr[i].name);
}
return 0;
}
替换为%d
,则会输出错误信息。
以下是我系统调用中记录%ld
:
pid
if(copy_long_to_user(&info_array[num_filled_kernel].pid, &curr_task->pid)) {
return -EFAULT;
}
很长。
答案 0 :(得分:5)
两个问题:
在你的copy_to_user
中,第二个参数是指向pid_t
的指针,它出现,正如你所说的是int
(32位)。所以你从32位变量复制64位;如果幸运的话,其余的32位(高半部分,因为x86是小端)将填充内存中的下一个内容。 (如果你运气不好,你会在这里遇到错误。)当你通过指针访问时,不会进行整数转换。
安全地执行此操作的最简单方法是使用临时变量:
long tmp = curr_task->pid; // sign-extension done here
copy_long_to_user(..., &tmp);
然后在您的用户空间代码中,您使用%d
格式说明符打印显然是long
的内容。这不起作用; printf
,作为一个可变函数,不知道其参数预期具有的类型,因此无法正确转换它们。如果您要传递long
,请使用%ld
格式说明符。
答案 1 :(得分:2)
pid - &gt;长期演员是一种不断扩大的转换,不应该引起任何问题。
当然,但除非你真正表演了演员表,否则它不会发生,至少不会发生varargs / stdargs。要么自己抛出参数,要么使用正确的说明符。
答案 2 :(得分:2)
如果您将数字看作十六进制数
8358006359962 bash
是十六进制
79A0000079A
和
1946 bash
是十六进制
79A
所以在这里猜测 - 但是当你在copy_long_to_user
中将数字转换为long long时,你已经做了一些不好的事情,因为79A序列在较高位中重复。
答案 3 :(得分:1)
因此使用以下命令将pid复制到输出数组中:
copy_long_to_user(&info_array[num_filled_kernel].pid, &curr_task->pid)
如果curr_task
是struct task_struct*
,则curr_task->pid
是int
,在Linux上是4个字节(无论是32位还是64位)。但是64位Linux上的long
是8个字节,所以你不仅要复制pid,还要复制4个其他相邻字节(可能是tgid
- 我不知道到底是什么,但看起来它通常与pid具有相同的值。
更新:tgid
是一个线程组ID,它通常与pid具有相同的值: