T9的空间

You will never walk alone!

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  69 随笔 :: 0 文章 :: 28 评论 :: 0 Trackbacks

记性不好,所以作业做起来--从第一章开始,期望有始有终
1.1 对根目录(/)来说,没有parent,所以'.' or '..'就没有区别了,可以用ls -li来看inode,/.. 代表他本身
1.2 多实例,Process ID递增,分配Process ID的具体方式-->//TODO:去查情景分析
1.3 const char* 放到perror中不想让perror自己改,其实我觉得strerror也可以const int
1.4 errno related
比较早之前errno是by process的,这并不合适,Linux支持多线程存取errno
extern int* __errno_location(void)
#define errno (* __errno_location())
Macro为一个函数,使得__errno_location有机会返回一个TSL的variable
这里APUE有Wrap一些简单的打error log的函数,由于errno是一个by thread variable,所以必须先保存
要不然后面print之类的system call同样有可能出现error然后覆盖掉errno
有看到变参函数,纠正了我自己长期的一个误区,之前一直以为变参函数只能用在特定的Case下才有用,例如scanf/printf之类的输入输出函数
因为他们都有带类似format的参数,能够通过%d,%c之类的东西找到后面的变参的个数以及类型,今天有去看一下va_list/va_arg/va_end的实现,会发现变参函数是,本来也应该是一种更加common的设计
这里顺带提一下function call的大体流程,这里需要比较多的背景知识,类似函数调用约定,程序内存分布...
找来一张图比较好看Linux process userspace的布局,印象中windows也一样只不过windows默认是2G/2G,linux是大家都知道的1G/3G


userspace从0~0xC0000000,kernel space(0xC0000000~0xffffffff)的样子跟这个差别比较大,那边分vmalloc/kmalloc还有类似high memory的概念,这里主要贴出userspace,可以看一下RO,RW,ZI,Heap的位置,顺便提一下整个linux virtual memory其实都可以分为两部分: anonymous memory/mapped memory,像RO就是mapped memory,RW开始是mapped memory,只要有人改变初始值,那就会变成anonymous memory,ZI,Heap,Stack这些都是anonymous memory,与函数调用相关的主要是stack -->stack的地址是向低地址增长的,栈底在高地址,栈顶在低地址
然后介绍两种常用的函数调用约定,一种是C语言默认的函数调用约定 __cdecl; 另外一种是PASCAL默认的调用约定 __stdcall
这两种都是从右到左将参数压栈,然后再push ebp; mov ebp, esp; 再然后压入函数局部变量,不一样的是cdecl是由caller function将函数参数出栈,stdcall是由callee function将函数出栈。
这两种方式各有好处,慨括讲一下cdecl的话每个调用者都必须去做pop stack的动作,code size会变大;stdcall则在callee function本身来看会比较单纯
但是这种类型比较难从编译器的角度来支持变参函数。
变参函数都是cdecl
可以玩一下这样的函数...

 

 1#include <cstdlib>
 2#include <iostream>
 3
 4using namespace std;
 5
 6void foo(int cnt, )
 7{
 8    cout << "start foo" << endl; 
 9    int* p = &cnt;
10    for(int i = 0; i < cnt; i++){        
11        cout << *(++p) << endl;
12    }

13    cout << "end foo" << endl;
14}

15
16int main(int argc, char *argv[])
17{
18    int a = 1, b = 3, c = 5, d = 7;
19    void (*functest)(int cnt, );
20    functest = foo;
21    functest(4, a, b, c, d);
22    system("PAUSE");
23    return EXIT_SUCCESS;
24}


加一点对va_list/va_arg/va_end的说法,其实他的实现蛮灵活的

///stdarg.h
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end  

///vadefs.h
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
typedef 
char *  va_list;
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

贴一段网上看到的应该是i386的实现,类似这种变参函数会直接去用指针操作stack来找参数的,因为涉及到对其的问题一定是by platform的,不太好移植
像_INTSIZEOF(n) 就是int对其
大概讲下意思 va_list 就是 char*,是一个用来找参数ptr的游标
va_start(ap, v)  通过第一个固定参数v 初始化 ap
va_arg(ap, t)  这里就给Type t了,所以并不一定需要类似printf的 format 来找,caller和callee可以有一些其他的约定方式,这个返回当前类型为t的参数的值,并且将ap指向下个参数,因为之前ap被初始化的时候其实他并不知道他指向的参数的类型。
va_end(ap) 清掉ap
多说一句,printf系列的函数还蛮多的,fprintf/sprintf/vprintf/svnprintf...带f的是面向FILE streaming的,s是针对char buffer的,v则是说参数是带va_list的,n则是具体指size
1.5/1.6讨论32位表示时间溢出的,不想写答案,时间比较晚,第一章作业写完。

 

记录一些关于系统总线与CPU“位数"的基本概念

通常所说的CPU的位数,32位or64位CPU,指的是ALU(算术逻辑单元)的宽度,也就是这个ALU处理数据的基本单元的宽度
所以数据总线基本会和ALU宽度相同(有例外,这个我没想清楚工作原理) -->应该是可以新加一些Module来做转换。
而地址总线则是CPU寻址的能力,一个是怎么去寻址,一个是寻到地址后,地址中内容的宽度(当然这个宽度跟地址类型(byte,short,int)有关,但送给CPU的时候一般是单位次数送数据总线的宽度的数据),地址总线决定CPU能访问的Memory的范围。

8086是16位ALU 20位数据总线寻址1M
每次CPU送出的地址都是16位,然后加上段寄存器作为最高4位

 

posted on 2013-05-21 23:25 Torres 阅读(192) 评论(0)  编辑 收藏 引用 所属分类: APUE

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理