:first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 15px;
}
h1 {
text-transform: uppercase;
font-weight: bold;
border-bottom: 1px solid;
}
h2 {
border-bottom: 1px solid;
}
h3,
h4,
h5,
h6 {
border-bottom: none;
}
html * {
color: #657b83;
}
html body {
background-color: #fdf6e3;
}
html h1,
html h2,
html h3,
html h4,
html h5,
html h6 {
color: #586e75;
border-color: #657b83;
}
html a,
html a:active,
html a:visited {
color: #586e75;
}
html a:hover {
background-color: #eee8d5;
}
html pre {
color: #586e75;
background-color: #eee8d5;
}
html a,
html a:active,
html a:visited,
html code.url {
color: #b58900;
}
html h1 {
color: #b58900;
}
html h2,
html h3,
html h4,
html h5,
html h6 {
color: #b58900;
}
@media print {
body {
margin: 0;
}
* {
color: #000 !important;
}
}
-->
code[class*="language-"],
pre[class*="language-"] {
background: #2d2d2d;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}
-->
Linux系统调用探究(上)
Linux系统提供了一系列系统调用,用户可以通过这些系统调用与Linux内核进行交互,对于一个典型的C程序来说,调用一次系统调用经历了如下三层
- C库API,这是由C库提供的对中断向量的wrapper,也是直接面向用户的一层
- 中断向量,中断向量是一个用户态进程进入内核态执行内核代码的途径,通过中断向量中的中断号,进程会被相应的陷阱门截获并转入内核态
- 中断服务程序,也就是真正执行的内核态代码
1,2运行在用户态,3才真正进入内核态
以简单的getpid系统调用来说,典型做法就是调用POSIX接口getpid(),代码如下
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = getpid();
printf("pid: %d\n", pid);
return 0;
}
这个过程非常简单,但是有必要探究一下系统调用在用户态真正的执行流程,于是我们使用内联汇编来调用getpid系统调用,代码如下
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = 0;
__asm__ __volatile__ (
"mov $0, %%ebx\n\t"
"mov $0x14, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (pid)
);
printf("pid-asm: %d\n", pid);
return 0;
}
我们要关注的部分主要在于内联汇编部分
"mov $0, %%ebx\n\t"
"mov $0x14, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=m" (pid)
最后一行声明了导入导出参数pid,不多说
第一行: mov $0, %%ebx
ebx用于传递系统调用的第一个参数,getpid不需要参数,所以这儿就是立即数0,相当于NULL 系统调用的传参规则如下:
- 当系统调用的参数少于五个时,依次使用ebx,ecx,edx,esi,edi传递参数
- 当系统调用的参数多余五个是,参数应当储存在一段连续内存中,ebx指向该内存区域的起始地址
第二行:mov $0x14, %%eax
触发软中断时,具体调用哪个系统调用是由系统调用号决定的,系统调用号需要储存在eax中,随后使用int $0x80触发中断
第三行:int $0x80
int 0x80触发中断,没啥说的
第四行:mov %%eax, %0
系统中断的返回值储存在eax中,这儿是将eax的值写进了pid变量的内存区域
这个过程也比较简单,但是已经阐明了系统调用在用户态的触发过程
运行结果如图
![实验截图](http://images.cnblogs.com/cnblogscom/current/613723/o7C%60YR(5QP53M18F07GYPU.jpg)
吴韬 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000