我在最近的求职面试中遇到了多个简单的问题。
起初,我被要求编写一个简单的程序,从用户那里获取输入 x 并在内存中分配(使用 malloc)x 字节。
我简单的写了:
void main()
{
int x;
scanf("%d",&x);
malloc(x);
}
然后我被告知在运行我的可执行文件时显示所有被调用的系统调用,所以我去了终端并键入:
strace ./my_program.o
这很棒,直到他问了类似的问题:
The output you received from running strace on your program was probably very messy. And there’s no way to tell which system call was used during the execution of malloc. Can you suggest a simple addition to your C code, such that you will be able to spot the system call used during the execution of malloc anyway. BTW, You’re not allowed to add flags to strace and Your change must be made in the C code.
我在这里失去了他。可以对 C 代码进行哪些添加?
您的建议的输出示例(仍然无济于事,因为出于奇怪的原因只有一个写入而不是 2,因为我将我的 C 代码更改为在 malloc 之前有一个,而在 malloc 之后有一个)
execve("./a.out", ["./a.out"], 0x7ffc38701620 /* 50 vars */) = 0
brk(NULL) = 0x55df6cc1b000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=96020, ...}) = 0
mmap(NULL, 96020, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb9d4900000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9d48fe000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb9d42fe000
mprotect(0x7fb9d44e5000, 2097152, PROT_NONE) = 0
mmap(0x7fb9d46e5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fb9d46e5000
mmap(0x7fb9d46eb000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb9d46eb000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7fb9d48ff500) = 0
mprotect(0x7fb9d46e5000, 16384, PROT_READ) = 0
mprotect(0x55df6b542000, 4096, PROT_READ) = 0
mprotect(0x7fb9d4918000, 4096, PROT_READ) = 0
munmap(0x7fb9d4900000, 96020) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL) = 0x55df6cc1b000
brk(0x55df6cc3c000) = 0x55df6cc3c000
read(0,
"\n", 1024) = 1
read(0, 5
"5\n", 1024) = 2
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "__________________________", 26__________________________) = 26
lseek(0, -1, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(0) = ?
+++ exited with 0 +++
最佳答案
您的 malloc
不会为x 的small 值发出任何系统调用(例如
(例如 23)brk
)
那是因为之前的 scanf
调用必须 [内部] 调用 malloc
。内部 malloc
调用 brk
。
堆已经分配了足够的空间,因此您的 malloc
可以满足请求而无需调用brk
。
为了能够看到这一点,请在 malloc
调用之前和之后放置一个 usleep(1)
。这会生成对 nanosleep
系统调用的无害调用,它作为 您的 malloc
调用的标记。
这是输入值 23
的 strace
输出:
execve("./fix1", ["./fix1"], 0x7ffea3366ac0 /* 94 vars */) = 0
brk(NULL) = 0x15fc000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcf4a51d40) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=340324, ...}) = 0
mmap(NULL, 340324, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f15ef9bf000
close(3) = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 E\2\0\0\0\0\0"..., 832) = 832
lseek(3, 792, SEEK_SET) = 792
read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0g\225=\371\27\203\227A\277:}7a\216\376\301"..., 68) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2786704, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f15ef9bd000
lseek(3, 792, SEEK_SET) = 792
read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0g\225=\371\27\203\227A\277:}7a\216\376\301"..., 68) = 68
lseek(3, 864, SEEK_SET) = 864
read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
mmap(NULL, 1857472, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f15ef7f7000
mprotect(0x7f15ef819000, 1679360, PROT_NONE) = 0
mmap(0x7f15ef819000, 1363968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f15ef819000
mmap(0x7f15ef966000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f15ef966000
mmap(0x7f15ef9b3000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bb000) = 0x7f15ef9b3000
mmap(0x7f15ef9b9000, 14272, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f15ef9b9000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f15ef9be500) = 0
mprotect(0x7f15ef9b3000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ) = 0
mprotect(0x7f15efa3c000, 4096, PROT_READ) = 0
munmap(0x7f15ef9bf000, 340324) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL) = 0x15fc000
brk(0x161d000) = 0x161d000
brk(NULL) = 0x161d000
read(0, "23\n", 1024) = 3
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
lseek(0, -1, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(0) = ?
+++ exited with 0 +++
但是,大 值会导致您的 malloc
发出 mmap
系统调用。
这是 1000000000
值的输出:
execve("./fix1", ["./fix1"], 0x7ffe4ec746d0 /* 94 vars */) = 0
brk(NULL) = 0x13df000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe5a768930) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=340324, ...}) = 0
mmap(NULL, 340324, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f61b6e83000
close(3) = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 E\2\0\0\0\0\0"..., 832) = 832
lseek(3, 792, SEEK_SET) = 792
read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0g\225=\371\27\203\227A\277:}7a\216\376\301"..., 68) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2786704, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f61b6e81000
lseek(3, 792, SEEK_SET) = 792
read(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0g\225=\371\27\203\227A\277:}7a\216\376\301"..., 68) = 68
lseek(3, 864, SEEK_SET) = 864
read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
mmap(NULL, 1857472, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f61b6cbb000
mprotect(0x7f61b6cdd000, 1679360, PROT_NONE) = 0
mmap(0x7f61b6cdd000, 1363968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f61b6cdd000
mmap(0x7f61b6e2a000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f61b6e2a000
mmap(0x7f61b6e77000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bb000) = 0x7f61b6e77000
mmap(0x7f61b6e7d000, 14272, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f61b6e7d000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f61b6e82500) = 0
mprotect(0x7f61b6e77000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ) = 0
mprotect(0x7f61b6f00000, 4096, PROT_READ) = 0
munmap(0x7f61b6e83000, 340324) = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL) = 0x13df000
brk(0x1400000) = 0x1400000
brk(NULL) = 0x1400000
read(0, "1000000000\n", 1024) = 11
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
mmap(NULL, 1000001536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f617b30e000
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
lseek(0, -1, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(0) = ?
+++ exited with 0 +++
这是我用来生成上述内容的程序。请注意,任何 生成的系统调用不是 scanf
或malloc
的一部分都可以替换usleep
调用(例如 time
):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void
main()
{
int x;
scanf("%d", &x);
usleep(1);
malloc(x);
usleep(1);
}
更新:
Can you please explain why this won't work: [redacted for space] – ariel
使用 write
应该 没问题。它是“无害的”,write
系统调用应该正常显示。正如我提到的,任何 系统调用都可以使用 [if 它与 scanf
或 malloc
会做的任何事情有唯一区别].
and why you said "not part of scanf"? – ariel
因为 [正如我提到的] scanf
将执行 malloc
[和 free
——但这可能不会生成系统调用].
Even with large numbers I don't see malloc being called. – ariel
我不确定您的设置中发生了什么。我用了 1,000,000,000 来真正强制 your malloc
发出系统调用。
nanosleep
或您的 write
系统调用应作为标记。
您是否在跟踪中看到标记调用之间的 mmap
调用 [或更多 brk
调用]?
更新#2:
Plus, I can't get an input to make it show sbrk() how can I do that – ariel
由各个堆管理器决定使用哪个系统调用 sbrk
、brk
和/或 mmap
以及何时使用.
在我的系统 [linux] 上,我正在使用 glibc
的 malloc
。我怀疑它只执行 brk
而不 sbrk
brk
和 sbrk
非常相似,给定的经理可能会使用其中一个,但不会同时使用这两个 -- YMMV。
来自 man sbrk
:
On Linux, sbrk() is implemented as a library function that uses the brk() system call, and does some internal bookkeeping so that it can return the old break value.
关于c - Strace 与 C 可执行文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68026252/