⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 index.html

📁 这是一个介绍 linux 编程知识的文章。
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<p>ENTRY(gdt_table)
<dir>.quad 0x0000000000000000/* NULL descriptor */
<p>.quad 0x0000000000000000/* not used */
<p>.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
<p>.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
<p>.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
<p>.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */</dir>
</dir>
</dir>
</dir>
</dir>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
2.0 linux/arch/i386/head.S
<dir>
<dir>
<dir>
<dir>ENTRY(gdt)
<dir>.quad 0x0000000000000000 /* NULL descriptor */
<p>.quad 0x0000000000000000 /* not used */
<p>.quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
<p>.quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
<p>.quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
<p>.quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 *
<p> 
<p><font face="宋体">在</font>2.0<font face="宋体">版的内核中</font>SAVE_ALL<font face="宋体">宏定义还有这样几条语句:</font>
<p>"movl $" STR(KERNEL_DS) ",%edx\n\t" \
<p>"mov %dx,%ds\n\t" \
<p>"mov %dx,%es\n\t" \
<p>"movl $" STR(USER_DS) ",%edx\n\t" \
<p>"mov %dx,%fs\n\t" \
<p>"movl $0,%edx\n\t" \
<p><font size=-2> </font>
<br>&nbsp;</dir>
</dir>
</dir>
</dir>
</dir>

<dir>
<dir><b><font face="宋体"><font size=+0>E.调用返回</font></font></b>
<br>调用返回的过程要做的工作比其响应过程要多一些,这些工作几乎是每次从核心态返回用户态都需要做的,这里将简要的说明:
<p>1.判断有没有软中断,如果有则跳转到软中断处理;
<br>2.判断当前进程是否需要重新调度,如果需要则跳转到调度处理;
<br>3.如果当前进程有挂起的信号还没有处理,则跳转到信号处理;
<br>4.使用用<font size=+0>RESTORE_ALL<font face="宋体">来弹出所有被</font>SAVE_ALL<font face="宋体">压入核心栈的内容并且使用</font>iret<font face="宋体">返回用户态。</font></font>
<p><b><font face="宋体"><font size=+0>F.实例介绍</font></font></b></dir>
</dir>

<dir>
<dir><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 前面介绍了系统调用相关的数据结构以及在</font>Linux<font face="宋体">中使用一个系统调用的过程中每一步是怎样处理的,下面将把前面的所有概念串起来,说明怎样在</font>Linux<font face="宋体">中增加一个系统调用。</font></font>
<p><font size=+0><font face="宋体">这里实现的系统调用</font>hello<font face="宋体">仅仅是在控制台上打印一条语句,没有任何功能。</font></font>
<p><font size=+0><font face="宋体">1.修改</font>linux/include/i386/unistd.h<font face="宋体">,在里面增加一条语句:</font></font></dir>
</dir>

<ol TYPE="I">
<ol TYPE="I">&nbsp;
<br><font size=+0>#define __NR_hello ???<font face="宋体">(这个数字可能因为核心版本不同而不同)</font></font>
<br><font size=+0><font face="宋体">2.在某个合适的目录中(如:</font>linux/kernel<font face="宋体">)增加一个</font>hello.c<font face="宋体">,修改该目录下的</font>Makefile<font face="宋体">(把相映的</font>.o<font face="宋体">文件列入</font>Makefile<font face="宋体">中就可以了)。</font></font>
<br><font size=+0><font face="宋体">3.编写</font>hello.c</font>
<p><font size=+0>. . . . . .</font>
<p><font size=+0>asmlinkage int sys_hello(char * str)</font>
<p><font size=+0>{</font>
<p><font size=+0>printk(“My syscall: hello, I know what you say to me:
%s ! \n”, str);</font>
<p><font size=+0>return 0;</font>
<p><font size=+0>}</font>
<p><font face="宋体"><font size=-2> </font></font>
<br><font size=+0><font face="宋体">4.修改</font>linux/arch/i386/kernel/entry.S<font face="宋体">,在里面增加一条语句:</font></font>
<p><font size=+0>ENTRY(sys_call_table)</font>
<p><font size=+0>. . . . . .</font>
<p><font size=+0>.long SYMBOL_NAME(sys_hello)</font>
<p><font face="宋体"><font size=+0>并且修改:</font></font>
<p><font size=+0>.rept NR_syscalls-???<font face="宋体"> </font>/* ???
= ??? +1 */</font>
<p><font size=+0>.long SYMBOL_NAME(sys_ni_syscall)</font>
<br><font size=+0><font face="宋体">5.在</font>linux/include/i386/<font face="宋体">中增加</font>hello.h<font face="宋体">,里面至少应包括这样几条语句:</font></font></ol>
</ol>

<dir>
<dir>
<dir>
<dir>
<dir><font size=+0>#include &lt;linux/unistd.h></font>
<p><font face="宋体"><font size=+0> </font></font>
<p><font size=+0>#ifdef __KERNEL</font>
<p><font size=+0>#else</font>
<p><font size=+0>inline _syscall1(int, hello, char *, str);</font>
<p><font size=+0>#endif</font>
<p><font size=+0><font face="宋体">这样就可以使用系统调用</font>hello<font face="宋体">了</font></font>
<p><font face="宋体"><font size=+0> </font></font>
<br>&nbsp;
<p><font face="宋体"><font size=+0> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<a href="#title">Back</a></font></font>
<p><font face="宋体"><font size=+0> </font></font></dir>
</dir>
</dir>
</dir>
</dir>

<ul>
<li>
<a NAME="c2"></a><b><font size=+1>Linux<font face="宋体">中的系统调用</font></font></b></li>
</ul>

<ol><b>1. 进程相关的系统调用</b>
<br><b><font size=+0>Fork &amp; vfork &amp; clone</font></b>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在</font>Linux<font face="宋体">中被抽象成各种数据对象:进程控制块、虚存空间、文件系统,文件</font>I/O<font face="宋体">、信号处理函数。所以创建一个进程的过程就是这些数据对象的创建过程。</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 在调用系统调用</font>fork<font face="宋体">创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:</font>pipe<font face="宋体">,</font>fifo<font face="宋体">,</font>System
V IPC<font face="宋体">机制等,另外通过</font>fork<font face="宋体">创建子进程系统开销很大,需要将上面描述的每种资源都复制一个副本。这样看来,</font>fork<font face="宋体">是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程</font>fork<font face="宋体">出一个子进程后,其子进程仅仅是为了调用</font>exec<font face="宋体">执行另一个执行文件,那么在</font>fork<font face="宋体">过程中对于虚存空间的复制将是一个多余的过程(由于</font>Linux<font face="宋体">中是采取了</font>copy-on-write<font face="宋体">技术,所以这一步骤的所做的工作只是虚存管理部分的复制以及页表的创建,而并没有包括物理也面的拷贝);另外,有时一个进程中具有几个独立的计算单元,可以在相同的地址空间上基本无冲突进行运算,但是为了把这些计算单元分配到不同的处理器上,需要创建几个子进程,然后各个子进程分别计算最后通过一定的进程间通讯和同步机制把计算结果汇总,这样做往往有许多格外的开销,而且这种开销有时足以抵消并行计算带来的好处。</font></font>
<p><font face="宋体"><font size=+0> </font></font><img SRC="a4.gif" height=351 width=479>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 这说明了把计算单元抽象到进程上是不充分的,这也就是许多系统中都引入了线程的概念的原因。在讲述线程前首先介绍以下</font>vfork<font face="宋体">系统调用,</font>vfork<font face="宋体">系统调用不同于</font>fork<font face="宋体">,用</font>vfork<font face="宋体">创建的子进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,子进程对虚拟地址空间任何数据的修改同样为父进程所见。但是用</font>vfork<font face="宋体">创建子进程后,父进程会被阻塞直到子进程调用</font>exec<font face="宋体">或</font>exit<font face="宋体">。这样的好处是在子进程被创建后仅仅是为了调用</font>exec<font face="宋体">执行另一个程序时,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的,通过</font>vfork<font face="宋体">可以减少不必要的开销。</font></font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 在</font>Linux<font face="宋体">中,</font>
fork<font face="宋体">和</font>vfork<font face="宋体">都是调用同一个核心函数</font></font>
<p><font size=+0>&nbsp;&nbsp;&nbsp; do_fork(unsigned long clone_flag, unsigned
long usp, struct pt_regs)</font>
<p><font size=+0><font face="宋体">&nbsp;&nbsp;&nbsp; 其中</font>clone_flag<font face="宋体">包括</font>CLONE_VM,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -