TLPI(liunx/unix系统编程手册)笔记(二)
第三章 系统编程概念
系统调用
在调用之前有几点必须要掌握,处理器从用户态转变到核心态,以便cpu访问内核内存;每个系统调用都有唯一的数字来标识;可以用一套参数,对用户空间与内核空间传递信息加以规范 。
X86-32为例子。1.app用c语言的wrapper外壳函数发起调用。2.外壳函数必须保证所有的参数可用,使参数复制到特定的寄存器3.(%eax)。4.执行int 0x80(128)引发处理器从用户态转变为核心态。并执行0x80 中断矢量所指向的代码。5.调用system_call()(arch/i386/entry.s):在内核栈中保存寄存器值,审核编号的有效性,以(sys_call_table)列表进行引索,发现调用服务程序,(中间还有好多的细节想必以后会有介绍),并把状态返回给system_call,从内核栈恢复值,并将返回值置于栈中,返回外壳函数切换回用户态。6.当返回值有误的时候,设置全局变量errno此时返回调用函数表明调用是否成功。-------2017-03-1923:41:27
书上内容用黑框框起来的内容,在我学到如何进行编写系统程序的时候,略有体会。我的调用的系统例程遵循成功时返回非负值,失败时会对相应的全局变量errno取反,返回负值。这点大家都知道,翻开man手册大家都会在ERROR一栏中可以看到,但是,就在上面的还有一栏就是RETURN,上面写的一般都是调用失败时,返回-1.那又是为什么呢?答案就在书上,在取反errno常量后紧接着外壳函数就对其再次取反,将结果拷贝至errno,同时用-1作为外壳函数的返回值,表明有错误发生。当然书上也指出了另类的情况。
例程调用的过程图片:
标准c语言函数库:glibc
用ldd命令查看动态依赖程序,在利用管道查看关键字。 ldd program | grep libc 书上说的gun_get_libc_version函数我是真的找不到他的库在哪里,以后遇在查看吧。confstr()函数的使用可以查看libc的版本的以下是代码:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> //confstr int main(int argc,char **argv[]) { char *pathbuf, *gun_libpthread_version_buf, *gun_libc_version_buf; size_t n; n = confstr(_CS_PATH,NULL,(size_t)0); pathbuf = malloc(n); if ( NULL == pathbuf ) abort(); confstr(_CS_PATH,pathbuf,n); printf("_CS_PATH,a value for the PATH variable:%s\n",pathbuf); free(pathbuf); pathbuf = NULL; n = confstr(_CS_GNU_LIBPTHREAD_VERSION,NULL,(size_t)0); if (n > 0) { gun_libpthread_version_buf = malloc(n); } if (NULL == gun_libpthread_version_buf) abort(); confstr(_CS_GNU_LIBPTHREAD_VERSION,gun_libpthread_version_buf,n); printf("_CS_GNU_LIBPTHREAD_VERSION,the POSIX implementation supplied by the C libary:%s\n",gun_libpthread_version_buf); free(gun_libpthread_version_buf); gun_libpthread_version_buf = NULL; n = confstr(_CS_GNU_LIBC_VERSION,NULL,(size_t)0); //gun_libc_version_buf 存放 libc信息 if (n > 0) { gun_libc_version_buf = malloc(n); } if (NULL == gun_libc_version_buf) abort(); confstr(_CS_GNU_LIBC_VERSION,gun_libc_version_buf,n); printf("_CS_GNU_LIBC_VERSION,the GUN C libary version on this system :%s\n",gun_libc_version_buf); free(gun_libc_version_buf); pathbuf = NULL; } 处理错误 感觉每一个带有返回值的系统调用都应该用if()去判断错误,那样会省去很多的系统调试的时间。运用man手册通过errno来看错误的原因。 2017-03-2600:06:13 下面是几个刚刚定义好的头文件:(当然我只是照搬书上的) /************************************************************************* > File Name: tlpi_hdr.h > Author: aaron > Mail: 60360329@163.com > Created Time: Sun 26 Mar 2017 01:39:31 PM CST ************************************************************************/ #ifndef _TLPI_HDR_H_ #define _TLPI_HDR_H_ #include<sys/types.h> #include<stdio.h> #include<stdlib.c> #include<unistd.h> #include<errno.h> #include<string.h> #include"get_num.h" //Declare our function for handling numeric argument (getInt(),getLing()) #include"error_functions.h" //Declare our error-handling function typedef enum { FALSE, TRUE }Boolean; #define min(m,n) ((m)<(n)?(m):(n)); #define max(m,n) ((m)>(n)?(m):(n)); #endif
#ifndef _ERROR_FUNCTIONS_H_ #define _ERROR_FUNCTIONS_H_ void errMsg(const char *format,...); #ifdef __GUNC__ #define NORETURN __attribute__ ((__noreturn)) #else #define NORETURN #endif void errExit(const char *format,...) NORETURN; void err_exit(const char * format,...) NORETURN; void errExitEN(int errnum, const char *format,...) NORETURN; void fatal(const char *format,...) NORETURN; void usageErr(const char *format,...) NORETURN; void cmdLineErr(const char *format,...) NORETURN; #endif
在这几个诊断函数中errExitEN()值得我们注意的,它需要的是一个(error number),减少了errno的函数调用,使用起来更加高效!
errno = pthread_create(&thread,NULL,func,&agrc); if (errno != 0) errExit("pthread_create"); /*other way*/ int s; s = pthread_create(&thread,NULL,func,&agrc) //用s代替errno,减少了系统调用! if (s != 0) errExitEN(s,"pthread_create");
下面是诊断错误的函数:
/************************************************************************* > File Name: error_functions.c > Author: aaron > Mail: 60360329@163.com > Created Time: Sun 26 Mar 2017 02:14:40 PM CST ************************************************************************/ #include<stdio.h> #include"error_functions.h" #include"tlpi_hdr.h" #include"ename.c.inc" #ifdef __GUNC__ __attribute__ ((__noreturn__)) #endif static void terminate(Boolean useExit3) { char *s; s = getenv("EF_DUMPCORE"); if (s != NULL && *s != '\0') abort(); else if (useExit3) exit(EXIT_FAILURE); else exit(EXIT_FAILURE); } static void outputError(Boolean useErr, int err, Boolean flushStdout, const char *format,va_list ap) { #define BUF_SIZE 500 char buf[BUF_SIZE],userMsg[BUF_SIZE],errText[BUF_SIZE]; vsnprintf(userMsg, BUF_SIZE, format, ap); if (userErr) snprintf(errText,BUF_SIZE," [%s %s]", (err > 0 && err <= MAXENAME)? ename[err] : "?UNKNOWN?",strerror(err)); else snprintf(errText,BUF_SIZE,":"); snprintf(buf,BUF_SIZE,"ERROR%s %s\n",errText,userMsg); if (flushStdout) fflush(stdout); fputs(buf,stderr); fflush(stderr); } void errMsg(const char *format,...) { va_list argList; int savedErrno; savedErrno = errno; va_start(argList, format); outputError(TURE,errno,TRUE,format,argList); va_end(argList); errno = savedErrno; } void errExit(const char *format,...) { va_list argList; va_start(argList,format); outputError(TURE,erron,TRUE,format,argList); va_end(argList); terminate(TRUE); } void err_exit(const char *format,...) { va_list argList; va_start(argList); outputError(TRUE,errno,FALSE,format,argList); va_end(argList); terminate(FALSE); } void errExitEN(int errnum, const char *format, ...) { va_list argList; va_start(argList); outputError(TRUE,errnum,TRUE,format,argList); va_end(argList); terminate(TRUE); } void fatal(const char *format, ...) { va_list argList; va_start(argList); outputError(FALSE,0,TRUE,format,argList); va_end(argList); terminate(TRUE); } void usageErr(const char *format, ...) { va_list argList; fflush(stdout); fprintf(stderr,"Usage: "); va_start(argList); vfprintf(stderr,format,argList); va_end(argList); fflush(stderr); exit(EXIT_FAILURE); } void cmdLineErr(const char *format, ...) { va_list argList; fflush(stdout); fprintf(stderr,"Command-line uasge error: "); va_start(argList); vfprintf(stderr,format,argList); va_end(argList); fflush(stderr); exit(EXIT_FAILURE); }
我们需要知道printf家族的成员,和他的变种方法来看待这个诊断错误的程序。
首先,我需要引用别人对printf家族的解析,
http://blog.chinaunix.net/uid-1771330-id-2863777.html。
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
/************************************************************************* > File Name: get_num.h > Author: aaron > Mail: 60360329@163.com > Created Time: Sun 26 Mar 2017 04:31:15 PM CST ************************************************************************/ #ifndef _GET_NUM_H_ #define _GET_NUM_H_ #define GN_NONNEG 01 /* >= 0 */ #define GN_GT_o 02 /* > 0 */ #define GN_ANY_BASE 0100 /*any base */ #define GN_BASE_8 0200 /* octal */ #define GN_BASE_16 0400 /* hexadecimal */ long getLong(const char *arg, int flags, const char *name); int getInt(const char *arg, int flags, const char *name); #endif
/************************************************************************* > File Name: get_num.c > Author: aaron > Mail: 60360329@163.com > Created Time: Sun 26 Mar 2017 04:36:48 PM CST ************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<limits.h> #include<errno.h> #include"get_num.h" static void gnFail(const char *fname, const char *msg,const char *arg,const char *name) { fprintf(stderr,"%s error",fname); if (name != NULL) fprintf(stderr,"(in %s)",name); fprintf(stderr,": %s\n",msg); if (arg != NULL && *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) { long res; char *endptr; int base; if (arg ==NULL || *arg == '\0') gnFail(fname,"NULL or empty string", arg,name); base = (flags & GN_ANY_BASE)? 0 : (flags & GN_BASE_8)? 8 : (flags & GN_BASE_16)? 16 : 10; errno = 0; res = strtol(arg, &endptr, base); if (errno != 0) gnFail(fname, "strtol failed", arg, name); if (*endptr != '\0') gnFail(fname, "nonnumeric charaters", arg, name); if ((flags & GN_NONNEG) && res < 0) gnFail(fname, "negative value not allowed", arg, name); if ((flags & GN_GT_o) && 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; res = getNum("getInt", arg, flags, name); if (res > INT_MAX || res < INT_MIN) gnFail("getInt","integer out of rang", arg, name); return (int) res; }
这个我还是不大懂的,我准备以后专研一下!2017-03-2621:35:41
可移植性问题
书上有写到好多的测试性宏。 _POSIX_SOURCE _POSIX_C_SOURCE _XOPEN_SOURCE _BSD_SOURCE ...
宏里面又有好的参数,用到的时候再做研究。
在做结构体的定义的时候,给结构体变量赋值的时候,顺序是是需要注意的地方。