`

Linux C中库函数与系统调用的区别

阅读更多

从程序完成的功能来看,函数库提供的函数通常是不需要操作系统的服务,函数是在用户空间内执行的,除非函数涉及到I/O操作等,一般是不会切到核心态的。

 

这样程序员可以只要写一次代码就能够在不同版本的linux系统间使用积压种具体实现完全不同的系统调用。

至于如何实现对不同的系统调用的兼容性问题,那是函数库开发者所关心的问题。

程序执行效率来看,系统调用的执行效率大多要比函数高,尤其是处理输入输出的函数。

当处理的数据量比较小时,函数库的函数执行效率可能比较好,因为函数库的作法是将要处理的数据先存入缓冲区内,等到缓冲区装满了,再将数据一次写入或者读出。

这种方式处理小量数据时效率比较高,但是在进行系统调用时,因为用户进程从用户模式进入系统核心模式,中间涉及了许多额外的任务的切换工作,这些操作称为上下文切换,此类的额外工作会影响系统的执行效率。

但是当要处理的数据量比较大时,例如当输入输出的数据量超过文件系统定义的尽寸时,利用系统调用可获得较高的效率。

 

系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。

 

Linux 下三种发生系统调用的方法:

glibc 提供的库函数

glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API(Application Programming Interface),除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。

 

glibc提供的系统调用API与内核特定的系统调用之间的关系

通常情况,每个特定的系统调用对应了至少一个 glibc 封装的库函数,

如系统提供的打开文件系统调用 sys_open 对应的是 glibc 中的 open 函数;

其次,glibc 一个单独的 API 可能调用多个系统调用,

如 glibc 提供的 printf 函数就会调用如 sys_open、sys_mmap、sys_write、sys_close 等等系统调用;

另外,多个 API 也可能只对应同一个系统调用,

如glibc 下实现的 malloc、calloc、free 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。

 

举例来说,我们通过 glibc 提供的chmod 函数来改变文件 etc/passwd 的属性为 444:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>

int main()
{
        int rc;

        rc = chmod("/etc/passwd", 0444);
        if (rc == -1)
                fprintf(stderr, "chmod failed, errno = %d\n", errno);
        else
                printf("chmod success!\n");
        return 0;
}

在普通用户下编译运用,输出结果为:

chmod failed, errno = 1

上面系统调用返回的值为-1,说明系统调用失败,错误码为1,

在 /usr/include/asm-generic/errno-base.h 文件中有如下错误代码说明:

#define EPERM       1                /* Operation not permitted */

即无权限进行该操作,我们以普通用户权限是无法修改 /etc/passwd 文件的属性的,结果正确。

 

syscall 直接调用

使用上面的方法有很多好处,首先你无须知道更多的细节,如 chmod 系统调用号,你只需了解 glibc 提供的 API 的原型;其次,该方法具有更好的移植性,你可以很轻松将该程序移植到其他平台,或者将 glibc 库换成其它库,程序只需做少量改动。

但有点不足是,如果 glibc 没有封装某个内核提供的系统调用时,我就没办法通过上面的方法来调用该系统调用。如我自己通过编译内核增加了一个系统调用,这时 glibc 不可能有你新增系统调用的封装 API,此时我们可以利用 glibc 提供的syscall 函数直接调用。

该函数定义在 unistd.h 头文件中,函数原型如下:

long int syscall (long int sysno, ...)

sysno 是系统调用号,每个系统调用都有唯一的系统调用号来标识。

在 sys/syscall.h 中有所有可能的系统调用号的宏定义。

... 为剩余可变长的参数,为系统调用所带的参数,根据系统调用的不同,可带0~5个不等的参数,如果超过特定系统调用能带的参数,多余的参数被忽略。

返回值 该函数返回值为特定系统调用的返回值,在系统调用成功之后你可以将该返回值转化为特定的类型,如果系统调用失败则返回 -1,错误代码存放在 errno 中。

还以上面修改 /etc/passwd 文件的属性为例,这次使用 syscall 直接调用:

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
        int rc;
        rc = syscall(SYS_chmod, "/etc/passwd", 0444);

        if (rc == -1)
                fprintf(stderr, "chmod failed, errno = %d\n", errno);
        else
                printf("chmod succeess!\n");
        return 0;
}

 在普通用户下编译执行,输出的结果与上例相同。

通过 int 指令陷入内核态

如果我们知道系统调用的整个过程的话,应该就能知道用户态程序通过软中断指令int 0x80 来陷入内核态(在Intel Pentium II 又引入了sysenter指令),参数的传递是通过寄存器,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递最多五个参数,当系统调用返回时,返回值存放在 eax 中。

 

仍然以上面的修改文件属性为例,将调用系统调用那段写成内联汇编代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
        long rc;
        char *file_name = "/etc/passwd";
        unsigned short mode = 0444;

        asm(
                "int $0x80"
                : "=a" (rc)
                : "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
        );

        if ((unsigned long)rc >= (unsigned long)-132) {
                errno = -rc;
                rc = -1;
        }

        if (rc == -1)
                fprintf(stderr, "chmode failed, errno = %d\n", errno);
        else
                printf("success!\n");

        return 0;
}

 如果 eax 寄存器存放的返回值(存放在变量 rc 中)在 -1~-132 之间,就必须要解释为出错码(在/usr/include/asm-generic/errno.h 文件中定义的最大出错码为 132),这时,将错误码写入 errno 中,置系统调用返回值为 -1;否则返回的是 eax 中的值。

分享到:
评论

相关推荐

    Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用.zip

    Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用、Debian(Ubuntu、Deepin)操作系统爬坑.zip Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用、...

    Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用、Debian系统爬坑.zip

    Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用、Debian(Ubuntu、Deepin)操作系统爬坑.zip Linux命令行与shell脚本编程、Linux C库函数及系统调用编程、Linux内核分析即应用、...

    _Linux命令行与shell脚本编程、Linux_C库函数及系统调用编程、Linux内核分析即应_Linux.zip

    _Linux命令行与shell脚本编程、Linux_C库函数及系统调用编程、Linux内核分析即应_Linux

    Linux C中库函数与系统调用的区别详细解析

    在Linux C编程中,库函数和系统调用是两个关键的概念,它们在程序设计和执行过程中扮演着不同的角色。理解它们之间的差异对于优化代码性能和提高程序的可移植性至关重要。 首先,库函数是程序员在用户空间中可以...

    linux c的库函数大全

    在Linux系统中,C语言是基础且强大的编程语言,它为开发者提供了丰富的库函数来实现各种功能。"Linux C的库函数大全"是一份宝贵的资源,涵盖了开发过程中可能用到的众多函数,对于Linux下的C语言开发人员来说,是不...

    Linux C语言编程库函数说明(英文)

    这份手册详细介绍了C库函数,包括系统调用,这些函数是编写Linux程序的核心元素。通过阅读这份PDF文档,开发者可以了解到如何利用C语言来实现各种功能,如文件操作、进程控制、网络通信、内存管理等。 文件"libc....

    Linux下C库函数大全

    在C语言编程中,标准库函数提供了一组广泛的工具,用于处理数据、文件操作、内存管理、数学计算等任务,而Linux下的C库函数则是在这个基础上对Linux操作系统的系统调用进行了封装,使得开发者能够更加方便地进行系统...

    Linux下通过系统调用(API)和C语言库函数对文件进行访问

    总的来说,Linux下的文件访问涉及系统调用和C语言库函数两大部分。了解并掌握这两者,能够帮助开发者更高效地进行文件操作,从而编写出稳定、高效的程序。在实际工作中,需要根据具体项目需求和性能要求来灵活选择...

    最全Linux C 库函数官方中文API手册

    Linux C库函数是开发Linux系统应用的基础,它们提供了一系列接口,让程序员能够高效地与操作系统进行交互。这份“最全Linux C库函数官方中文API手册”涵盖了从基本的数据类型定义、内存管理到输入/输出、文件操作、...

    Linux C 常用库函数手册.chm

    《Linux C 常用库函数手册》是程序员在Linux环境下使用C语言开发时的重要参考资料。这份手册详细列举了Linux C编程中常见的库函数,并提供了详细的使用方法和示例,帮助开发者快速理解和应用这些函数。以下是一些...

    Linux常用系统调用手册

    此外,Linux系统调用通常与C语言的库函数结合使用,如`stdio.h`中的`printf()`和`scanf()`,`stdlib.h`中的`malloc()`和`free()`,以及`string.h`中的`strcpy()`和`strlen()`。库函数是程序员日常工作中最常用的工具...

    Linux库函数大全(C语言)

    在Linux系统中,C语言是核心开发语言,其丰富的库函数是进行系统级编程和应用开发的重要工具。本文将深入探讨Linux库函数,主要涵盖内存操作、文件操作和字符串操作这三个方面,帮助开发者更好地理解和利用这些功能...

    Linux c 库函数大全

    本资源“Linux c 库函数大全”显然是一个针对Linux环境下C语言库函数的全面指南,它可能包含了标准C库(glibc)、POSIX扩展以及其他特定于Linux的库函数的详细信息。 C库函数是C语言编程的重要组成部分,它们提供了...

    linux 常用 c 库函数

    在Linux环境下进行C语言编程时,常常需要使用到各种库函数来实现系统级的功能调用。这些库函数涵盖了从基本的数据处理到复杂的系统交互等多个方面。以下是对标题和描述中涉及的一些重要知识点的详细说明: 1. **...

    linux c库函数源代码 全部库函数的实现源码

    Linux C库函数源代码是了解C语言在操作系统层面如何工作的宝贵资源,特别是对于开发者和系统程序员来说,深入理解这些库函数的实现可以帮助他们优化代码、解决底层问题以及增强对计算机科学原理的理解。在这个主题中...

    LINUX的系统调用与函数调用.pdf

    本文主要探讨了Linux系统调用和函数调用的区别以及它们在系统中的作用。 首先,系统调用是操作系统为应用程序提供的一种直接与内核交互的机制。它允许用户空间的程序请求操作系统执行特定的服务,如文件操作、进程...

    Linux C语言库函数原代码

    本文将深入探讨“Linux C语言库函数原代码”这一主题,分析C库函数的源码,以及它们在Linux环境中的应用。 首先,我们要理解C库函数是C编程的基础,它们是一系列预先编译好的函数集合,为开发者提供了诸如输入/输出...

    c语言标准库函数源码

    glibc是GNU项目开发的C语言标准库,它是Linux系统中最常用的C库之一。glibc-2.25是该库的一个版本,包含了大量C语言标准库函数的实现。在分析源码之前,我们需要了解C语言标准库的基本结构和主要功能模块。 1. 输入...

    Linux C常用库函数手册

    在Linux系统中,C语言是基础且强大的编程工具,它为开发者提供了丰富的库函数来实现各种功能。本手册“Linux C常用库函数手册”聚焦于这些常用的库函数,旨在帮助开发者快速查阅和理解相关API,从而提高开发效率。...

Global site tag (gtag.js) - Google Analytics