ioctl 函數html
本函數影響由fd 參數引用的一個打開的文件。linux
#include<unistd.h>ios
int ioctl( int fd, int request, .../* void *arg */ );編程
返回0 :成功 -1 :出錯緩存
第三個參數老是一個指針,但指針的類型依賴於request 參數。網絡
咱們能夠把和網絡相關的請求劃分爲6 類:異步
套接口操做socket
文件操做函數
接口操做測試
ARP 高速緩存操做
路由表操做
流系統
下表列出了網絡相關ioctl 請求的request 參數以及arg 地址必須指向的數據類型:
類別 |
Request |
說明 |
數據類型 |
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位於帶外標記 設置套接口的進程ID 或進程組ID 獲取套接口的進程ID 或進程組ID |
int int int |
文 件 |
FIONBIN FIOASYNC FIONREAD FIOSETOWN FIOGETOWN |
設置/ 清除非阻塞I/O 標誌 設置/ 清除信號驅動異步I/O 標誌 獲取接收緩存區中的字節數 設置文件的進程ID 或進程組ID 獲取文件的進程ID 或進程組ID |
int int int int int |
接 口 |
SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx |
獲取全部接口的清單 設置接口地址 獲取接口地址 設置接口標誌 獲取接口標誌 設置點到點地址 獲取點到點地址 獲取廣播地址 設置廣播地址 獲取子網掩碼 設置子網掩碼 獲取接口的測度 設置接口的測度 獲取接口MTU (還有不少取決於系統的實現) |
struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
ARP |
SIOCSARP SIOCGARP SIOCDARP |
建立/ 修改ARP 表項 獲取ARP 表項 刪除ARP 表項 |
struct arpreq struct arpreq struct arpreq |
路 由 |
SIOCADDRT SIOCDELRT |
增長路徑 刪除路徑 |
struct rtentry struct rtentry |
流 |
I_xxx |
|
|
套接口操做:
明確用於套接口操做的ioctl 請求有三個, 它們都要求ioctl 的第三個參數是指向某個整數的一個指針。
SIOCATMARK: 若是本套接口的的度指針當前位於帶外標記,那就經過由第三個參數指向的整數返回一個非0 值;不然返回一個0 值。POSIX 以函數sockatmark 替換本請求。
SIOCGPGRP : 經過第三個參數指向的整數返回本套接口的進程ID 或進程組ID ,該ID 指定針對本套接口的SIGIO 或SIGURG 信號的接收進程。本請求和fcntl 的F_GETOWN 命令等效,POSIX 標準化的是fcntl 函數。
SIOCSPGRP : 把本套接口的進程ID 或者進程組ID 設置成第三個參數指向的整數,該ID 指定針對本套接口的SIGIO 或SIGURG 信號的接收進程,本請求和fcntl 的F_SETOWN 命令等效,POSIX 標準化的是fcntl 操做。
文件操做:
如下5 個請求都要求ioctl 的第三個參數指向一個整數。
FIONBIO : 根據ioctl 的第三個參數指向一個0 或非0 值分別清除或設置本套接口的非阻塞標誌。本請求和O_NONBLOCK 文件狀態標誌等效,而該標誌經過fcntl 的F_SETFL 命令清除或設置。
FIOASYNC : 根據iocl 的第三個參數指向一個0 值或非0 值分別清除或設置針對本套接口的信號驅動異步I/O 標誌,它決定是否收取針對本套接口的異步I/O 信號(SIGIO )。本請求和O_ASYNC 文件狀態標誌等效,而該標誌能夠經過fcntl 的F_SETFL 命令清除或設置。
FIONREAD : 經過由ioctl 的第三個參數指向的整數返回當前在本套接口接收緩衝區中的字節數。本特性一樣適用於文件,管道和終端。
FIOSETOWN : 對於套接口和SIOCSPGRP 等效。
FIOGETOWN : 對於套接口和SIOCGPGRP 等效。
接口配置:
獲得系統中全部接口由SIOCGIFCONF 請求完成,該請求使用ifconf 結構,ifconf 又使用ifreq
結構,以下所示:
Struct ifconf{
int ifc_len; // 緩衝區的大小
union{
caddr_t ifcu_buf; // input from user->kernel
struct ifreq *ifcu_req; // return of structures returned
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf //buffer address
#define ifc_req ifc_ifcu.ifcu_req //array of structures returned
#define IFNAMSIZ 16
struct ifreq{
char ifr_name[IFNAMSIZ]; // interface name, e.g., 「le0」
union{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr // address
#define ifr_dstaddr ifr_ifru.ifru_dstaddr // otner end of p-to-p link
#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address
#define ifr_flags ifr_ifru.ifru_flags // flags
#define ifr_metric ifr_ifru.ifru_metric // metric
#define ifr_data ifr_ifru.ifru_data // for use by interface
再調用ioctl 前咱們必須先分撇一個緩衝區和一個ifconf 結構,而後才初始化後者。以下圖
展現了一個ifconf 結構的初始化結構,其中緩衝區的大小爲1024 ,ioctl 的第三個參數指向
這樣一個ifconf 結構。
1024
---------------------> 緩存
假設內核返回2 個ifreq 結構,ioctl 返回時經過同一個ifconf 結構緩衝區填入了那2 個ifreq 結構,ifconf 結構的ifc_len 成員也被更新,以反映存放在緩衝區中的信息量
ioctl調用與網絡編程有關(本文只討論這一點),文件描述符fd其實是由socket()系統調用返回的。參數command的取值由/usr/include/linux/sockios.h 所規定。這些command的因爲功能的不一樣,可分爲如下幾個小類:
• 改變路由表 (例如 SIOCADDRT, SIOCDELRT),
• 讀/更新 ARP/RARP 緩存(如:SIOCDARP, SIOCSRARP),
• 通常的與網絡接口有關的(例如 SIOCGIFNAME, SIOCSIFADDR 等等)
在 Gooodies目錄下有不少樣例程序展現瞭如何使用ioctl。當你看這些程序時,注意參數argstruct是與參數command相關的。例如,與 路由表相關的ioctl使用rtentry這種結構,rtentry定義在/usr/include/linux/route.h(參見例子 adddefault.c)。與ARP有關的ioctl調用使用arpreq結構,arpreq定義在/usr/include/linux /if_arp.h(參見例子arpread.c)
與網絡接口有關的ioctl調用使用的command參數一般看起來像SIOCxIFyyyy的形式,這裏x要 麼是S(設定set,寫write),要麼是G(獲得get,讀read)。在getifinfo.c程序中就使用了這種形式的command參數來讀 IP地址,硬件地址,廣播地址和獲得與網絡接口有關的一些標誌(flag)。在這些ioctl調用中,第三個參數是ifreq結構,它在/usr /include/linux/if.h中定義。在某些狀況下, ioctrl調用可能會使用到在sockios.h以外的新的定義。
實例:
程序1:檢測接口的 .net_addr,netmask,broad_addr
程序2:檢查接口的物理鏈接是否正常
程序3:更簡單一點測試物理鏈接
程序4:調節音量
***************************程序1****************************************
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
static void usage(){
printf("usage : ipconfig interface \n");
exit(0);
}
int main(int argc,char **argv)
{
struct sockaddr_in *addr;
struct ifreq ifr;
char *name,*address;
int sockfd;
if(argc != 2)
usage();
else
name = argv[1]; 內容來自ltesting.net
sockfd = socket(AF_INET,SOCK_DGRAM,0);
strncpy(ifr.ifr_name,name,IFNAMSIZ-1);
if(ioctl(sockfd,SIOCGIFADDR,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&(ifr.ifr_addr);
address = inet_ntoa(addr->sin_addr);
printf("inet addr: %s ",address);
if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&ifr.ifr_broadaddr;
address = inet_ntoa(addr->sin_addr);
printf("broad addr: %s ",address);
if(ioctl(sockfd,SIOCGIFNETMASK,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&ifr.ifr_addr;
address = inet_ntoa(addr->sin_addr);
printf("inet mask: %s ",address);
printf("\n");
exit(0);
}
******************************** 程序2*****************************************************
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdlib.h>
#include <unistd.h>
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned char u8;
#include <linux/ethtool.h>
#include <linux/sockios.h>
int detect_mii(int skfd, char *ifname)
{
struct ifreq ifr;
u16 *data, mii_val;
unsigned phy_id;
/* Get the vitals from the interface. */
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCG
MIIPHY, &ifr) < 0) { fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", ifname, strerror(errno)); (void) close(skfd); return 2; } data = (u16 *)(&ifr.ifr_data); phy_id = data[0]; data[1] = 1; if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) { fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name, 領測軟件測試網http://www.ltesting.net strerror(errno)); return 2; } mii_val = data[3]; return(((mii_val & 0x0016) == 0x0004) ? 0 : 1); } int detect_ethtool(int skfd, char *ifname) { struct ifreq ifr; struct ethtool_value edata; memset(&ifr, 0, sizeof(ifr)); edata.cmd = ETHTOOL_GLINK; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1); ifr.ifr_data = (char *) &edata; if (ioctl(skfd, SIOCETHTOOL, &ifr) == -1) { printf("ETHTOOL_GLINK failed: %s\n", strerror(errno)); return 2; } return (edata.data ? 0 : 1); } int main(int argc, char **argv) { int skfd = -1; char *ifname; int retval; if( argv[1] ) ifname = argv[1]; else ifname = "eth0"; /* Open a socket. */ if (( skfd = socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) { printf("socket error\n"); exit(-1); } retval = detect_ethtool(skfd, ifname); if (retval == 2) retval = detect_mii(skfd, ifname); close(skfd); if (retval == 2) printf("Could not determine status\n"); if (retval == 1) printf("Link down\n"); if (retval == 0) printf("Link up\n"); return retval; } *******************************程序3***************************************************** #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <net/if.h> #include <linux/sockios.h> #include <sys/ioctl.h> #define LINKTEST_GLINK 0x0000000a struct linktest_value { unsigned int cmd; unsigned int data; }; static void usage(const char * pname) { fprintf(stderr, "usage: %s <device>\n", pname); fprintf(stderr, "returns: \n"); fprintf(stderr, "\t 0: link detected\n"); fprintf(stderr, "\t%d: %s\n", ENODEV, strerror(ENODEV)); fprintf(stderr, "\t%d: %s\n", ENONET, strerror(ENONET)); fprintf(stderr, "\t%d: %s\n", EOPNOTSUPP, strerror(EOPNOTSUPP)); exit(EXIT_FAILURE); } static int linktest(const char * devname) { struct ifreq ifr; struct linktest_value edata; int fd; /* setup our control structures. */ memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, devname); /* open control socket. */ fd=socket(AF_INET, SOCK_DGRAM, 0); if(fd < 0 ) { return -ECOMM; } errno=0; edata.cmd = LINKTEST_GLINK; ifr.ifr_data = (caddr_t)&edata; if(!ioctl(fd, SIOCETHTOOL, &ifr)) { if(edata.data) { ltesting.net fprintf(stdout, "link detected on %s\n", devname); return 0; } else { errno=ENONET; } } perror("linktest"); return errno; } int main(int argc, char *argv[]) { if(argc != 2) { usage(argv[0]); } return linktest(argv[1]); } *************************************程序4********************************************************* #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/soundcard.h> #include <stdio.h> #include <unistd.h> #include <math.h> #include <string.h> #include <stdlib.h> #define BASE_VALUE 257 int main(int argc,char *argv[]) { int mixer_fd=0; char *names[SOUND_MIXER_NRDEVICES]=SOUND_DEVICE_LABELS; int value,i; printf("\nusage:%s dev_no.[0..24] value[0..100]\n\n",argv[0]); printf("eg. %s 0 100\n",argv[0]); printf(" will change the volume to MAX volume.\n\n"); printf("The dev_no. are as below:\n"); for (i=0;i<SOUND_MIXER_NRDEVICES;i++){ if (i%3==0) printf("\n"); printf("%s:%d\t\t",names[i],i); } printf("\n\n"); if (argc<3) exit(1); if ((mixer_fd = open("/dev/mixer",O_RDWR))){ printf("Mixer opened successfully,working...\n"); value=BASE_VALUE*atoi(argv[2]); if (ioctl(mixer_fd,MIXER_WRITE(atoi(argv[1])),&value)==0) printf("successfully....."); else printf("unsuccessfully....."); printf("done.\n"); }else printf("can't open /dev/mixer error....\n"); ltesting.net exit(0); }