OPERATING SYSTEMS -LINUX SYSTEM CALLS 1 龚玲 lgong@sjtu.edu.cn
OPERATING SYSTEMS -LINUX SYSTEM CALLS 龚玲 lgong@sjtu.edu.cn 1
什么是系统调用? o Linux内核中设置了一组用于实现各种系统功能的子程序 称为系统调用。用户可以通过系统调用命令在自己的 应用程序中调用它们。从某种角度来看,系统调用和普 通的函数调用非常相似。区别仅仅在于,系统调用由操 作系统核心提供,运行于核心态;而普通的函数调用由 函数库或用户自己提供,运行于用户态。 0 随Linux核心还提供了一些C语言函数库,这些库对系统 调用进行了一些包装和扩展,因为这些库函数与系统调 用的关系非常紧密,所以习惯上把这些函数也称为系统 调用
什么是系统调用? Linux内核中设置了一组用于实现各种系统功能的子程序 ,称为系统调用。用户可以通过系统调用命令在自己的 应用程序中调用它们。从某种角度来看,系统调用和普 通的函数调用非常相似。区别仅仅在于,系统调用由操 作系统核心提供,运行于核心态;而普通的函数调用由 函数库或用户自己提供,运行于用户态。 随Linux核心还提供了一些C语言函数库,这些库对系统 调用进行了一些包装和扩展,因为这些库函数与系统调 用的关系非常紧密,所以习惯上把这些函数也称为系统 调用
为什么要用系统调用? 实际上,很多已经被我们习以为常的C语言标准函数,在 Linux平台上的实现都是靠系统调用完成的,所以如果想对 系统底层的原理作深入的了解,掌握各种系统调用是初步的 要求。进一步,若想成为一名Linux下编程高手,也就是我 们常说的Hacker,其标志之一也是能对各种系统调用有透彻 的了解
为什么要用系统调用? 实际上,很多已经被我们习以为常的C语言标准函数,在 Linux平台上的实现都是靠系统调用完成的,所以如果想对 系统底层的原理作深入的了解,掌握各种系统调用是初步的 要求。进一步,若想成为一名Linux下编程高手,也就是我 们常说的Hacker,其标志之一也是能对各种系统调用有透彻 的了解
系统调用是怎么工作的? 一 般的,进程是不能访问内核的。它不能访问内核所 占内存空间也不能调用内核函数。CPU硬件决定了 这些(这就是为什么它被称作"保护模式")。系统调 用是这些规则的一个例外。其原理是进程先用适当的 值填充寄存器,然后调用一个特殊的指令,这个指令 会跳到一个事先定义的内核中的一个位置(当然,这 个位置是用户进程可读但是不可写的)。在Intel CPU中,这个由中断0x80实现。硬件知道一旦你跳 到这个位置,你就不是在限制模式下运行的用户,而 是作为操作系统的内核-所以你就可以为所欲为
系统调用是怎么工作的? 一般的,进程是不能访问内核的。它不能访问内核所 占内存空间也不能调用内核函数。CPU硬件决定了 这些(这就是为什么它被称作"保护模式")。系统调 用是这些规则的一个例外。其原理是进程先用适当的 值填充寄存器,然后调用一个特殊的指令,这个指令 会跳到一个事先定义的内核中的一个位置(当然,这 个位置是用户进程可读但是不可写的)。在Intel CPU中,这个由中断0x80实现。硬件知道一旦你跳 到这个位置,你就不是在限制模式下运行的用户,而 是作为操作系统的内核--所以你就可以为所欲为
系统调用是怎么工作的? 0 进程可以跳转到的内核位置叫做sysem_call。这个过 程检查系统调用号,这个号码告诉内核进程请求哪种 服务。然后,它查看系统调用表(sys_call_table)找 到所调用的内核函数入口地址。接着,就调用函数, 等返回后,做一些系统检查,最后返回到进程(或到 其他进程,如果这个进程时间用尽)。 如果你希望读 这段代码,它在kernel/entry.S, Entry(system_call)的下一行
系统调用是怎么工作的? 进程可以跳转到的内核位置叫做sysem_call。这个过 程检查系统调用号,这个号码告诉内核进程请求哪种 服务。然后,它查看系统调用表(sys_call_table)找 到所调用的内核函数入口地址。接着,就调用函数, 等返回后,做一些系统检查,最后返回到进程(或到 其他进程,如果这个进程时间用尽)。如果你希望读 这段代码,它在/kernel/entry.S, Entry(system_call)的下一行
ERRNO是什么? 。为防止和正常的返回值混淆,系统调用并不直接返 回错误码,而是将错误码放入一个名为erno的全局 变量中。如果一个系统调用失败,你可以读出errno 的值来确定问题所在。 errno不同数值所代表的错误消息定义在erno.h中 你也可以通过命令"man3 errno"来察看它们。 需要注意的是,errno的值只在函数发生错误时设置 如果函数不发生错误,errno的值就无定义,并不 会被置为0。另外,在处理errnoj前最好先把它的值 存入另一个变量,因为在错误处理过程中,即使像 printf0这样的函数出错时也会改变errno的值。 6
ERRNO是什么? 为防止和正常的返回值混淆,系统调用并不直接返 回错误码,而是将错误码放入一个名为errno的全局 变量中。如果一个系统调用失败,你可以读出errno 的值来确定问题所在。 errno不同数值所代表的错误消息定义在errno.h中 ,你也可以通过命令"man 3 errno"来察看它们。 需要注意的是,errno的值只在函数发生错误时设置 ,如果函数不发生错误,errno的值就无定义,并不 会被置为0。另外,在处理errno前最好先把它的值 存入另一个变量,因为在错误处理过程中,即使像 printf()这样的函数出错时也会改变errno的值。 6
GETPID getpid的作用很简单,就是返回当前进程的进程ID。 0 /getpid_test.c * #include mainO printf("The current process ID is %d\n",getpidO);
GETPID getpid的作用很简单,就是返回当前进程的进程ID。 /* getpid_test.c */ #include main() { printf("The current process ID is %d\n",getpid()); }
0 编译并运行程序getpid_test.c: o Sgcc getpid test.c -o getpid _test o $./getpid test The current process ID is 1980 。(你自己的运行结果很可能与这个数字不一样,这是 很正常的。) 。再运行一遍: o $./getpid test The current process ID is 1981 。正如我们所见,尽管是同一个应用程序,每一次运行 的时候,所分配的进程标识符都不相同
编译并运行程序getpid_test.c: $gcc getpid_test.c -o getpid_test $./getpid_test The current process ID is 1980 (你自己的运行结果很可能与这个数字不一样,这是 很正常的。) 再运行一遍: $./getpid_test The current process ID is 1981 正如我们所见,尽管是同一个应用程序,每一次运行 的时候,所分配的进程标识符都不相同
FORK o#include/*提供类型pidt的定义*/ include/提供函数的定义*/ pid_t fork(void); 。ork系统调用的作用是复制一个进程。当一个进程调 用它,完成后就出现两个几乎一模一样的进程,我们 也由此得到了一个新进程。据说fork的名字就是来源 于这个与叉子的形状颇有几分相似的工作流程。 。在Linux中,创造新进程的方法只有一个,就是我们 正在介绍的fork。其他一些库函数,如system0,看 起来似乎它们也能创建新的进程,如果能看一下它们 的源码就会明白,它们实际上也在内部调用了ok。 包括我们在命令行下运行应用程序,新的进程也是由 shell调用fork制造出来的
FORK #include /* 提供类型pid_t的定义 */ #include /* 提供函数的定义 */ pid_t fork(void); fork系统调用的作用是复制一个进程。当一个进程调 用它,完成后就出现两个几乎一模一样的进程,我们 也由此得到了一个新进程。据说fork的名字就是来源 于这个与叉子的形状颇有几分相似的工作流程。 在Linux中,创造新进程的方法只有一个,就是我们 正在介绍的fork。其他一些库函数,如system(),看 起来似乎它们也能创建新的进程,如果能看一下它们 的源码就会明白,它们实际上也在内部调用了fork。 包括我们在命令行下运行应用程序,新的进程也是由 shell调用fork制造出来的
/fork_test.c * #include #inlcude mainO { pid_t pid; ~此时仅有一个进程*/ pid=forkO; /~此时己经有两个进程在同时运行*/ if(pid<0) printf("error in fork!"); else if(pid==0) printf("I am the child process,my process ID is %d\n",getpid(); else printf("I am the parent process,my process ID is %d\n",getpido);
/* fork_test.c */ #include #inlcude main() { pid_t pid; /*此时仅有一个进程*/ pid=fork(); /*此时已经有两个进程在同时运行*/ if(pid<0) printf("error in fork!"); else if(pid==0) printf("I am the child process, my process ID is %d\n",getpid()); else printf("I am the parent process, my process ID is %d\n",getpid()); }