内容:Introduction 和 Error Reporting
1. glibc 所实现全部或者部分规范下的功能有
- ISO C: The international standard for the C programming language.
- POSIX: The ISO/IEC 9945 (aka IEEE 1003) standards for operating systems.
- Berkeley Unix: BSD and SunOS.
- SVID: The System V Interface Description.
- XPG: The X/Open Portability Guide.
2. 头文件里面某些函数的实现使用 macro 做到的,理由是一般这样会更快。但是不需要区别他们,因为一般定义了同名的函数,如 abs(int) 既有函数定义也有 macro 定义,使用 & 操作时,由于 macro 调用时不能匹配到 () 因此仍然能取到函数的地址。如果需要显式的调用函数可以用 () 将函数名包围,如 (abs)(3),又或者使用 #undef abs 将定义过的 macro 取消。
3. ISO C 允许重复 #include 头文件,但是 glibc 使用了 #define #ifndef 等预处理器命令避免了同一头文件重复装载,因此可以放心的 #include glibc 的头文件。
4. 除了 ISO C 定义的一些保留字,由于 glibc 实现了一些额外的功能,建议不要使用可能与之冲突的一些命名方式。
5. 如何开启某种标准下 glibc 的功能。前面提到可以利用 -ansi 仅仅使用 ISO C 以内的功能。其他的通过定义 macro 实现条件编联,实现方式分两种,可以在 .c 里面 #define,或者用 -D 把这个 macro 传递给 gcc。为了实现 POSIX 的功能,应定义 _POSIX_SOURCE (_POSIX_C_SOURCE 决定实现的 POSIX 标准类型,如 1、2、>3 实现的功能逐步增多)。类似的有 _BSD_SOURCE、_SVID_SOURCE、_XOPEN_SOURCE。为了处理大文件产生的问题,应定义 _LARGEFILE_SOURCE,又比如 64 位文件系统应定义 _LARGEFILE64_SOURCE。_ISOC99_SOURCE 允许使用 ISO C 99 的标准。_GNU_SOURCE 将允许使用全部的 glibc 的功能。
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
int
main( int argc, char *argv[] )
{
printf( "%s\n%s\n%s\n", argv[0],
program_invocation_short_name,
program_invocation_name ) ;
return 0 ;
}
该例子给出了如何使用非 ISO C 的 program_invocation_(short_)name 变量(程序名带或者不带路径)的例子。使用 _REENTRANT 和 _THREAD_SAFE 时保证使用对应的函数。
6. error handling 模型,这种依赖于全局变量 errno 的做法 ms 被 BS bs 了 -.-b 指责的原因是不能处理多线程倒是文档中解释了如何处理 signal handling 时 errno 的处理(先备份,再修改以免丢失),但是如果忘了这样做可能会把一些潜在的错误覆盖掉,其错误的定义在 errno.h 文件中。可使用 string.h 里面的 strerror( int errno ) 获得该 errno 下的一个字符串解释,但是有更简单的函数,如 perror( const char * ) 输出指定字符串后加上一个冒号和空格再输出错误解释(errno.h)。有一些功能更强大的函数 strerror_r 将字符串写入指定的缓存(多线程时需要),另外为了使得输出更统一(符合 GNU Coding Standard),在 error.h 中定义了一些更加方便的函数,如 error 系列 err 系列,在解析文件时可以用到的 error_at_line 系列、warn 系列。注意 error 函数可以指定一个调用 exit 的值,
这样就不会返回了,但是可以让其返回增大 error_message_count。
内容:Memory
1. 几个基本概念,page、frame、paging、segment。
进程分配内存的两种模式,一个使用 exec 系列函数,一个使用 programmatically(malloc 等函数)。
重要的 segment 有 text segment(存放代码等等,一般在进程的生命周期中不变)、data segment
(存放数据,可以用一些函数来调整大小,但是低位端位置不变)、stack segment(随着使用的堆栈
变大而变大,但不变小...)
2. 内存的静态分配和自动分配。前者是对于 static 变量或者全局变量,一旦开始就分配,即一直存在到最后。
后者是临时变量,如调用函数。值得注意的是:
In GNU C, the size of the automatic storage can be an expression that
varies. In other C implementations, it must be a constant.
3. 内存的动态分配不为 C 语言本身支持,不像 C++。基本方法是
void * malloc (size_t size)
分配到的内存没有初始化(calloc 会做清零,clear allocate),因此可以用 memset 来
进行初始化。分配后应检测返回指针是否为 NULL。malloc 返回的一个块多数情况下对齐
(这样可以存储任意类型的数据)过了,地址为 8 的倍数(64 位系统里面是 16 的倍数),
在一些特殊情况下(page 的边界)可以利用 memalign、posix_memalign、vlign 来返回
对齐(2 的幂次)的内存块。free 的内存很少被返还给操作系统,多数情况被留作后面
malloc 使用。如果需要调整已经 malloc 的块的大小,使用 realloc。glibc 不会将分配的块
对 2 的幂次进行向上取整。
4. 一次性分配很大的内存(大于一个 page)会使用向 2 的幂次取整的策略,使用 mmap
相关的函数 mallopt,这种分配得到的内存在 free 时会返回给操作系统。
5. 如何调整 malloc 的行为?使用 mallopt 调整一些参数的值(malloc.h),如
M_TRIM_THRESHOLD(返回给 OS 的内存的一个阈值)、M_MMAP_THRESHOLD
(大于此值的内存分配请求使用 mmap 系统调用)等等。
6. 分配的内存来自堆(heap),可以用
int mcheck (void (*abortfn) (enum mcheck_status status))
来检查分配内存的一致性,调用在 malloc 之前。
enum mcheck_status mprobe (void *pointer)
为特定的一块内存做检查。这都是 GNU extension。(mcheck.h)
![nixsky[www.nixsky.com]](/templets/images/toplogo.gif)

