ioctl (I/O Control) 是字元裝置驅動程式與用戶空間交換控制資訊的標準介面。不同於 read/write 傳輸資料流,ioctl 處理裝置專屬的「控制命令」:設定鮑率、啟動 DMA、查詢感測器狀態、配置 GPIO 等。
// 用戶空間 (glibc 包裝)
#include
int ioctl(int fd, unsigned long request, ...);
// 核心空間 (驅動實作)
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
// 舊版
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
fd:open() 回傳的檔案描述符request/cmd:編碼後的命令碼 (包含類型、序號、方向、資料大小)arg:使用者空間緩衝區位址 (輸入/輸出/雙向)32 位元整數編碼 (定義於 asm-generic/ioctl.h):
| 類型 (8 bits) | 序號 (8 bits) | 方向 (2 bits) | 大小 (14 bits) |
| 'V' (0x56) | 0x01 | _IOC_WRITE | sizeof(struct) |
'V' 視訊、'K' 鍵盤、'M' MTD)_IOC_NONE(0) _IOC_READ(1) _IOC_WRITE(2) _IOC_READ|_IOC_WRITE(3) — 從核心視角 (READ = 核心→用戶)// 無參數
#define MY_IOCTL_RESET _IO('M', 0)
// 讀取 (核心→用戶)
#define MY_IOCTL_GET_STATUS _IOR('M', 1, struct my_status)
// 寫入 (用戶→核心)
#define MY_IOCTL_SET_CONFIG _IOW('M', 2, struct my_config)
// 雙向
#define MY_IOCTL_EXCHANGE _IOWR('M', 3, struct my_exchange)
// my_char_drv.c
#include
#include "my_ioctl_cmds.h" // 共用命令碼定義
static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
struct my_device *dev = filp->private_data;
int ret = 0;
// 驗證魔數與序號 (防止誤傳他裝置命令)
if (_IOC_TYPE(cmd) != 'M') return -ENOTTY;
if (_IOC_NR(cmd) > MY_IOCTL_MAXNR) return -ENOTTY;
// 驗證資料大小 (防止緩衝區溢位)
if (_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (ret) return -EFAULT;
switch (cmd) {
case MY_IOCTL_RESET:
reset_device(dev);
break;
case MY_IOCTL_GET_STATUS:
if (copy_to_user((void __user *)arg, &dev->status, sizeof(dev->status)))
return -EFAULT;
break;
case MY_IOCTL_SET_CONFIG:
if (copy_from_user(&dev->config, (void __user *)arg, sizeof(dev->config)))
return -EFAULT;
apply_config(dev);
break;
default:
return -ENOTTY; // 未知命令
}
return 0;
}
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = my_ioctl,
.compat_ioctl = compat_ptr_ioctl, // 32-bit 相容
// ... open, release, read, write
};
#include
#include
#include "my_ioctl_cmds.h"
int main() {
int fd = open("/dev/mydev", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
struct my_config cfg = { .rate = 115200, .mode = 1 };
if (ioctl(fd, MY_IOCTL_SET_CONFIG, &cfg) < 0)
perror("ioctl SET_CONFIG");
struct my_status st;
if (ioctl(fd, MY_IOCTL_GET_STATUS, &st) < 0)
perror("ioctl GET_STATUS");
printf("Status: %d\n", st.value);
close(fd);
return 0;
}
access_ok() 驗證用戶空間位址合法compat_ioctl 處理結構體大小差異-EFAULT, -ENOTTY, -EINVAL)| 裝置類型 | 常用 ioctl | 用途 |
|---|---|---|
| 終端機 (tty) | TCGETS/TCSETS, TIOCGWINSZ | 取得/設定終端屬性、視窗大小 |
| 網路介面 | SIOCGIFCONF, SIOCSIFADDR | 介面配置、IP 位址設定 |
| 區塊裝置 | BLKGETSIZE64, BLKFLSBUF | 取得磁碟大小、刷新快取 |
| 音效 (ALSA) | SNDRV_CTL_IOCTL_* | 混音器控制、PCM 參數 |
| 視訊 (V4L2) | VIDIOC_G_FMT, VIDIOC_S_FMT | 格式協商、緩衝區管理 |
# 追蹤 ioctl 呼叫
strace -e trace=ioctl ./myapp
# 核心動態追蹤 (需 debugfs)
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_ioctl/enable
cat /sys/kernel/debug/tracing/trace_pipe
ioctl 雖介面簡單,但命令碼編碼、資料複製方向、版本相容、安全檢查環環相扣。驅動開發者必須精通 _IO/_IOR/_IOW/_IOWR 巨集與 copy_to/from_user 機制,才能寫出健壯、安全的字元裝置驅動。