为了做好运维面试路上的助攻手,特整理了上百道 【运维技术栈面试题集锦】 ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。
本份面试集锦涵盖了
- 174 道运维工程师面试题
- 128道k8s面试题
- 108道shell脚本面试题
- 200道Linux面试题
- 51道docker面试题
- 35道Jenkis面试题
- 78道MongoDB面试题
- 17道ansible面试题
- 60道dubbo面试题
- 53道kafka面试
- 18道mysql面试题
- 40道nginx面试题
- 77道redis面试题
- 28道zookeeper
总计 1000+ 道面试题, 内容 又全含金量又高
- 174道运维工程师面试题
1、什么是运维?
2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
3、现在给你三百台服务器,你怎么对他们进行管理?
4、简述raid0 raid1raid5二种工作模式的工作原理及特点
5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
7、Tomcat和Resin有什么区别,工作中你怎么选择?
8、什么是中间件?什么是jdk?
9、讲述一下Tomcat8005、8009、8080三个端口的含义?
10、什么叫CDN?
11、什么叫网站灰度发布?
12、简述DNS进行域名解析的过程?
13、RabbitMQ是什么东西?
14、讲一下Keepalived的工作原理?
15、讲述一下LVS三种模式的工作过程?
16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
17、如何重置mysql root密码?
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
rax:系统调用号 参数传递:rdi,rsi,rdx,r10、r8、r9 (rcx -> r10)
(2)
在x86_64架构中,调用sys_call_table的机器码操作是通过间接调用(indirect call)指令来实现的。具体的操作码是ff 14 c5,其表示的汇编指令是callq *%rax。
这条指令的作用是从rax寄存器中获取一个指针地址,然后跳转到该地址执行代码。在这种情况下,我们假设rax寄存器中存储了sys_call_table的地址,以便通过间接调用来调用相应的系统调用函数。
我们通过crash调试便可以获取到sys_call_table的地址:
由上面的汇编代码:
system_call
-->system_call_fastpath
-->call \*sys\_call\_table(,%rax,8)
我们反汇编system_call_fastpath:
crash> dis system_call_fastpath
0xffffffff816b4fb3 <system_call_fastpath>: cmp $0x146,%rax
0xffffffff816b4fb9 <system_call_fastpath+6>: ja 0xffffffff816b5081 <badsys>
0xffffffff816b4fbf <system_call_fastpath+12>: mov %r10,%rcx
0xffffffff816b4fc2 <system_call_fastpath+15>: callq \*-0x7e941120(,%rax,8)
0xffffffff816b4fc9 <system_call_fastpath+22>: mov %rax,0x20(%rsp)
可以看到在地址0xffffffff816b4fc2,调用call *sys_call_table(,%rax,8),然后读取内存地址0xffffffff816b4fc2的值:
crash> rd -64 0xffffffff816b4fc2
ffffffff816b4fc2: 48816beee0c514ff .....k.H
call的操作码是0xff14c5,后面就是sys_call_table的地址0x816beee0
因此sys_call_table的地址是0xffffffff816beee0。
备注:第三节使用了rdmsr来获取 system_call 符号的值。
(3)
我们也可以借助vmlinux来objdump来获取其地址:
# ./extract-vmlinux vmlinuz-3.10.0-693.el7.x86_64 > vmlinux
# objdump -d vmlinux > vmlinux.txt
# vim vmlinux.txt
我们根据 ff 14 c5 指令码来搜索,上一条指令且是movq %r10,%rcx:
movq %r10,%rcx
call \*sys\_call\_table(,%rax,8) # XXX: rip relative
ffffffff816b4fbf: 4c 89 d1 mov %r10,%rcx
ffffffff816b4fc2: ff 14 c5 e0 ee 6b 81 callq \*-0x7e941120(,%rax,8)
可以看到 ff 14 c5 机器码后面的地址就是sys_call_table的地址0x816beee0(x86_64是小端机器)。
因此sys_call_table的地址是0xffffffff816beee0。
二、使用dump_stack
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
static int __init lkm\_init(void)
{
dump\_stack();
return 0;
}
static void __exit lkm\_exit(void)
{
}
module\_init(lkm_init);
module\_exit(lkm_exit);
MODULE\_LICENSE("GPL");
[ 7666.386756] Call Trace:
[ 7666.386761] [<ffffffff816a3d91>] dump_stack+0x19/0x1b
[ 7666.386762] [<ffffffffc01f3009>] lkm_init+0x9/0x1000 [sys_call_table]
[ 7666.386764] [<ffffffff810020e8>] do_one_initcall+0xb8/0x230
[ 7666.386766] [<ffffffff81100734>] load_module+0x1f64/0x29e0
[ 7666.386769] [<ffffffff8134bbf0>] ? ddebug_proc_write+0xf0/0xf0
[ 7666.386770] [<ffffffff810fcdd3>] ? copy_module_from_fd.isra.42+0x53/0x150
[ 7666.386772] [<ffffffff81101366>] SyS_finit_module+0xa6/0xd0
[ 7666.386774] [<ffffffff816b4fc9>] system_call_fastpath+0x16/0x1b
我们就可以看到 system_call_fastpath+0x16 的地址是0xffffffff816b4fc9,因此system_call_fastpath的地址是:
0xffffffff816b4fc9 - 0x16 = 0xffffffff816b4fb3
我们可以从/proc/kallsyms验证:
# cat /proc/kallsyms | grep system\_call\_fastpath
ffffffff816b4fb3 t system_call_fastpath
system_call_fastpath:
#if \_\_SYSCALL\_MASK == ~0
cmpq $__NR_syscall_max,%rax
#else
andl $__SYSCALL_MASK,%eax
cmpl $__NR_syscall_max,%eax
#endif
ja badsys
movq %r10,%rcx
call \*sys\_call\_table(,%rax,8) # XXX: rip relative
这里我们简单点,从上面的crash可以看到:
crash> dis system_call_fastpath
0xffffffff816b4fb3 <system_call_fastpath>: cmp $0x146,%rax
0xffffffff816b4fb9 <system_call_fastpath+6>: ja 0xffffffff816b5081 <badsys>
0xffffffff816b4fbf <system_call_fastpath+12>: mov %r10,%rcx
0xffffffff816b4fc2 <system_call_fastpath+15>: callq \*-0x7e941120(,%rax,8)
call *sys_call_table 在 system_call_fastpath 的 0xf(15)处。
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
static int __init lkm\_init(void)
{
int i;
unsigned char \*ptr;
ptr = (unsigned char \*)(0xffffffff816b4fc9 - 0x16 + 0xf);
for (i = 0; i < 8; i ++) {
//printk("%02x ", (unsigned char)ptr[i]);
printk("%02x ", (unsigned char)\*(ptr + i));
}
return 0;
}
static void __exit lkm\_exit(void)
{
}
module\_init(lkm_init);
module\_exit(lkm_exit);
MODULE\_LICENSE("GPL");
(unsigned char)ptr[i]) = (unsigned char)\*(ptr + i))
# insmod sys\_call\_table.ko
[# dmesg -c
[ 8416.858466] ff 14 c5 e0 ee 6b 81 48
可以看到 ff 14 c5 机器码后面的地址就是sys_call_table的地址0x816beee0(x86_64是小端机器)。
因此sys_call_table的地址是0xffffffff816beee0。
或者直接搜索:
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
static int __init lkm\_init(void)
{
int i, j;
unsigned char \*ptr;
ptr = (unsigned char \*)(0xffffffff816b4fc9 - 0x16 + 0xf);
for (i = 0; i < 20; i ++) {
if( ((unsigned char)ptr[i] == 0xff) && ((unsigned char)ptr[i + 1] == 0x14) && ((unsigned char)ptr[i +2] == 0xc5) ){
printk("0x%x ", \*(unsigned int \*)(ptr+i+3));
break;
}
}
return 0;
}
static void __exit lkm\_exit(void)
{
}
module\_init(lkm_init);
module\_exit(lkm_exit);
MODULE\_LICENSE("GPL");
# insmod sys\_call\_table.ko
# dmesg -c
[ 280.502659] 0x816beee0
因此sys_call_table的地址是0xffffffff816beee0。
三、根据MSR_LSTAR寄存器
我们来看一下内核的启动过程:
start\_kernel()
-->trap\_init()
-->cpu\_init()
-->syscall\_init()
#define MSR\_STAR 0xc0000081 /\* legacy mode SYSCALL target \*/
#define MSR\_LSTAR 0xc0000082 /\* long mode SYSCALL target \*/
#define MSR\_CSTAR 0xc0000083 /\* compat mode SYSCALL target \*/
| Register
Address | Architectural MSR Name / Bit Fields (Former MSR Name) | MSR/Bit Description |
---|---|---|
C000_0081H | IA32_STAR | System Call Target Address (R/W) |
C000_0082H | IA32_LSTAR | IA-32e Mode System Call Target Address (R/W) Target RIP for the called procedure when SYSCALL is executed in 64-bit mode. |
C000_0083H | IA32_CSTAR | IA-32e Mode System Call Target Address (R/W) Not used, as the SYSCALL instruction is not recognized in compatibility mode. |
我们主要看MSR_LSTAR寄存器:
IA32_LSTAR 是 IA-32e 模式下的系统调用目标地址寄存器,用于存储在 64 位模式下执行 SYSCALL 指令时被调用过程的目标 RIP(指令指针)。
当在 64 位模式下执行 SYSCALL 指令时,系统将从 IA32_LSTAR 寄存器中加载 RIP 的值,以跳转至相应的系统调用处理程序。
为了使内核接收传入的系统调用,它必须通过将其地址写入IA32_LSTAR MSR寄存器 来注册将在发生系统调用时执行的代码的地址。
/\* May not be marked \_\_init: used by software suspend \*/
void syscall\_init(void)
{
/\*
\* LSTAR and STAR live in a bit strange symbiosis.
\* They both write to the same internal register. STAR allows to
\* set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
\*/
wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
wrmsrl(MSR_LSTAR, system_call);
wrmsrl(MSR_CSTAR, ignore_sysret);
......
/\* Flags to clear on syscall \*/
wrmsrl(MSR_SYSCALL_MASK,
X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|
X86_EFLAGS_IOPL|X86_EFLAGS_AC);
}
这行代码把system_call入口地址存入到MSR_LSTAR寄存器。syscall指令会把该地址加载到到%rip寄存器,从该地址开始执行。
syscall指令:
SYSCALL 指令用于在特权级别 0(内核模式)下调用操作系统的系统调用处理程序。它通过从 IA32_LSTAR MSR 寄存器加载 RIP(同时将 SYSCALL 指令后面的指令地址保存在 RCX 中)来实现这一功能。IA32_LSTAR MSR 寄存器的值是一个规范地址(canonical address),通过 WRMSR 指令确保其始终包含一个规范地址。
SYSCALL 指令:RIP = IA32_LSTAR MSR 寄存器 = system_call
过程如下:
特别说明一下,因为初始化时,掩码中包含中断标志位X86_EFLAGS_IF,所以syscall指令执行时,中断是禁止的。
我们可以看到在syscall_init中将 system_call 的地址写入了 MSR_LSTAR 寄存器:
wrmsrl(MSR_LSTAR, system_call);
那么我们读取MSR_LSTAR寄存器就可以获取到system_call的地址,进入获取到sys_call_table的地址。
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
static int __init lkm\_init(void)
{
unsigned long msr_lstar;
rdmsrl(MSR_LSTAR, msr_lstar);
printk("MSR\_LSTAR = %lx\n", msr_lstar);
return 0;
}
static void __exit lkm\_exit(void)
{
}
module\_init(lkm_init);
module\_exit(lkm_exit);
MODULE\_LICENSE("GPL");
]# cat /proc/kallsyms | grep '\<system\_call\>'
ffffffff816b4f50 T system_call
# insmod get\_msr.ko
# dmesg -c
[ 1474.209016] MSR_LSTAR = ffffffff816b4f50
获取到了system_call的地址那么解析其后面的字节便可以找到sys_call_table的地址。
完整代码:
#include <linux/module.h>
#include <linux/kernel.h>
void \*memmem(const void \*haystack, size\_t haystack_size, const void \*needle, size\_t needle_size )
{
char \*p;
for (p = (char \*)haystack; p <= ((char \*)haystack - needle_size + haystack_size); p++ )
if (memcmp(p, needle, needle_size) == 0 )
return (void \*)p;
return NULL;
}
unsigned long \*find\_sys\_call\_table ( void )
{
char \*\*p;
unsigned long sct_off = 0;
unsigned char code[512];
rdmsrl(MSR_LSTAR, sct_off);
memcpy(code, (void \*)sct_off, sizeof(code));
p = (char \*\*)memmem(code, sizeof(code), "\xff\x14\xc5", 3);
if (p)
{
unsigned long \*sct = \*(unsigned long \*\*)((char \*)p + 3);
// Stupid compiler doesn't want to do bitwise math on pointers
sct = (unsigned long \*)(((unsigned long)sct & 0xffffffff) | 0xffffffff00000000);
return sct;
}
else
return NULL;
}
static int __init lkm\_init(void)
{
unsigned long \*sys_call_table = find\_sys\_call\_table();
printk("The sys\_call\_table address is:%lx\n",(unsigned long)sys_call_table);
return 0;
}
static void __exit lkm\_exit(void)
{
}
module\_init(init_get_sys_call_table);
module\_exit(exit_get_sys_call_table);
MODULE\_LICENSE("GPL");
# cat /proc/kallsyms | grep '\<sys\_call\_table\>'
ffffffff816beee0 R sys_call_table
# insmod sys\_call\_table.ko
# dmesg -c
[ 2363.360239] The sys_call_table address is:0xffffffff816beee0
四、使用sys_close
// linux-3.10/fs/open.c
/\*
\* Careful here! We test whether the file pointer is NULL before
\* releasing the fd. This ensures that one clone task can't release
\* an fd while another clone is opening it.
\*/
SYSCALL\_DEFINE1(close, unsigned int, fd)
{
int retval = \_\_close\_fd(current->files, fd);
/\* can't restart close syscall because file table entry was cleared \*/
if (unlikely(retval == -ERESTARTSYS ||
retval == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK))
retval = -EINTR;
return retval;
}
EXPORT\_SYMBOL(sys_close);
EXPORT\_SYMBOL(sys_close);
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
t->files, fd);
/\* can't restart close syscall because file table entry was cleared \*/
if (unlikely(retval == -ERESTARTSYS ||
retval == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK))
retval = -EINTR;
return retval;
}
EXPORT_SYMBOL(sys_close);
EXPORT_SYMBOL(sys_close);
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**