• 《Unix系统编程手册》第三章系统编程概念学习


    系统调用

    系统调用是内核提供的受控的内核入口,通过API的形式,内核提供了一系列服务供程序调用。
    系统调用基本特点:
    1、系统调用会从用户态切换到核心态,以便CPU访问受到保护的内核内存;
    2、系统调用的组成是固定的,每个系统调用都由唯一的数字来标识(程序通过名称标识系统调用);
    3、每个系统调用可以使用一套参数,对用户空间和内核空间传递的信息进行规范。

    系统调用执行的基本步骤:
    1、应用程序通过调用包装后的库函数发起系统调用请求;
    2、API函数需要保证传入的参数可用,然后将参数传入到寄存器中;
    3、由于系统调用进入内核的方式相同,所以内核需要区分每个系统调用,对此,内核会将系统调用的编号复制到特殊的CPU寄存器中;
    4、API函数执行一条中断指令,将用户态切换到核心态,并执行中断所指向的代码;
    5、为了响应中断请求,内核会调用system_call()例程来处理中断
    6、若系统调用执行结果表明执行出错,API函数会设置errno。

    处理中断过程:
    1、在内核栈中保存寄存器值;
    2、审核系统调用编号的有效性;
    3、以系统调用编号对存放的所有调用服务历程的列表进行索引,并调用相应的系统调用服务例程,若有参数,将先检验参数的有效性

    处理来自系统调用和库函数的错误

    几乎每个系统调用和库函数都会返回某类状态值,用于表明调用成功与否。少数系统函数在调用时不会失败,例如getpid()总能成功返回进程ID,而_exit()总能终止进行,无需对此系统调用的返回值进行检测。
    每个系统调用的手册页记录有调用可能的返回值,并指明了哪些值表示错误。系统调用失败时,会将全局整型变量errno设置为一个正值,以标识具体的错误。程序应包含#include头文件。
    如果调用系统和库函数成功,errno绝不会被重置为0,如果errno不为0,可能是之前调用失败造成的。因此在错误检查时,必须坚持首先检查函数的返回值是否表明调用出错,然后再检查errno确定错误原因。此外,少数系统调用在调用成功后,也会返回-1。因此要判断此类系统调用是否发生错误,应在调用前将errno置为0,并在调用后对其检查

    解析数值型命令行参数的函数

    #include "tlpi_hdr.h"
    
    int getInt(const char *arg, int flags, const char* name);
    long getLong(const char *arg, int flags, const char* name);
    
    • 1
    • 2
    • 3
    • 4

    与atoi()、atol()和strtol()相比,它们针对数值型参数提供了一些基本的有效性检查。它们会将arg指向的字符串转换成int或long,如果不是一个有效的字符串,那么它们会打印一条错误并终止程序。如果name非空,则将该字符串用于标识arg对应于命令行中相应参数的名称,即如果出现错误,该字符串会出现在打印的错误信息中。函数实现如下:

    #include
    #include
    #include
    #include
    #include
    
    static void gnFail(const char* fname, const char* msg, const char* arg, const char* name)
    {
    	fprintf(stderr, "%s error", fname);
    	if(name)fprintf(stderr, " (in %s)", name);
    	fprintf(stderr, ": %s\n", msg);
    	if(arg && *arg != '\0')
    		fprintf(stderr, "   offending text: %s\n", arg);
    	
    	exit(EXIT_FAILURE);
    }
    
    static long getNum(const char* fname, const char* arg, int flags, const char* name)
    {
    	if(!arg ||*arg == '\0')
    		gnFail(fname, "null or empty string", arg, name);
    	int base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 : (flags & GN_BASE_16) ? 16 : 10;
    	errno = 0;
    	char* endptr;
    	long res = strtol(arg, &endptr, base);
    	
    	if(errno != 0)
    		gnFail(fname, "strtol() failed", arg, name);
    	
    	if(*endptr != '\0')
    		gnFail(fname, "nonnumeris characters", arg, name);
    	
    	if((flags & GN_NONNEG) && res < 0)
    		gnFail(fname, "negative value not allowed", arg, name);
    	
    	if((flags & GN_GT_0) && res <= 0)
    		gnFail(fname, "value must be > 0", arg, name);
    	
    	return res;
    }
    
    long getLong(const char* arg, int flags, const char* name)
    {
    	return getNum("getLong", arg, flags, name);
    }
    
    int getInt(const char* arg, int flags, const char* name)
    {
    	long res = getNum("getIng", arg, flags, name);
    	if(res >INT_MAX || res < INT_MIN)
    		gnFail("getInt", "integer out of rangs", arg, name);
    	
    	return (int)res;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    总结

    系统调用允许进程向内核请求服务,与用户空间的函数调用相比,哪怕最简单的系统调用都会产生显著的开销,其原因是在执行系统调用时,系统需要切换到核心态。此外内核还需要验证系统调用的参数、用户内存以及内存之间也有数据需要传递

    练习

    3-1 使用Linux专有的reboot()系统调用重启系统时,必须将第二个参数magic2定义为一组magic号。这些magic号有什么意义?
    答:没太懂就去查了一下,参考链接见参考第二条。

    include/linux/reboot.h:#define LINUX_REBOOT_MAGIC1  0xfee1dead 
    include/linux/reboot.h:#define LINUX_REBOOT_MAGIC2  672274793 
    include/linux/reboot.h:#define LINUX_REBOOT_MAGIC2A 85072278 
    include/linux/reboot.h:#define LINUX_REBOOT_MAGIC2B 369367448 
    include/linux/reboot.h:#define LINUX_REBOOT_MAGIC2C 537993216 
    Here are the answers. 
    0xfee1dead ="Feel Dead"
    
    672274793=0x28121969 28/12/1969 is when Linus Torvalds was born. 
    
    These are Linus' daughters' birthdays: 
    85072278= 0x5121996     05/12/1996 is when Patricia Miranda was born. 
    367369448=0x16041998 16/04/1998 is when Daniela Yolanda was born. 
    537993216=0x20122000 20/12/2000 is when Celeste Amanda was born
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    参考

    1、《Unix系统编程手册》上篇
    2、https://blog.csdn.net/JohnnyHu90/article/details/50877322

  • 相关阅读:
    不讲武德!为击破苹果的“隐私高墙”,谷歌、Facebook 竟然“二打一”?
    Kubernetes 笔记 / 入门 / 生产环境 / 容器运行时
    loT行业生死竞速:Aqara绿米得用户得天下
    【JavaSE】继承
    Python数据类型的相互转换
    【PAT B-1038】统计同成绩学生
    Dynamics 365Online ApplicationUser创建方式变更
    RSIC-V指令及介绍(1)
    Oracle/PLSQL: NANVL Function
    Windows原理深入学习系列-Windows内核提权
  • 原文地址:https://blog.csdn.net/i_actor/article/details/132698658