我在一个long
arrays中存储了一系列的pid
(即Linux进程id)。 我意识到一个pid
不是很long
,但我没有select使用不同的variablestypes。
我遇到的问题发生在我尝试使用printf
打印pid
。 如果我打印使用%ld
存储pid
的long
,我得到错误的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; }
如果我用%ld
replace%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具有相同的值: