使用%d vs%ld,linux

时间:2016-04-16 00:32:52

标签: c linux casting printf

我在一组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; } 很长。

4 个答案:

答案 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_taskstruct task_struct*,则curr_task->pidint,在Linux上是4个字节(无论是32位还是64位)。但是64位Linux上的long是8个字节,所以你不仅要复制pid,还要复制4个其他相邻字节(可能是tgid - 我不知道到底是什么,但看起来它通常与pid具有相同的值。

更新:tgid是一个线程组ID,它通常与pid具有相同的值: