libvirt中错误码和错误信息的分析
2016-04-10 21:26
871 查看
libvirt每一个重要的操作之前大都会调用virResetLastError函数来清除错误信息,这是为了使本次操作的错误结果可以存储在错误信息结构里面,同时使上一次的错误不会影响本次操作的结果判断。
查看源码,virResetLastError函数的实现如下:
从字面意思可以知道virLastErrorObject是获得错误信息对象(其实是一个结构体),如果获取到了,说明有错误信息,则用virResetError函数清除对象中的错误信息。下面进入这两个函数,如下:
可以知道函数最总返回去的错误信息是通过virThreadLocalGet(&virLastErr)获得的,virLastErr是一个全局变量,定义如下:
再查看virThreadLocalGet函数定义:
从这里就可以知道了,错误信息的存储是用了线程特定数据,这样每个线程都会有自己的数据存储,该线程中的每个函数都可以访问本线程的特定数据,而其他线程不能访问,这样就能保证数据在线程间安全了。
这里是使用线程特定数据,但是必须得有初始化的部分,经搜索源码,virsh命令有
virResetError函数是复位错误信息对象的,定义如下:
结合virErrorPtr 结构体变量的定义:
virResetError函数就是释放错误信息结构体变量中的指针。从结构体域的定义可以知道,code是错误码,message是可以读懂的错误信息。
经过分析virResetLastError函数的作用就是先获得存储错误信息的线程特定数据,然后将线程特定数据中的指针释放,并将里面的全部变量赋值为0。
virResetLastError是读取和复位错误信息的,virsh 和 libvirtd中虽然有多个函数给线程特定数据赋值的,但最终调用的函数大概只有virSetError和virRaiseErrorFull,进入这两个函数:
可以看到设置错误信息,都是先获取线程特定数据,然后将错误信息赋值给它。
最后virsh 和libvirtd初始化线程特定数据的函数都是virInitialize,定义如下:
virOnce确保一个进程中virGlobalInit函数只执行一次,该函数中调用virErrorInitialize,经过一层层的跟进,可以看到最后pthread_key_create函数初始化了线程特定数据。
查看源码,virResetLastError函数的实现如下:
void virResetLastError(void) { virErrorPtr err = virLastErrorObject(); if (err) virResetError(err); }
从字面意思可以知道virLastErrorObject是获得错误信息对象(其实是一个结构体),如果获取到了,说明有错误信息,则用virResetError函数清除对象中的错误信息。下面进入这两个函数,如下:
static virErrorPtr virLastErrorObject(void) { virErrorPtr err; err = virThreadLocalGet(&virLastErr); if (!err) { if (VIR_ALLOC_QUIET(err) < 0) return NULL; if (virThreadLocalSet(&virLastErr, err) < 0) VIR_FREE(err); } return err; }
可以知道函数最总返回去的错误信息是通过virThreadLocalGet(&virLastErr)获得的,virLastErr是一个全局变量,定义如下:
struct virThreadLocal { pthread_key_t key; };
再查看virThreadLocalGet函数定义:
void *virThreadLocalGet(virThreadLocalPtr l) { return pthread_getspecific(l->key); }
从这里就可以知道了,错误信息的存储是用了线程特定数据,这样每个线程都会有自己的数据存储,该线程中的每个函数都可以访问本线程的特定数据,而其他线程不能访问,这样就能保证数据在线程间安全了。
这里是使用线程特定数据,但是必须得有初始化的部分,经搜索源码,virsh命令有
virResetError函数是复位错误信息对象的,定义如下:
void virResetError(virErrorPtr err) { if (err == NULL) return; VIR_FREE(err->message); VIR_FREE(err->str1); VIR_FREE(err->str2); VIR_FREE(err->str3); memset(err, 0, sizeof(virError)); }
结合virErrorPtr 结构体变量的定义:
struct _virError { int code; /* The error code, a virErrorNumber */ int domain; /* What part of the library raised this error */ char *message;/* human-readable informative error message */ virErrorLevel level;/* how consequent is the error */ virConnectPtr conn VIR_DEPRECATED; /* connection if available, deprecated see note above */ virDomainPtr dom VIR_DEPRECATED; /* domain if available, deprecated see note above */ char *str1; /* extra string information */ char *str2; /* extra string information */ char *str3; /* extra string information */ int int1; /* extra number information */ int int2; /* extra number information */ virNetworkPtr net VIR_DEPRECATED; /* network if available, deprecated see note above */ };
virResetError函数就是释放错误信息结构体变量中的指针。从结构体域的定义可以知道,code是错误码,message是可以读懂的错误信息。
经过分析virResetLastError函数的作用就是先获得存储错误信息的线程特定数据,然后将线程特定数据中的指针释放,并将里面的全部变量赋值为0。
virResetLastError是读取和复位错误信息的,virsh 和 libvirtd中虽然有多个函数给线程特定数据赋值的,但最终调用的函数大概只有virSetError和virRaiseErrorFull,进入这两个函数:
void virRaiseErrorFull(const char *filename, const char *funcname, size_t linenr, int domain, int code, virErrorLevel level, const char *str1, const char *str2, const char *str3, int int1, int int2, const char *fmt, ...) { int save_errno = errno; virErrorPtr to; char *str; virLogMetadata meta[] = { { .key = "LIBVIRT_DOMAIN", .s = NULL, .iv = domain }, { .key = "LIBVIRT_CODE", .s = NULL, .iv = code }, { .key = NULL }, }; /* * All errors are recorded in thread local storage * For compatibility, public API calls will copy them * to the per-connection error object when necessary */ to = virLastErrorObject(); if (!to) { errno = save_errno; return; /* Hit OOM allocating thread error object, sod all we can do now */ } virResetError(to); if (code == VIR_ERR_OK) { errno = save_errno; return; } /* * formats the message; drop message on OOM situations */ if (fmt == NULL) { ignore_value(VIR_STRDUP_QUIET(str, _("No error message provided"))); } else { va_list ap; va_start(ap, fmt); ignore_value(virVasprintfQuiet(&str, fmt, ap)); va_end(ap); } /* * Save the information about the error */ /* * Deliberately not setting conn, dom & net fields since * they're utterly unsafe */ to->domain = domain; to->code = code; to->message = str; to->level = level; ignore_value(VIR_STRDUP_QUIET(to->str1, str1)); ignore_value(VIR_STRDUP_QUIET(to->str2, str2)); ignore_value(VIR_STRDUP_QUIET(to->str3, str3)); to->int1 = int1; to->int2 = int2; virRaiseErrorLog(filename, funcname, linenr, to, meta); errno = save_errno; }
static int virCopyError(virErrorPtr from, virErrorPtr to) { int ret = 0; if (!to) return 0; virResetError(to); if (!from) return 0; to->code = from->code; to->domain = from->domain; to->level = from->level; if (VIR_STRDUP_QUIET(to->message, from->message) < 0) ret = -1; if (VIR_STRDUP_QUIET(to->str1, from->str1) < 0) ret = -1; if (VIR_STRDUP_QUIET(to->str2, from->str2) < 0) ret = -1; if (VIR_STRDUP_QUIET(to->str3, from->str3) < 0) ret = -1; to->int1 = from->int1; to->int2 = from->int2; /* * Deliberately not setting 'conn', 'dom', 'net' references */ return ret; }
可以看到设置错误信息,都是先获取线程特定数据,然后将错误信息赋值给它。
最后virsh 和libvirtd初始化线程特定数据的函数都是virInitialize,定义如下:
int virInitialize(void) { if (virOnce(&virGlobalOnce, virGlobalInit) < 0) return -1; if (virGlobalError) return -1; return 0; }
virOnce确保一个进程中virGlobalInit函数只执行一次,该函数中调用virErrorInitialize,经过一层层的跟进,可以看到最后pthread_key_create函数初始化了线程特定数据。
int virErrorInitialize(void) { return virThreadLocalInit(&virLastErr, virLastErrFreeData); } int virThreadLocalInit(virThreadLocalPtr l, virThreadLocalCleanup c) { int ret; if ((ret = pthread_key_create(&l->key, c)) != 0) { errno = ret; return -1; } return 0; }
相关文章推荐
- 简单易懂云计算(转自天涯感谢原楼主iamsatisfied)
- 2011云计算知识库:盘点千奇百怪的云名称
- 如何使用 virt-manager 运行虚拟机
- Linux下三大免费桌面虚拟机评测
- 3ff8 《sharepoint 2010云计算解决方案》使用SQL Azure 的BI 解决方案
- convert(一)—— 部署managed
- convirt(二)—— 创建第一台虚机
- IaaS, PaaS, SaaS 解释
- 在Google使用Borg进行大规模集群的管理 3-4
- 在Google使用Borg进行大规模集群的管理 5-6
- 在Google使用Borg进行大规模集群的管理 7-8
- 未来的容器云技术栈会怎么样?在容器技术大会上的演讲
- QEMU 1: 使用QEMU创建虚拟机
- QEMU 2: 参数解析
- 手动安装openstack并配置虚拟化集成VM
- 某虚拟化项目中思科与华为交换机链路聚合互连案例