第三章 系统编程概念
系统调用
在调用之前有几点必须要掌握,处理器从用户态转变到核心态,以便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 ...
宏里面又有好的参数,用到的时候再做研究。
在做结构体的定义的时候,给结构体变量赋值的时候,顺序是是需要注意的地方。