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。

       #include
       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const char *format, ...);
 
       #include
       int vprintf(const char *format, va_list ap);
       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);
上面有好多的s,v,n就和stat族一样,v就是veter数组,s是string,n是number,f就是file,第一项如果带svfn的话就是输出到哪里;
第二项就是格式,va_list 也是格式有关归为一项。
 
其次,我类似于stat和printf,我们也可做出想他们一样的族,把功能集中到一个接口上outputerror(),给不同的参数,让它适用于不同的情况。
下面是解析数值型命令行参数的函数的头和函数
/*************************************************************************
	> 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 ...

宏里面又有好的参数,用到的时候再做研究。

      在做结构体的定义的时候,给结构体变量赋值的时候,顺序是是需要注意的地方。

  

posted @ 2017-03-19 23:42  AAAron  阅读(842)  评论(0编辑  收藏  举报