用%d和%ld打印pid,linux

我在一个longarrays中存储了一系列的pid (即Linux进程id)。 我意识到一个pid不是很long ,但我没有select使用不同的variablestypes。

我遇到的问题发生在我尝试使用printf打印pid 。 如果我打印使用%ld存储pidlong ,我得到错误的pid:

 8435315771308 process_ancesto 8358006359962 bash 8353711392665 login 4294967297 init 0 swapper/0 

但是,如果我打印使用%d (它会生成一个编译器警告),我得到正确的结果(即键入ps到terminal返回的结果):

 1969 process_ancesto 1946 bash 1945 login 1 init 0 swapper/0 

什么是造成这种行为? 一个pid – > long cast是一个扩大的转换,不应该引起任何问题。

这里是我用来做系统调用返回pids的程序:

 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; } 

如果我用%ldreplace%d ,它会输出不正确的信息。

以下是我的系统调用中loggingpid

 if(copy_long_to_user(&info_array[num_filled_kernel].pid, &curr_task->pid)) { return -EFAULT; } 

info_array[num_filled_kernel].pid很长。

两个问题:

在你的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格式说明符。

一个pid – > long cast是一个扩大的转换,不应该引起任何问题。

当然,但除非你真的表演演员,否则不会发生,至少不会与可变参数/标准。 既可以自己施放参数,也可以使用正确的说明符。

如果你看看你的号码是十六进制数字

 8358006359962 bash 

在十六进制

 79A0000079A 

 1946 bash 

在十六进制

 79A 

因此,在这里猜测 – 但是当你在copy_long_to_user中将数字转换成long long时,你做了一些坏事,因为79A序列在较高位中重复。

所以使用下面的代码将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位)。 然而long在64位的Linux上,长度是8字节,所以你最终不仅复制了pid,还复制了其他4个字节(这可能是tgid – 我不知道它到底是什么,但它看起来像是经常有与pid相同的值)。

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