我正在尝试使用套接字将getrusage
函数实现到我的客户端服务器程序中,所有这些都在FreeBSD上运行。我想打印出处理器时间使用和内存使用。
我试图实现以下代码,但我得到输出非法指令(核心转储)
int getrusage(int who, struct rusage *usage){
int errorcode;
__asm__(
"syscall"
: "=a" (errorcode)
: "a" (117), "D" (who), "S" (usage) //const Sysgetrusage : scno = 117
: "memory"
);
if (errorcode<0) {
printf("error");
}
return 1;
}
更新:我尝试运行这个,但我得到零值或一些随机数值或负数值。任何想法我错过了什么?
int getrusage(int who, struct rusage *usage){
int errorcode;
__asm__("push $0;"
"push %2;"
"push %1;"
"movl $117, %%eax;"
"int $0x80;"
:"=r"(errorcode)
:"D"(who),"S"(usage)
:"%eax"
);
if (errorcode<0) {
printf("error");
}
return 1;
}
我想更有可能使用系统调用write
,但它给了我一个编译警告:传递'strlen'的arg 1使指针来自整数而没有强制转换
编辑:(这是现在的工作代码,关于评论)
struct rusage usage;
getrusage(RUSAGE_SELF,&usage);
char tmp[300];
write(i, "Memory: ", 7);
sprintf (tmp, "%ld", usage.ru_maxrss);
write(i, tmp, strlen(tmp));
write(i, "Time: ", 5);
sprintf (tmp, "%lds", usage.ru_utime.tv_sec);
write(i, tmp, strlen(tmp));
sprintf (tmp, "%ldms", usage.ru_utime.tv_usec);
write(i, tmp, strlen(tmp));
知道什么是错的吗?
您收到非法指令
错误的原因是SYSCALL指令仅在运行64位程序的64位FreeBSD上可用。这是一个严重的问题,因为您的一条评论表明您的代码正在32位FreeBSD上运行。
在正常情况下,您不需要编写自己的getrusage,因为它是该平台上C库(libc)的一部分。似乎您的任务是使用内联汇编来完成它。
由于SYSCALL会破坏RCX和R11的内容,您的64位代码有点bug。您的代码可能会工作,但将来可能会失败,尤其是当程序扩展并启用优化时。以下更改将这2个寄存器添加到垃圾列表中:
int errorcode;
__asm__(
"syscall"
: "=a" (errorcode)
: "a" (117), "D" (who), "S" (usage) //const Sysgetrusage : scno = 117
: "memory", "rcx", "r11"
);
使用内存
阻塞器会导致生成低效代码,因此我仅在必要时使用它。随着您成为专家,可以消除对内存
阻塞器的需求。如果不允许我使用getrusage的C库版本,我会使用如下函数:
int getrusage(int who, struct rusage *usage){
int errorcode;
__asm__(
"syscall"
: "=a"(errorcode), "=m"(*usage)
: "0"(117), "D"(who), "S"(usage)
: "rcx", "r11"
);
if (errorcode<0) {
printf("error");
}
return errorcode;
}
这使用内存操作数作为输出约束并丢弃内存
阻塞器。由于编译器知道rusage
结构有多大并且是=m
表示输出约束修改了我们不需要的内存,需要内存
阻塞器。
正如注释和更新代码中提到的,要在FreeBSD中使用32位代码进行系统调用,您必须使用int 0x80
。这在FreeBSD系统调用约定中有所描述。参数从右向左压入堆栈,您必须在压入最后一个参数后将任何4字节值压入堆栈,从而在堆栈上分配4个字节。
您编辑的代码有一些错误。首先您在其余参数之前推送额外的4个字节。您需要在之后推送它。您需要在int 0x80
之后调整堆栈,以有效地回收传递的参数使用的堆栈空间。您在堆栈上推送了三个4字节值,因此您需要在int 0x80
之后添加12ESP。
你还需要一个内存
重击器,因为编译器根本不知道你实际上修改了内存。这是因为你做约束的方式变量用法
中的数据被修改了,但编译器不知道是什么。
int 0x80
的返回值将在EAX中,但您使用约束=r
。它应该是=a
,因为返回值将在EAX中返回。由于使用=a
告诉编译器EAX已被破坏,因此您无需再将其列为破坏者。
修改后的代码可能如下所示:
int getrusage(int who, struct rusage *usage){
int errorcode;
__asm__("push %2;"
"push %1;"
"push $0;"
"movl $117, %%eax;"
"int $0x80;"
"add $12, %%esp"
:"=a"(errorcode)
:"D"(who),"S"(usage)
:"memory"
);
if (errorcode<0) {
printf("error");
}
return errorcode;
}
另一种可以用更先进的技术编写的方法是:
int getrusage(int who, struct rusage *usage){
int errorcode;
__asm__("push %[usage]\n\t"
"push %[who]\n\t"
"push %%eax\n\t"
"int $0x80\n\t"
"add $12, %%esp"
:"=a"(errorcode), "=m"(*usage)
:"0"(117), [who]"ri"(who), [usage]"r"(usage)
:"cc" /* Don't need this with x86 inline asm but use for clarity */
);
if (errorcode<0) {
printf("error");
}
return errorcode;
}
有关扩展内联汇编程序的更多信息,请参见GCC留档。