标签:current switch语句 container segment default 建立连接 err 汇编代码 ror
本文主要阐述C语言socket api追踪至系统调用的详细过程。追踪过程分为用户态的追踪和内核态的追踪。
操作系统通过系统调用为运行于其上的进程提供服务。
当用户态进程发起一个系统调用, CPU 将切换到内核态并开始执行一个内核函数 。 内核函数负责响应应用程序的要求,例如操作文件、进行网络通讯或者申请内存资源等。
验证过程:分别在以上函数处打断点,启动内核
537 asmlinkage __visible void __init start_kernel(void)
538 {
....
595 trap_init();
596 mm_init();
....
740 }
929 void __init trap_init(void)
930 {
931 /* Init cpu_entry_area before IST entries are set up */
932 setup_cpu_entry_areas();
933
934 idt_setup_traps();
....
955 }
/* Interrupt gate 中断门*/
#define INTG(_vector, _addr) G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL0, __KERNEL_CS)
/* System interrupt gate 系统中断门 */
#define SYSG(_vector, _addr) G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL3, __KERNEL_CS)
/* Task gate 任务门*/
#define TSKG(_vector, _gdt) G(_vector, NULL, DEFAULT_STACK, GATE_TASK, DPL0, _gdt << 3)
// def_idt的定义
// 可以看到int 0x80对应的中断服务例程是entry_INT80_32。这就是系统调用的中断门
static const __initconst struct idt_data def_idts[] = {
INTG(X86_TRAP_DE, divide_error),
INTG(X86_TRAP_NMI, nmi),
INTG(X86_TRAP_BR, bounds),
INTG(X86_TRAP_UD, invalid_op),
INTG(X86_TRAP_NM, device_not_available),
INTG(X86_TRAP_OLD_MF, coprocessor_segment_overrun),
INTG(X86_TRAP_TS, invalid_TSS),
INTG(X86_TRAP_NP, segment_not_present),
INTG(X86_TRAP_SS, stack_segment),
INTG(X86_TRAP_GP, general_protection),
INTG(X86_TRAP_SPURIOUS, spurious_interrupt_bug),
INTG(X86_TRAP_MF, coprocessor_error),
INTG(X86_TRAP_AC, alignment_check),
INTG(X86_TRAP_XF, simd_coprocessor_error),
#ifdef CONFIG_X86_32
TSKG(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS),
#else
INTG(X86_TRAP_DF, double_fault),
#endif
INTG(X86_TRAP_DB, debug),
#ifdef CONFIG_X86_MCE
INTG(X86_TRAP_MC, &machine_check),
#endif
SYSG(X86_TRAP_OF, overflow),
#if defined(CONFIG_IA32_EMULATION)
SYSG(IA32_SYSCALL_VECTOR, entry_INT80_compat),
#elif defined(CONFIG_X86_32)
SYSG(IA32_SYSCALL_VECTOR, entry_INT80_32),
#endif
};
/**
* idt_setup_traps - Initialize the idt table with default traps
*/
void __init idt_setup_traps(void)
{
idt_setup_from_table(idt_table, def_idts, ARRAY_SIZE(def_idts), true);
}
static void
idt_setup_from_table(gate_desc *idt, const struct idt_data *t, int size, bool sys)
{
gate_desc desc;
for (; size > 0; t++, size--) {
idt_init_desc(&desc, t);
write_idt_entry(idt, t->vector, &desc);
if (sys)
set_bit(t->vector, system_vectors);
}
}
其中,第2步的从用户态到内核态的切换比较复杂,又分为以下几个步骤
证明过程如下:
因为我们无法在menuOS里使用gdb,意味着我们无法在socket api打断点来进行追踪。
但从上面的步骤我们可知,程序是通过int 0x80来实现软中断,进而实现系统调用的。
以socket()函数为例。如果我们可以通过软中断的方式实现创建socket,则可证明以上系统调用执行的步骤是正确的。
在menu/test.c里增加menuOS命令和实现。
实现一个sockettest函数,用最简单的socket()函数创建一个socket,判断是否成功并打印。
然后再写一个sockettestASM函数。这里我们不再用socket函数了,而是写入汇编代码,通过寄存器传参,通过"int 0x80来实现软中断"。
其中,%ebx存放PF_INET=2,表示用IP协议。%ecx存放SOCK_STRAEM=1,表示用TCP协议。%edx传0。%eax存放sys_socket系统调用号,为0x167=359。
//系统调用号:arch/x86/include/generated/uapi/asm/unistd_32.h
...
#define __NR_socketcall 102
...
#define __NR_socket 359
#define __NR_socketpair 360
#define __NR_bind 361
#define __NR_connect 362
#define __NR_listen 363
#define __NR_accept4 364
#define __NR_getsockopt 365
#define __NR_setsockopt 366
#define __NR_getsockname 367
#define __NR_getpeername 368
#define __NR_sendto 369
#define __NR_sendmsg 370
#define __NR_recvfrom 371
#define __NR_recvmsg 372
#define __NR_shutdown 373
在menuOS中添加sockettest和sockettestasm命令。
分别运行这两个命令。比较运行结果。
可以看到两种方式都成功创建了socket。可以证明用户态下是通过软中断的方式
用上次实验的replyhi和hello来跟踪socket api的系统调用
在/lab3/main.c中找到replyhi和hello的实现,并在syswarpper.h里找到每个函数具体调用哪个socket api
#include"syswrapper.h"
#define MAX_CONNECT_QUEUE 1024
// StartReplyhi创建了一个子进程来执行Replyhi
int Replyhi()
{
char szBuf[MAX_BUF_LEN] = "\0";
char szReplyMsg[MAX_BUF_LEN] = "hi\0";
InitializeService(); // socket() -> bind() -> listen()
while (1)
{
ServiceStart(); // accept()
RecvMsg(szBuf); // recv() 收到hello
SendMsg(szReplyMsg); // send()回复hi
ServiceStop(); // close()
}
ShutdownService();
return 0;
}
int Hello(int argc, char *argv[])
{
char szBuf[MAX_BUF_LEN] = "\0";
char szMsg[MAX_BUF_LEN] = "hello\0";
OpenRemoteService(); // socket() -> connect()
SendMsg(szMsg); // send() 发送hello
RecvMsg(szBuf); // recv() 收到hi
CloseRemoteService(); // close()
return 0;
}
可以看到这两个程序就是通过socket api,严格按照服务端和客户端的执行流程来编写的。再次打出socket 客户端和服务端建立连接过程,顺便复习一下。
系统调用表:arch/x86/include/generated/uapi/asm/unistd_32.h
在gdb中把断点设在sys_socketcall,在menuOS里执行replyhi
可以看到进入到了sys_socketcall里,并调用了SYSCALL_DEFINE2函数
在net/socket.c中可以找到sys_socketcall和SYSCALL_DEFINE2。查看实现
/*
* System call vectors.
*
* Argument checking cleaned up. Saved 20% in size.
* This function doesn't need to set the kernel lock because
* it is set by the callees.
*/
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
...
err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
if (err)
return err;
a0 = a[0];
a1 = a[1];
switch (call) {
case SYS_SOCKET:
err = __sys_socket(a0, a1, a[2]);
break;
case SYS_BIND:
err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_CONNECT:
err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_LISTEN:
err = __sys_listen(a0, a1);
break;
case SYS_ACCEPT:
err = __sys_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], 0);
break;
case SYS_GETSOCKNAME: ...
case SYS_GETPEERNAME: ...
case SYS_SOCKETPAIR: ...
case SYS_SEND: ...
case SYS_SENDTO: ...
case SYS_RECV: ...
case SYS_RECVFROM: ...
case SYS_SHUTDOWN: ...
case SYS_SETSOCKOPT: ...
case SYS_GETSOCKOPT: ...
case SYS_SENDMSG: ...
case SYS_SENDMMSG: ...
case SYS_RECVMSG: ...
case SYS_RECVMMSG: ...
case SYS_ACCEPT4: ...
default:
err = -EINVAL;
break;
}
return err;
}
由此可知socket api的系统调用入口为sys_socketcall,然后调用SYSCALL_DEFINE2并传递call参数。在SYSCALL_DEFINE2里通过switch语句和call的值调用不同的socket系统调用。
这些socket接口函数编号的宏定义见include/uapi/linux/net.h
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */
以socket为例,从sys_socketcall开始函数调用栈如下
socket()
--------内核态---------
| -->sys_socketcall
|--> SYSCALL_DEFINE2(call=1)
|-->sock_create(family, type, protocol, &sock)
|-->__sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
|-->sock_alloc();
|-->return sock = SOCKET_I(inode);
|-->return &container_of(inode, struct socket_alloc, vfs_inode)->socket
|-->pf->create()
|-->inet_create()
|-->sock_map_fd()
|-->sock_alloc_fd()
|-->sock_attach_fd()
|-->fd_install
标签:current switch语句 container segment default 建立连接 err 汇编代码 ror
原文地址:https://www.cnblogs.com/zaihua/p/12061787.html