2019-07-29 09:59:57 qq_42685292 阅读数 1560

系统调用

UNIX/Linux系统绝大部分功能都是通过系统调用实现,比如:open/close…
UNIX/Linux把系统调用都封装成了C函数的形式,但他们并不是标准C的一部分。

一切皆文件

在UNIX/Linux系统下,几乎所有资源都是以文件形式提供了,所以在UNIX/Linux系统下一切皆文件,操作系统把它的服务、功能、设备抽象成简单的文件,提供一套简单统一的接口,这样程序就可以像访问磁盘上的文件一样访问串口、终端、打印机、网络等功能。
大多数情况下只需要 open/read/write/ioctl/close 就可以实现对各种设备的输入、输出、设置、控制等。 UNIX/Linux下几乎任何对象都可以当作特殊类型的文件,可以以文件的形式访问。

文件相关系统调用

open 打开或创建文件
creat 创建文件
close 关闭文件
read 读文件
write 写文件
lseek 设置文件读写位置
unlink 删除链接
remove 删除文件

(1) open()

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);

功能:打开文件

返回值:文件描述符

参数:

pathname:文件的路径
flags:打开的权限

O_RDONLY, 只读
O_WRONLY, 只写
O_RDWR,读写
O_NOCTTY, 当打开的是终端设备文件,不要把该文件当作主控终端。
O_TRUNC,清空
O_APPEND,追加

creat()

int creat (const char *pathname, int flags, mode_t mode);

功能:创建文件

pathname:文件的路径

flags:打开的权限

O_CREAT, 文件不存在则创建
O_EXCL,如果文件存在,则创建失败

mode:设置文件的权限

S_IRWXU 00700 read,write and execute permission
S_IRUSR00400 user has read permission
S_IWUSR 00200 user has writepermission
S_IXUSR 00100 user has execute permission
S_IRWXG 00070 read,write and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others have read, write and execute permission S_IROTH 00004 others have read permission
S_IWOTH 00002 others have writepermission
S_IXOTH 00001 others have execute permission

read()/write()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

功能:从文件中读取数据到内存

fd:文件描述符,open函数的返回值

buf:数据的存储位置 count:读取的字节数

返回值:成功读取到的字节数

ssize_t write(int fd,const void *buf, size_t count);

功能:把数据写入到文件

fd:文件描述符,open函数的返回值

buf:要写入的数据内存首地址

count:要写入的字节数

返回值:成功写入的字节数

注意:如果把结构体以文本形式写入到文件,需要先把结构体转换成字符串。

2012-09-07 11:44:47 sunlight664 阅读数 800

UNIX的系统调用与库过程几乎是一一对应的。接口遵循POSIX标准,共约100个过程调用。

Windows的系统调用与库过程几乎是不对应的。微软自己定义了一套Win32 API,共有数千个。且API中不一定都是系统调用,还包含一些完全运行在用户态的调用;并且,在不同时间及版本里,API的数目也有所增加。微软保证,随时间流逝,Win32过程将保持稳定,但我们必须清楚,里面不是所有都是系统调用(即陷入内核中)。

2017-03-05 19:01:54 pk_20140716 阅读数 751

系统调用(system call)是操作系统提供给程序调用的接口,程序调用系统调用完成所需的系统任务。
系统调用类似于我们平常使用的工具包。当你需要钳子时,你就从工具包中拿出钳子使用。当你需要老虎钳时,你就从工具包中拿出老虎钳使用。当你需要螺丝刀,你就从工具包中拿出螺丝刀使用……系统调用也是同样的原理,当你需要获取当前时间时,你可以调用获取当前时间的系统调用。当你需要创建文件时,你可以调用创建文件的系统调用。系统调用提供了许多不同的功能,你可以调用它们,让操作系统帮你完成所需的功能。

获取时间
#include <stdio.h>
#include <time.h>

int main(int argc,char **argv)
{
    long tm=time(NULL);
    printf("time = %d \n",tm):
    return 0;
}

在明白系统调用的用处后,应该对系统调用有更深入的了解。
操作系统在运行每个程序时,都会为运行的程序提供4G的内存空间。(32位系统下。这里的内存空间指的是虚拟内存空间,不是实际的物理内存空间。虚拟内存将在以后讲解。这里暂时可以认为操作系统提供了4G物理内存给程序。)4G内存空间中0-3G属于用户程序,3G以上属于操作系统内核。3G以上空间存放了 内核的代码与数据。这其中包含了一系列”特殊”的函数。如图一。

这里写图片描述

这些”特殊”的函数是系统调用对应的内核函数。系统调用被调用后,操作系统将执行系统调用对应的内核函数,也就是说系统调用的实现是由内核函数完成的。系统调用实质上就是保存在内核中的函数。0-3G的内存空间保存了用户程序的代码与数据。用户程序可以执行特殊的机器指令,切入内核,执行系统调用对应的内核函数。一般该指令是int 0x80。内核执行完成后,将返回用户空间,继续执行下一条指令。如图二。

这里写图片描述

2015-07-30 10:41:21 maopaopao2087 阅读数 338

系统调用:System calls 

库函数: Library Functions

系统调用是内核提供的接口(entry points),库函数是对系统调用接口的封装。

Version 7 :   50个。

4.4BSD: 110个。

SVR4:   120个。

Linux:    根据版本不同,有大概240~260个系统调用。

Free BSD:    320个。


系统调用(System calls)函数放在man 文档的2区。库函数放在man 文档的3区。

2017-03-13 20:20:56 pk_20140716 阅读数 164

系统调用

每个程序在运行时都可以调用一系列的特殊“函数”。这些特殊的“函数”能够完成各种不同的系统任务——获取当前时间,打开读写文件等。这些特殊的函数有一个特殊的名字——系统调用。

调用系统调用

系统调用不同于普通函数,调用过程十分特殊。(以下讨论基于linux系统)其调用过程是:一 设置系统调用号。每个系统调用都有一个独特的系统调用号。通过系统调用号可以在几百个系统调用中确定系统调用。设置系统调用号要用到EAX寄存器。当调用open系统调用时,便是置EAX寄存器为5。movl %eax,$5。二 设置参数。系统调用都有参数。参数从0个到6个不等。每个参数都对应一个寄存器。参数一对应EBX,参数二对应ECX,参数三对应EDX,参数四对应ESI,参数五对应EDI,参数六对应EBP。有几个参数便设置几个寄存器。三 执行int 0x80。执行int 0x80指令,程序将切入内核执行,执行完成后返回继续执行int 0x80后一条指令。四 取返回值。返回值放在EAX寄存器中。从EAX中取出返回值,判断系统调用执行情况。

系统调用的封装

明白系统调用的调用过程后,可以简单对系统调用的调用过程进行封装。
以下为一段系统调用的封装代码。

#ifndef _SYSCALL_SYSDEP_H
#define _SYSCALL_SYSDEP_H

//使用宏生成函数

#define __syscall_return(type, res) \
do { \
    if ((unsigned long)(res) >= (unsigned long)(-4095)) { \
        errno = -(res); \
        res = -1; \
    } \
    return (type) (res); \
} while (0)


#define _syscall0(syscall_name,type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name)); \
__syscall_return(type,__res); \
}

#define _syscall1(syscall_name,type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1))); \
__syscall_return(type,__res); \
}

#define _syscall2(syscall_name,type,name,type1,arg1,type2,arg2) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
__syscall_return(type,__res); \
}

#define _syscall3(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
          "d" ((long)(arg3))); \
__syscall_return(type,__res); \
}

#define _syscall4(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4))); \
__syscall_return(type,__res); \
} 

#define _syscall5(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
      type5,arg5) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \
__syscall_return(type,__res); \
}

#define _syscall6(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
      type5,arg5,type6,arg6) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \
{ \
long __res; \
__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \
    : "=a" (__res) \
    : "i" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \
      "0" ((long)(arg6))); \
__syscall_return(type,__res); \
}

#endif

_syscall0用于封装参数为0的系统调用;_syscall1用于封装参数为1的系统调用,以此类推。
我们以_syscall6为例来说明代码是如何封装系统调用的。
_syscall6是一个宏定义,其功能用于构建一个函数,函数名字是name,返回值类型为type。函数有6个参数,分别为arg1,arg2,arg3,arg4,arg5,arg6。参数类型分别是type1,type2,type3,type4,type5,type6。构建的函数为:

type name(type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6)
{
    ...
}

函数内是嵌入式汇编代码。

__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \
    : "=a" (__res) \
    : "i" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \
      "0" ((long)(arg6))); \

3-5行是执行汇编语句前的操作,2行是执行汇编语句后的操作。1行是执行的汇编语句。

3-5行中”b” ((long)(arg1))指将arg1的值放入EBX寄存器中,b对应EBX寄存器。”c” ((long)(arg2))指将arg2的值放入ECX寄存器中,c对应ECX寄存器。”d” ((long)(arg3))指将arg3的值放入EDX寄存器中,d对应EDX寄存器。”S” ((long)(arg4))指将arg4的值放入ESI寄存器中,S对应ESI寄存器。”D” ((long)(arg5))指将arg5的值放入EDI寄存器中,D对应EDI寄存器。”0” ((long)(arg6))指将arg6的值放入EAX寄存器中,0对应EAX寄存器(”=a”是0个出现的寄存器,a指EAX寄存器)。

第1行中指令将EAX寄存器的值放入EBP寄存器中,并置EAX的值为系统调用号。其中movl %1,%%eax指将_NR##syscall_name的值放入EAX中。##把参数变成字符串,_NR和syscall_name为##的参数。若syscall_name为open,则其_NR##syscall_name为__NR_open。__NR_open是定义在内核头文件unistd.h文件中的宏,对应数字5。参数设置完毕,执行int 0x80正式切入内核执行系统调用。
第2行”=a” (__res)指将EAX寄存器的值赋值给__res。也就是将系统调用的返回值给__res。

__syscall_return(type,__res)也是宏定义,它主要用于判断系统调用执行是否出错。若出错则置errno为-res,并返回-1。判断出错的方式是判断res作为无符号长整型是否大于-4095。若大于则出错。这是linux内核做出的规定。

unix系统调用(转载)

阅读数 3322

没有更多推荐了,返回首页