语音解读

图文解释

目录

最近在研究go语言,发现go语言系统调用源码只有调用函数的定义,没有指导文档,网上也没有相关文档的说明,自己稍微研究了一下,不对的地方欢迎指教

golang系统调用定义

go源码中关于系统调用的定义如下:

func Syscall(trap,a1,a2,a3 uintptr) (r1,r2 uintptr,err Errno)
func Syscall6(trap,a3,a4,a5,a6 uintptr) (r1,err Errno)
func RawSyscall(trap,err Errno)
func RawSyscall6(trap,err Errno)

其中Syscall和RawSyscall区别在于Syscall开始和结束,分别调用了 runtime 中的进入系统调用和退出系统调用的函数,说明Syscall函数受调度器控制,不会造成系统堵塞,而RawSyscall函数没有调用runtime,因此可能会造成堵塞,一般我们使用Syscall就可以了,RawSyscall最好用在不会堵塞的情况下。

func Syscall(trap,err Errno)

Syscall 的定义位于 src/syscall/asm_linux_amd64.s,是用汇编写成的,封装了对linux底层的调用。接收4个参数,其中trap为中断信号,a1,a3为底层调用函数对应的参数

ioctl函数定义

trap中断类型传入syscall.SYS_IOCTL,SYS_IOCTL中断号表示调用linux底层ioctl函数

Syscall函数中剩下三个参数a1,a3分别对应ioctl的三个参数。可以man命令查看linux ioctl函数参数,如下

int ioctl(int d,int request,...);

第一个参数d指定一个由open/socket创建的文件描述符,即socket套接字

第二个参数request指定操作的类型,即对该文件描述符执行何种操作,设备相关的请求的代码

第三个参数为一块内存区域,通常依赖于request指定的操作类型

实例调用流程

1 通过socket创建套接字

2 初始化struct ifconf与/或struct ifreq结构

3 调用ioctl函数,执行相应类型的SIO操作

4 获取返回至truct ifconf与/或struct ifreq结构中的相关信息

调用底层socket函数创建socket套接字,linux下用man命令查看socket函数用法

int socket(int domain,int type,int protocol);

其中domain为协议类型,type为套接字类型,protocol指定某个协议类型常值
domain的值有:

AF_INET IPv4协议
AF_INET6 Ipv6协议
AF_ROUTE 路由套接字
...

type的值有:

protocol的值有:

IPPROTO_IP IP传输协议
IPPROTO_TCP TCP传输协议
IPPROTO_UDP UDP传输协议
...

因此linux下调用socket生成套接字写法:

fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);

综上,转换成go语言中系统调用写法

fd,_,err := syscall.RawSyscall(syscall.SYS_SOCKET,syscall.AF_INET,syscall.soCK_DGRAM,syscall.IPPROTO_IP)

此时即生成了的socket套接字fd

我们传给int ioctl(int d,…);函数作为第一个参数,

第二个参数request操作的类型我们传入SIOCETHTOOL,获取ethtool信息

SIOCETHTOOL 在源码中宏定义为

#define SIOCETHTOOL     0x8946

第三个参数为struct ifreq结构内存地址
Struct ifreq结构如下:

Struct ifreq{
Char ifr_name[IFNAMSIZ];
Union{
    Struct  sockaddr  ifru_addr;
    Struct  sockaddr  ifru_dstaddr;
    Struct  sockaddr  ifru_broadaddr;
    Struct  sockaddr  ifru_netmask;
    Struct  sockaddr  ifru_hwaddr;
    Short  ifru_flags;
    Int     ifru_metric;
    Caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr        ifr_ifru.ifru_addr
#define ifr_broadaddr   ifr_ifru.ifru_broadadd
#define ifr_hwaddr      ifr_ifru_hwaddr

综上,linux调用ioctl函数如下:

fd = socket(AF_INET,IPPROTO_IP);
ioctl(fd,SIOCETHTOOL,&ifreq);

调用实例

fd,syscall.IPPROTO_IP)
if err != 0 {
        return syscall.Errno(err)
    }

_,ep := syscall.Syscall(syscall.SYS_IOCTL,uintptr(e.fd),uintptr(unsafe.Pointer(&ifreq)))
if ep != 0 {
        return syscall.Errno(ep)