Windows/Linux中C++对于系统函数发生错误时的调试方法(调试Windows/Linux下创建原始socket失败返回-1)

调用系统API时,经常会由于操作不当导致系统函数调用发生错误,而系统API也是比较友好的,会给你一些特殊的返回值,普遍返回-1,同时,会设置一些变量,表示错误类型。在Windows中,调用GetLastError,可以得到最近的调用失败的错误码;在Linux中,“全局变量”errno记录了最近调用失败的错误码。

这里纠正一下,errno其实并不是全局变量,errno的作用是thread local,在线程中是全局的。

当然,这可以在代码中手动添加代码来得到这些错误码,从而推断出错误原因;

但是,我们不想去改代码,想在调试中就推断出系统API调用失败的原因怎么办,有办法的。

这里通过今天在Windows和Linux下创建原始套接字失败直接返回-1进行调试。

Windows

Windows一般用Visual Studio来写代码。而Visual Studio号称宇宙最强IDE,必然有一些方便我们的地方。

我们可以在调用API失败处打上一个断点,然后在监视器中填上$err,hr,然后向下执行这个系统调用,如果失败,那么监视器中就会显示错误代码以及错误的原因描述,这个真的很方便。

int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

socketFd为-1,运用上面的方法,得到下面的图片。

在这里插入图片描述

看到这条信息,我就知道错误原因了,Windows下调用套接字函数有那么一套必须的代码流程:

#include <WinSock2.h>
#pragma comment(lib, "WS2_32")

WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);

加上这段代码就能正常调用socket函数了。

Linux

Linux下,都是GNU那一套,用的是GDB调试,同样也是打断点,执行系统调用,然后在GDB中输入p errno,查看错误代码,注意,Linux下远没有Visual Studio那么方便,所以,得到了错误码,你要自己去查看错误码对应的错误原因;

这里有个办法,使用man errno及查看errno.h头文件。

第一种

man errno会得出一些宏观及其对应的错误原因,但是这些宏的值是多少?这个就要看头文件了。

不同的Linux版本,不同的G++版本,errno.h中的定义有点不一样,这个要特殊情况特殊分析,举个例子,Ubuntu 14.04 LTS中,想看到errno.h,这个头文件就要费点功夫了,得用find或者grep/usr/include中找,且,头文件可能还套头文件,其中,Ubuntu 14.04 LTS的错误码宏定义在/usr/include/asm-generic/errno-base.h以及/usr/include/asm-generic/errno.h中,这里和MinGW的头文件errno.h对比下,看看这些宏定义的值有哪些不一样。

// /usr/include/asm-generic/errno-base.h

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

// MinGW/include/error.h

#define EPERM		1	/* Operation not permitted */
#define	ENOFILE		2	/* No such file or directory */
#define	ENOENT		2
#define	ESRCH		3	/* No such process */
#define	EINTR		4	/* Interrupted function call */
#define	EIO		5	/* Input/output error */
#define	ENXIO		6	/* No such device or address */
#define	E2BIG		7	/* Arg list too long */
#define	ENOEXEC		8	/* Exec format error */
#define	EBADF		9	/* Bad file descriptor */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Resource temporarily unavailable */
#define	ENOMEM		12	/* Not enough space */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
/* 15 - Unknown Error */
#define	EBUSY		16	/* strerror reports "Resource device" */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Improper link (cross-device link?) */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* Too many open files in system */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Inappropriate I/O control operation */
/* 26 - Unknown Error */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Invalid seek (seek on a pipe?) */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Domain error (math functions) */
#define	ERANGE		34	/* Result too large (possibly too small) */
/* 35 - Unknown Error */
#define	EDEADLOCK	36	/* Resource deadlock avoided (non-Cyg) */
#define	EDEADLK		36
/* 37 - Unknown Error */
#define	ENAMETOOLONG	38	/* Filename too long (91 in Cyg?) */
#define	ENOLCK		39	/* No locks available (46 in Cyg?) */
#define	ENOSYS		40	/* Function not implemented (88 in Cyg?) */
#define	ENOTEMPTY	41	/* Directory not empty (90 in Cyg?) */
#define	EILSEQ		42	/* Illegal byte sequence */

可以看出,大部分通用的错误代码的宏定义还是一样的,不过也有一些区别,这很正常。而且,头文件中有注释,这些就是错误的原因。

int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

于是在Linux下,在GDB中也运用这个方法:

在这里插入图片描述

发现错误原因是Operation not permitted,这说明我们权限不够,然后查资料,得出,Linux下root用户才能创建原始套接字。

总结

这些只是一些技巧,能解决问题的方法有很多,选择适合自己的就行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值