=============================================================================== int main (int argc, char *argv[]); ===============================================================================
=============================================================================== #includevoid exit (int status); #include void _exit (int status); ===============================================================================
=============================================================================== #includeint atexit (void (*function) (void)); ===============================================================================
=============================================================================== GNU 风格的命令行参数特点: * 长参数名, 比如, --binary, 但对应有短参数, 比如, -b ===============================================================================
=============================================================================== #includepoptContext poptGetContext (char * name, int argc, char ** argv, struct poptOption * options, int flags); void poptFreeContext (poptContext con); void poptResetContext (poptContext con); int poptGetNextOpt (poptContext con); char * poptGetOptArg (poptContext con); char * poptGetArg (poptContext con); ...... ------------------------------------------------------------------------------- EXAMPLE: /* This example comes from popt(3) man page. */ #include #include void usage (poptContext optCon, int exitcode, char *error, char *addl) { poptPrintUsage (optCon, stderr, 0); if (error) fprintf (stderr, "%s: %s", error, addl); exit (exitcode); } int main (int argc, char *argv[]) { char c; /* used for argument parsing */ int i = 0; /* used for tracking options */ char *portname; int speed = 0; /* used in argument parsing to set speed */ int raw = 0; /* raw mode? */ int j; char buf[BUFSIZ+1]; poptContext optCon; /* context for parsing command-line options */ struct poptOption optionsTable[] = { { "bps", 'b', POPT_ARG_INT, &speed, 0, "signaling rate in bits-per-second", "BPS" }, { "crnl", 'c', 0, 0, 'c', "expand cr characters to cr/lf sequences" }, { "hwflow", 'h', 0, 0, 'h', "use hardware (RTS/CTS) flow control" }, { "noflow", 'n', 0, 0, 'n', "use no flow control" }, { "raw", 'r', 0, &raw, 0, "don't perform any character conversions" }, { "swflow", 's', 0, 0, 's', "use software (XON/XOF) flow control" } , POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } }; optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); if (argc < 2) { poptPrintUsage(optCon, stderr, 0); exit(1); } /* Now do options processing, get portname */ while ((c = poptGetNextOpt(optCon)) >= 0) { switch (c) { case 'c': buf[i++] = 'c'; break; case 'h': buf[i++] = 'h'; break; case 's': buf[i++] = 's'; break; case 'n': buf[i++] = 'n'; break; } } portname = poptGetArg (optCon); if((portname == NULL) || !(poptPeekArg (optCon) == NULL)) usage (optCon, 1, "Specify a single port", ".e.g., /dev/cua0"); if (c < -1) { /* an error occurred during option processing */ fprintf(stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c)); return 1; } /* Print out options, portname chosen */ printf ("Options chosen: "); for (j = 0; j < i ; j++) printf ("-%c ", buf[j]); if (raw) printf("-r "); if (speed) printf("-b %d ", speed); printf ("\nPortname chosen: %s\n", portname); poptFreeContext (optCon); exit (0); } --------------------------------------- $ ./popt --usage Usage: popt [-chnrs?] [-b BPS] [--usage] [OPTIONS]* $ ./popt -c -b 9600 /dev/cua0 Options chosen: -c -b 9600 Portname chosen: /dev/cua0 ===============================================================================
=============================================================================== #includechar *getenv (const char *name); int putenv (const char *string); int setenv (const char *name, const char *value, int overwrite); void unsetenv (const char *name); ===============================================================================
=============================================================================== ----------------- 高地址 | | ---> 命令行参数和环境变量 (只读) ----------------- | 栈 | |- - - - - - - -| | | | | | \ / | | | | | | / \ | | | | | |- - - - - - - -| | 堆 | |---------------| | 未初始化数据 | | (bss) | ---> 由 exec 初始化为零 |---------------| | 初始化后数据 | \ |---------------| | | text | | 由 exec 从程序中读取 低地址 | | / |---------------| ===============================================================================
ldd 用来显示执行文件需要哪些共享库, 共享库装载管理器在哪里找到了需要的共享库.
共享库的一个非常重要的,也是非常难的概念是 soname——简写共享目标名(short for shared object name)。这是一个为共享库(.so)文件而内嵌在控制数据中的名字。如前面提到的,每一个程序都有一个需要使用的库的清单。这个清单的内容是一系列库的 soname,如同 ldd 显示的那样,共享库装载器必须找到这个清单。
soname 的关键功能是它提供了兼容性的标准。当要升级系统中的一个库时,并且新库的 soname 和老的库的 soname 一样,用旧库连接生成的程序,使用新的库依然能正常运行。这个特性使得在 Linux 下,升级使用共享库的程序和定位错误变得十分容易。
在 Linux 中,应用程序通过使用 soname,来指定所希望库的版本。库作者也可以通过保留或者改变 soname 来声明,哪些版本是相互兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。
查看/usr/local/lib 目录,分析 MiniGUI 的共享库文件之间的关系
当程序被调用的时候,Linux 共享库装载器(也被称为动态连接器)也自动被调用。它的作用是保证程序所需要的所有适当版本的库都被调入内存。共享库装载器名字是 ld.so 或者是 ld-linux.so,这取决于 Linux libc 的版本,它必须使用一点外部交互,才能完成自己的工作。然而它接受在环境变量和配置文件中的配置信息。
文件 /etc/ld.so.conf 定义了标准系统库的路径。共享库装载器把它作为搜索路径。为了改变这个设置,必须以 root 身份运行 ldconfig 工具。这将更新 /etc/ls.so.cache 文件,这个文件其实是装载器内部使用的文件之一。
可以使用许多环境变量控制共享库装载器的操作(表1-4+)。
表 1-4+ 共享库装载器环境变量 变量 含义 LD_AOUT_LIBRARY_PATH 除了不使用 a.out 二进制格式外,与 LD_LIBRARY_PATH 相同。 LD_AOUT_PRELOAD 除了不使用 a.out 二进制格式外,与 LD_PRELOAD 相同。 LD_KEEPDIR 只适用于 a.out 库;忽略由它们指定的目录。 LD_LIBRARY_PATH 将其他目录加入库搜索路径。它的内容应该是由冒号 分隔的目录列表,与可执行文件的 PATH 变量具有相同的格式。 如果调用设置用户 ID 或者进程 ID 的程序,该变量被忽略。 LD_NOWARN 只适用于 a.out 库;当改变版本号是,发出警告信息。 LD_PRELOAD 首先装入用户定义的库,使得它们有机会覆盖或者重新定义标准库。 使用空格分开多个入口。对于设置用户 ID 或者进程 ID 的程序, 只有被标记过的库才被首先装入。在 /etc/ld.so.perload 中指定 了全局版本号,该文件不遵守这个限制。
另外一个强大的库函数是 dlopen()。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如 Apache Web 服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。
可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。
当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
=============================================================================== #includevoid *calloc (size_t nmemb, size_t size); void *malloc (size_t size); void free (void *ptr); void *realloc (void *ptr, size_t size); ------------------------------------------------------------------------------- * calloc 分配后的数据被设置为 0. * malloc 分配的数据不清除. * free (NULL) 是合法的. * realloc 新分配的区域不经过初始化. ===============================================================================
=============================================================================== #includevoid *alloca( size_t size); ------------------------------------------------------------------------------- * alloca 在栈中分配, 不需要释放, 函数返回时自动释放. * 在某些系统的线程库实现中, 要注意每个线程的栈大小有限制. ===============================================================================
=============================================================================== #includeint setjmp (jmp_buf env); void longjmp (jmp_buf env, int val); ------------------------------------------------------------------------------- EXAMPLE: /* C 程序的长跳转 */ #include jmp_buf jmpbuffer; int main (void) { char line [MAXLINE]; switch (setjmp (jmpbuffer)) { case 0: break; case 1: break; default: break; } return 0; } void some_func (void) { if (...) longjmp (jmpbuffer, 0); else if (...) longjmp (jmpbuffer, 1); else longjmp (jmpbuffer, 2); } ------------------------------------------------------------------------------- * jmpbuffer 必须是全局变量. * 注意在长跳转返回之后, 对不同类型的 (自动, 寄存器, 非易失) 变量处理不同. 非易失变量不会发生改变, 但自动变量和寄存器变量可能要恢复为调用 setjmp 之前 的值. ===============================================================================
=============================================================================== #include#include #include int getrlimit (int resource, struct rlimit *rlim); int getrusage (int who, struct rusage *usage); int setrlimit (int resource, const struct rlimit *rlim); ------------------------------------------------------------------------------- struct rlimit { int rlim_cur; int rlim_max; }; struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; ===============================================================================