详解Linux设备专用控制ioctl函数

AI 概述
ioctl 是 Linux 中用于用户程序与设备驱动交互的系统调用,通过文件描述符执行特定设备控制操作。当标准 I/O 无法满足需求时,它可处理复杂控制和信息查询。其函数原型包含文件描述符、设备特定控制命令及数据结构指针等参数。常见使用场景有设备控制、状态查询、修改设置等。文中列举了获取网卡 MAC 地址、IPv4 地址、MTU、网络掩码等网络接口信息,以及设置接口状态、获取终端窗口大小等实例,使用时要注意正确使用头文件。
目录
文章目录隐藏
  1. 1、获取网卡的 mac 地址
  2. 2、获取网络接口的 IPv4 地址
  3. 3、获取网络接口的最大传输单元(MTU)
  4. 4、设置网络接口的状态标志(如启用或禁用接口)
  5. 5、获取网络接口的网络掩码
  6. 6.、获取网络接口的广播地址
  7. 7、获取终端窗口大小

详解 Linux 设备专用控制 ioctl 函数

ioctl(输入输出控制)是一个系统调用,常用于 Linux 操作系统。它允许用户程序通过文件描述符与设备驱动程序进行交互,执行特定于设备的控制操作。

在许多设备(如网络接口、串口、终端等)中,标准的输入输出操作(例如 read 和 write)无法满足所有需求。但是 ioctl 提供了一种处理这种方法的机制,通过它可以进行更复杂的控制和信息查询。

函数原型:

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ... /* argp */);

参数:

  • fd:已打开的设备文件描述符(如 /dev/tty、/dev/sda)。
  • request:设备特定的控制命令(如 TIOCGWINSZ 获取终端大小)。
  • argp:指向数据结构的指针或整数值,内容由 request 决定。

返回值:

  • 成功时:通常返回 0 或非负值(具体由设备驱动定义)。
  • 失败时:返回 -1,并设置 errno(如 ENOTTY 表示 fd 不是字符设备)。

错误处理:

当 ioctl 调用失败时,errno 会被设置为相应的错误代码。常见的错误包括:

  • EBADF: 文件描述符无效。
  • EFAULT: 提供的地址无效。
  • EINVAL: 请求码无效或不支持。
  • ENOTTY: 尝试对不支持 ioctl 的设备进行控制。

使用场景

ioctl 可以用于多种情况,具体如下:

  • 设备控制: 控制硬件设备的行为,如设置串口的波特率、获取网络接口的信息等。
  • 状态查询: 查询设备的状态或属性,如获取网络接口的 MAC 地址、获取终端的窗口大小等。
  • 修改设置: 修改设备的配置或属性,如设置音频设备的音量、调整图形设备的分辨率等。

举一些常用场景与代码示例:

1、获取网卡的 mac 地址

  • 请求码:SIOCGIFHWADDR
  • devname: 网络设备的名称(例如 “eth0” 或 “wlan0″)。
  • buf: 用于存放 MAC 地址字符串的缓冲区。
  • size: buf 的大小。
  • 返回值: 函数返回一个整数,通常 0 表示成功,-1 表示错误。
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
int get_mac_address(char *devname ,char *buf ,int size)
{
    int sock_mac;
    struct ifreq ifr_mac;  
    sock_mac = socket(AF_INET, SOCK_STREAM, 0);
    if( sock_mac < 0){
          return -1;
    }  
    memset(&ifr_mac,0,sizeof(ifr_mac));
    strncpy(ifr_mac.ifr_name, devname, sizeof(ifr_mac.ifr_name)-1);
     
    if(ioctl(sock_mac, SIOCGIFHWADDR, &ifr_mac) < 0){
        goto err;
    }  
    snprintf(buf, size,"%02X:%02X:%02X:%02X:%02X:%02X",
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[0],
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[1],
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[2],
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[3],
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[4],
            (unsigned char)ifr_mac.ifr_hwaddr.sa_data[5]);  
    close(sock_mac);
    return 0;
err:
    close(sock_mac);
    return -1;
}

2、获取网络接口的 IPv4 地址

  • 请求码: SIOCGIFADDR
  • 使用方法: 将 struct ifreq 传递给 ioctl
  • 返回数据: IPv4 地址存储在 参数 buf 中
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
void get_ip_address(const char *devname, char *buf ,int size) {
    int sock;
    struct ifreq ifr;
    // 创建套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return;
    }
    // 清空结构体并设置接口名称
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    // 获取 IP 地址
    if (ioctl(sock, SIOCGIFADDR, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return;
    }
    struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
    snprintf(buf, size,"%s", inet_ntoa(ipaddr->sin_addr));
    close(sock);
}

3、获取网络接口的最大传输单元(MTU)

  • 请求码: SIOCGIFMTU
  • 使用方法: 将 struct ifreq 传递给 ioctl。
  • 返回数据: MTU 值存储在 ifr_mtu 字段。
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
int get_mtu(const char *devname) {
    int sock;
    struct ifreq ifr;
    // 创建套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return -1;
    }
    // 清空结构体并设置接口名称
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    // 获取 MTU
    if (ioctl(sock, SIOCGIFMTU, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return -1;
    }
    printf("MTU of %s: %d\n", devname, ifr.ifr_mtu);
         
    close(sock);
    return ifr.ifr_mtu;
}

4、设置网络接口的状态标志(如启用或禁用接口)

  • 请求码: SIOCSIFFLAGS
  • 使用方法: 修改 struct ifreq 中的 ifr_flags 字段,然后通过 ioctl 发送。
  • 注意: 需要使用 SIOCGIFFLAGS 先获取当前的标志,再进行修改。
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
void set_interface_up(const char *devname) {
    int sock;
    struct ifreq ifr;
    // 创建套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return;
    }
    // 清空结构体并设置接口名称
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    // 获取当前接口状态
    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return;
    }
    // 设置接口为 UP
    ifr.ifr_flags |= IFF_UP; // 启用接口
    // 更新接口状态
    if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return;
    }
    printf("Interface %s is now up.\n", devname);
    close(sock);
}

5、获取网络接口的网络掩码

  • 请求码: SIOCGIFNETMASK
  • 使用方法: 将 struct ifreq 传递给 ioctl。
  • 返回数据: 网络掩码存储在 buf 参数中。
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
void get_netmask(const char *devname, char *buf, int size) {
    int sock;
    struct ifreq ifr;
    // 创建套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return;
    }
    // 清空结构体并设置接口名称
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    // 获取网络掩码
    if (ioctl(sock, SIOCGIFNETMASK, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return;
    }
    struct sockaddr_in *netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
    snprintf(buf, size, "%s\n", inet_ntoa(netmask->sin_addr));
    close(sock);
}

6.、获取网络接口的广播地址

  • 请求码: SIOCGIFBRDADDR
  • 使用方法: 将 struct ifreq 传递给 ioctl。
  • 返回数据: 广播地址存储在 参数 buf 中。
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
void get_broadcast_address(const char *devname, char *buf, int size) {
    int sock;
    struct ifreq ifr;
    // 创建套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return;
    }
    // 清空结构体并设置接口名称
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    // 获取广播地址
    if (ioctl(sock, SIOCGIFBRDADDR, &ifr) == -1) {
        perror("ioctl");
        close(sock);
        return;
    }
    struct sockaddr_in *broadaddr = (struct sockaddr_in *)&ifr.ifr_broadaddr;
    snprintf(buf, size, "%s", inet_ntoa(broadaddr->sin_addr));
    close(sock);
}

7、获取终端窗口大小

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
void get_term_size() {
    struct winsize ws; // 终端窗口大小结构体
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
        perror("ioctl failed");
        return 1;
    }
    printf("终端窗口大小:%d 行 x %d 列\n", ws.ws_row, ws.ws_col);
    return 0;
}

上面是一些常见的 ioctl 操作都是小编在实际工作中用到过的。每个请求代码都有特定的结构体和字段用于获取或设置网络接口的各种信息。小伙伴们在使用这些请求代码时,请确保使用正确的头文件,并了解相应的结构和字段,以便正确处理数据。

以上关于详解Linux设备专用控制ioctl函数的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

1

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 详解Linux设备专用控制ioctl函数

发表回复