TLPI-Chapter 3系统编程概念
来源:互联网 发布:淘宝女式大衣 编辑:程序博客网 时间:2024/06/05 07:55
第三章内容概念讲的其实相对好理解,主要有如下几点:
系统调用是可控的内核入口,进程可以请求内核以自己的名义去执行某些动作,这就用到了系统调用,讲处理器从用户态切换到内核态。
在书中作者用到一个例子X86-32为例,按事件发生顺序:
1.应用程序通过外壳(wapper)函数,发起系统调用
2.参数入栈,传入外壳函数。
3.外壳函数将参数置入特定寄存器(包括系统调用编号)
4执行中断机器指令(int 0x80)。
5.内核响应中断指令,调用system_call()里程处理中断。
如何处理中断呢?
在内核栈保存寄存器的值
审核系统调用编号的有效性
通过编号找到相应的系统调用服务例程,调用时会先检查参数的有效性,然后执行任务。结果状态返回给system_call()例程
从内核栈中恢复寄存器的值,将系统调用返回值置于栈中
返回至外壳函数,切换回用户态
6.若系统调用服务例程的返回值表明调用有误,外壳函数会设置errno为对应的错误码.同时返回一个整型值表明系统调用是否成功。
本章的重点是围绕第6步,作者来处理来自函数库的错误,在阅读这些代码前,必须要学习C语言可变参数相关。ANSI C为了提高可移植性,通过头文件stdarg.h提供了一组方便使用可变长参数的宏。
/* * stdarg.h * * Provides facilities for stepping through a list of function arguments of * an unknown number and type. * * NOTE: Gcc should provide stdarg.h, and I believe their version will work * with crtdll. If necessary I think you can replace this with the GCC * stdarg.h. * * Note that the type used in va_arg is supposed to match the actual type * *after default promotions*. Thus, va_arg (..., short) is not valid. * * This file is part of the Mingw32 package. * * Contributors: * Created by Colin Peters <colin@bird.fu.is.saga-u.ac.jp> * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. * * This code is distributed in the hope that it will be useful but * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY * DISCLAMED. This includes but is not limited to warranties of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * $Revision: 1.2 $ * $Author: noer $ * $Date: 1998/10/10 00:51:16 $ * */#ifndef _STDARG_H_#define _STDARG_H_/* * Don't do any of this stuff for the resource compiler. */#ifndef RC_INVOKED/* * I was told that Win NT likes this. */#ifndef _VA_LIST_DEFINED#define _VA_LIST_DEFINED#endif#ifndef _VA_LIST#define _VA_LISTtypedef char* va_list;#endif/* * Amount of space required in an argument list (ie. the stack) for an * argument of type t. */#define __va_argsiz(t) \ (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))/* * Start variable argument list processing by setting AP to point to the * argument after pN. */#ifdef __GNUC__/* * In GNU the stack is not necessarily arranged very neatly in order to * pack shorts and such into a smaller argument list. Fortunately a * neatly arranged version is available through the use of __builtin_next_arg. */#define va_start(ap, pN) \ ((ap) = ((va_list) __builtin_next_arg(pN)))#else/* * For a simple minded compiler this should work (it works in GNU too for * vararg lists that don't follow shorts and such). */#define va_start(ap, pN) \ ((ap) = ((va_list) (&pN) + __va_argsiz(pN)))#endif/* * End processing of variable argument list. In this case we do nothing. */#define va_end(ap) ((void)0)/* * Increment ap to the next argument in the list while returing a * pointer to what ap pointed to first, which is of type t. * * We cast to void* and then to t* because this avoids a warning about * increasing the alignment requirement. */#define va_arg(ap, t) \ (((ap) = (ap) + __va_argsiz(t)), \ *((t*) (void*) ((ap) - __va_argsiz(t))))#endif /* Not RC_INVOKED */#endif /* not _STDARG_H_ */
下面我们分别解析每个具体的函数:
看到源文件中typedef char* va_list; 我们可以知道va_list就是char *类型,揭开神秘面纱第一步.
va函数的优势表现在使用的方便性和易用性上,可以使代码更简洁。C编译器为了统一在不同的硬件架构、硬件平台上的实现,和增加代码的可移植性,提供了一系列宏来屏蔽硬件环境不同带来的差异。
ANSI C标准下,va的宏定义在stdarg.h中,它们有:
va_list,va_start(),va_arg(),va_end()。
这里,移动指针使其指向下一个参数,那么移动指针时的偏移量是多少呢,没有具体答案,因为这里涉及到内存对齐(alignment)问题,内存对齐跟具体使用的硬件平台有密切关系,比如大家熟知的32位x86平台规定所有的变量地址必须是4的倍数(sizeof(int) = 4)。va机制中用宏__va_argsiz(_INTSIZEOF(n))来解决这个问题,没有这些宏,va的可移植性无从谈起。注:依据不同glibc版本,对应的宏名字不同。
/* * Amount of space required in an argument list (ie. the stack) for an * argument of type t. */#define __va_argsiz(t) \(((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
#define va_start(ap, pN) \((ap) = ((va_list) (&pN) + __va_argsiz(pN)))#endif//第一个可选参数地址
/* * Increment ap to the next argument in the list while returing a * pointer to what ap pointed to first, which is of type t. * * We cast to void* and then to t* because this avoids a warning about * increasing the alignment requirement. */#define va_arg(ap, t) \(((ap) = (ap) + __va_argsiz(t)), \*((t*) (void*) ((ap) - __va_argsiz(t))))
同样注释部分也说明了,我们指向list中的下一个参数,返回list开始指向的参数.
参考如下例子:
/* Name: 可变参数 Copyright: 52coder.net Author: 52coder Date: 04/09/17 23:44 Description: 可变参数*/#include <stdio.h>#include <stdarg.h>void print_args(int count, ...);int main(int argc, char* argv[]) { print_args(5,1,2,3,4,5); return 0;}void print_args(int count, ...) { int i, value; va_list arg_ptr; va_start(arg_ptr, count); for(i=0; i<count; i++) { value = va_arg(arg_ptr,int); printf("position %d = %d\n", i+1, value); } va_end(arg_ptr);}
参考例子:
/* Name: 可变参数 Copyright: 52coder.net Author: 52coder Date: 04/09/17 23:44 Description: 可变参数*/#include <stdio.h>#include <stdarg.h>double average(int num,...){ va_list valist; double sum = 0.0; int i; /* 为 num 个参数初始化 valist */ va_start(valist, num); /* 访问所有赋给 valist 的参数 */ for (i = 0; i < num; i++) { sum += va_arg(valist, int); } /* 清理为 valist 保留的内存 */ va_end(valist); return sum/num;}int main(){ printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5)); printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));}
下面这个例子是征服C指针中的一个例子,我个人认为这个例子非常非常的好。代码如下:
#include <stdio.h>#include <stdarg.h>#include <assert.h>void tiny_printf(char * format,...){ int i; va_list ap; va_start(ap,format); for(i = 0;format[i]!='\0';i++) { switch(format[i]) { case 's': printf("%s ",va_arg(ap,char*)); break; case 'd': printf("%d ",va_arg(ap,int)); break; default: assert(0); } } va_end(ap); putchar('\n');}int main(){ tiny_printf("sdd","result..",3,5); return 0;}
首先书中从printf入手讲解可变长参数,例如 printf(“%d,%s\n”,100,str);
参数压入栈中,不论有多少个参数,第一个参数(指向”%d,%s\n”的指针)一定存在于距离固定的场所,如果参数不存入栈,按照从左往右的顺序的话,就不能找到第一个参数。
在代码中利用了assert(0),只要程序经过这里就hi报错,因为我们设计的tiny_printf只能处理s和d类型,用户输入不能输入其它类型。
- TLPI-Chapter 3系统编程概念
- TLPI-Chapter 11系统限制和选项
- TLPI-Chapter 12系统和进程信息
- TLPI-Chapter 6 进程
- TLPI-Chapter 10 时间
- TLPI-Chapter 20 信号
- TLPI-Chapter 4文件IO
- TLPI-Chapter 9 进程凭证
- TLPI UNIX linux系统编程手册源代码运行
- TLPI-Chapter 5深入探究文件
- TLPI-Chapter 13文件I/O缓冲
- Chapter 3:迭代器概念以及 traits 编程技法
- Linux系统编程---线程概念
- 系统编程概念与文件属性
- Linux系统编程手册读书笔记——第3章 系统编程概念
- Python 编程学习 chapter 3 变量
- TLPI-Ch24
- Linux/Unix系统编程手册二:系统编程概念
- CodeForces
- java多线程线程池
- git常用命令,分支操作,子模块
- 数组中内容(数字,字符,字符串)交换,不引用第三变量
- 修改maven的默认jdk版本
- TLPI-Chapter 3系统编程概念
- java多线程基础
- 笔试题
- ---coc(clash of clan阵型分析)---
- 2017年9月21日之首
- 谈谈线程池的使用
- 最大子阵和(百度2017秋招真题)
- Android项目:简单拨号器的实现
- oracle误删除数据的恢复方法