linux入门到精通-第八章-系统调用

参考

视频教程

简介

什么是系统调用

系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊“接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。
从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交写的接口一一它好比一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将外理结果送回给用户空间。
在这里插入图片描述

系统服务之所以需要通过系统调用来提供给用户空间的根本原因是为了对系统进行“保护”。因为我们知道Linux 的运行空间分为内核空间与用户空间,它们各自运行在不同的级别中,逻辑上相互隔离。

所以用户进程在通常情况下不允许访问内核据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。

比如我们熟悉的“hello world”程序(执行时)就是标准的用户空间进程,它使用的打印函数printf就属于用于用户空间数据户空间函数,打印的字符“hello word”字符串也属于用户空间数据。

但是很多情况下,用户进程需要获得系统服务(调用系统程序),这时就必须利用系统提供给用户的“特殊接口”一一系统调用了,它的特殊性主要在于规定了用户进程进入内核的县体位置

换句话说,用户访问内核的路径是事先规定的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径限制才能保障内核安全无误。我们可以形象地描述这种机制:作为一个游客,你可以买票要求进入野生动物园,但你必须老老实实坐在观光车上,按照规定的路线观光游览。当然,不准下车,因为那样太危险,不是让你丢掉小命,就是让你吓坏了野生动物。

系统调用的实现

系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU 可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?

答案是软件中断。软件中断和我们常说的中断(硬件中断)不同之处在于,它是通过软件指令触发而并非外设应发的中断,也就是说,又是编程人员开发出的一种异常(该异常为正常的异常)。操作系统一般是通过软件中断从用户态切换到内核态。

系统调用和库函数的区别

Linux 下对文件操作有两种方式:系统调用 (system call)库函数调用 (Library functions)
库函数由两类函数组成

  • 1)、不需要调用系统调用
    不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,如strcpy、bzero 等字符串操作函数。
  • 2)、需要调用系统调用
    需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、fread等。

在这里插入图片描述

C库中IO函数的工作流程

在这里插入图片描述

硬盘为什么慢?
大部分硬盘都是机械硬盘,读取寻道时间和写入寻道时间都是是在毫秒级(ms)
相对来说内存读写速度都非常快,因为内存属于电子设务,读写速度是纳秒(ns) 级别的
1 秒 = 1000毫秘(ms)
1 秒 = 1000,000 微秒(us)
1 秒 = 1 000,000,000 纳秒(ns)
两者相差一百万倍 ! ! !
寄存器(空间小) > cpu缓存区 > 内存 > 硬盘

库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用IO系统函数的次数,提高了访问效率。

这个过程类似于快递员给某个区域(内核空间)送快递一样,快递员有两种方式:

  • 1)、来一件快递就马上送到目的地,来一件送一件,这样导致来回走比较频繁 (系统调用)
  • 2)、等快递攒着差不多后(缓冲区),才一次性送到目的地 (库函数调用)

错误处理函数

介绍

errno 是一个int型的值,在errno.h中定义。查看错误代码errno是调errno是调试程序的一个重要方法。

当Linux C api函数发生异常时,一般会将errro全局变量赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因.

示例:test_errno.c

#include <stdio.h>//fopen
#include <errno.h> //errno
#include <string.h> //strerror(errno)
// errno是一个全局变量,在errno.h文件中定义
// errno是保存系统最近出错错误码
int main()
{
   
    FILE *fp = fopen("xxxx", "r");
    if (NULL == fp)
    {
   
        //打印错误码
        printf("%d\n", errno); 
        // 把errno的数字转换成相应的文字
        printf("%s\n", strerror(errno)); 
        // 打印错误原因的字符串
        perror("fopen err");
    }

    return 0;
}

运行

# 编译
root@sony-HP-Notebook:/usr/local/cpp_demo/fun# gcc test_errno.c 
# 运行
root@sony-HP-Notebook:/usr/local/cpp_demo/fun# ./a.out 
2
No such file or directory
fopen err: No such file or directory

查看帮助文档

man errno

查看错误号

cat /usr/include/asm-generic/errno-base.h
cat /usr/include/asm-generic/errno.h

在这里插入图片描述

虚拟地址空间

每个进程都会分配虚拟地址空间,在32位机器上,该地址空间为4G。

2^32 b  = 2^22 Kb = 2^12 Mb  =  2^2 Gb 

在这里插入图片描述
在进程里平时所说的指针变量,保存的就是虚拟地址。当应用程序使用虚拟地址访问内存时,处理器 (CPU) 会将其转化成物理地址 (MMU)

MMU:将虚拟的地址转化为物理地址.这样做的好处在于

  • 进程隔离,更好的保护系统安全运行
  • 屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

相关推荐

  1. Linux入门精通14 传输文件)

    2023-12-13 08:20:05       33 阅读
  2. WPF入门跪下 动画-基础

    2023-12-13 08:20:05       28 阅读
  3. Python从入门精通秘籍

    2023-12-13 08:20:05       20 阅读
  4. Linux入门精通

    2023-12-13 08:20:05       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-13 08:20:05       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-13 08:20:05       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-13 08:20:05       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-13 08:20:05       20 阅读

热门阅读

  1. PostgreSql 序列

    2023-12-13 08:20:05       27 阅读
  2. elasticsearch 基础语法

    2023-12-13 08:20:05       39 阅读
  3. (第27天)Oracle 数据泵转换分区表

    2023-12-13 08:20:05       40 阅读
  4. 使用NanoPi NEO4进行rtsp拉流

    2023-12-13 08:20:05       40 阅读
  5. (第30天)Oracle 数据泵常用技巧总结

    2023-12-13 08:20:05       27 阅读
  6. 无重复字符的最长子串(LeetCode 3)

    2023-12-13 08:20:05       50 阅读