1. 程式人生 > >linux ioctl詳解1

linux ioctl詳解1

一個字元裝置驅動通常會實現常規的開啟、關閉、讀、寫等功能,但在一些細分的情境下,如果需要擴充套件新的功能,通常以增設ioctl()命令的方式實現,其作用類似於“拾遺補漏”。在檔案I/O中,ioctl扮演著重要角色,本文將以驅動開發為側重點,從使用者空間到核心空間縱向分析ioctl函式。

ioctl呼叫流程

使用者空間的ioctl()

#include <sys/ioctl.h> 

int ioctl(int fd, int cmd, ...) ;
  • 1
  • 2
  • 3
引數 描述
fd 檔案描述符
cmd 互動協議,裝置驅動將根據cmd執行對應操作
可變引數arg,依賴cmd指定長度以及型別

ioctl()執行成功時返回0,失敗則返回-1並設定全域性變數errorno值,如下:

EBADF d is not a valid descriptor.
EFAULT argp references an inaccessible memory area.
EINVAL Request or argp is not valid.
ENOTTY d is not associated with a character special device.
ENOTTY The specified request does not apply to the kind of object that the descriptor d references.

因此,在使用者空間使用ioctl時,可以做如下的出錯判斷以及處理:

    int ret;
    ret = ioctl(fd, MYCMD);
    if (ret == -1) {
        printf("ioctl: %s\n", strerror(errno));
    }
  • 1
  • 2
  • 3
  • 4
  • 5

tips: 在實際應用中,ioctl出錯時的errorno大部分是ENOTTY(error not a typewriter),顧名思義,即第一個引數fd指向的不是一個字元裝置,不支援ioctl操作,這時候應該檢查前面的open函式是否出錯或者裝置路徑是否正確。

驅動中的ioctl()

    long
(*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  • 1
  • 2

  在新版核心中,unlocked_ioctl()與compat_ioctl()取代了ioctl()。unlocked_ioctl(),顧名思義,應該在無大核心鎖(BKL)的情況下呼叫;compat_ioctl(),compat全稱compatible(相容的),主要目的是為64位系統提供32位ioctl的相容方法,也是在無大核心鎖的情況下呼叫。在《Linux Kernel Development》中對兩種ioctl方法有詳細的解說。
  

So Many Ioctls!
Not long ago, there existed only a single ioctlmethod. Today, there are three methods.unlocked_ioctl() is the same as ioctl(), except it is called without the Big KernelLock (BKL). It is thus up to the author of that function to ensure proper synchronization.Because the BKL is a coarse-grained, inefficient lock, drivers should implementunlocked_ioctl() and not ioctl().

compat_ioctl() is also called without the BKL, but its purpose is to provide a 32-bit compatible ioctl method for 64-bit systems. How you implement it depends on your existing ioctlcommands. Older drivers with implicitly sized types (such as long) should implement a compat_ioctl() method that works appropriately with 32-bit applications. This generally means translating the 32-bit values to the appropriate types for a 64-bit kernel. New driversthat have the luxury of designing their ioctl commands from scratch should ensure all their arguments and data are explicitly sized, safe for 32-bit apps on a 32-bit system, 32-bit apps on a 64-bit system, and 64-bit apps on a 64-bit system. These drivers can then point the compat_ioctl() function pointer at the same function as
unlocked_ioctl().

tips:在字元裝置驅動開發中,一般情況下只要實現unlocked_ioctl()即可,因為在vfs層的程式碼是直接呼叫unlocked_ioctl()。

// fs/ioctl.c

static long vfs_ioctl(struct file *filp, unsigned int cmd,
              unsigned long arg)
{
    int error = -ENOTTY;

    if (!filp->f_op || !filp->f_op->unlocked_ioctl)           
        goto out;

    error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
    if (error == -ENOIOCTLCMD) {
        error = -ENOTTY;
    }   
 out:
    return error;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

ioctl命令,使用者與驅動之間的協議

  前文提到ioctl方法第二個引數cmd為使用者與驅動的“協議”,理論上可以為任意int型資料,可以為0、1、2、3……,但是為了確保該“協議”的唯一性,ioctl命令應該使用更科學嚴謹的方法賦值,在linux中,提供了一種ioctl命令的統一格式,將32位int型資料劃分為四個位段,如下圖所示:
  

ioctl命令的分段組成

在核心中,提供了巨集介面以生成上述格式的ioctl命令:

// include/uapi/asm-generic/ioctl.h

#define _IOC(dir,type,nr,size) \
    (((dir)  << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr)   << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. dir(direction),ioctl命令訪問模式(資料傳輸方向),佔據2bit,可以為_IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分別指示了四種訪問模式:無資料、讀資料、寫資料、讀寫資料;

  2. type(device type),裝置型別,佔據8bit,在一些文獻中翻譯為“幻數”或者“魔數”,可以為任意char型字元,例如‘a’、‘b’、‘c’等等,其主要作用是使ioctl命令有唯一的裝置標識;
    tips:Documentions/ioctl-number.txt記錄了在核心中已經使用的“魔數”字元,為避免衝突,在自定義ioctl命令之前應該先查閱該文件。

  3. nr(number),命令編號/序數,佔據8bit,可以為任意unsigned char型資料,取值範圍0~255,如果定義了多個ioctl命令,通常從0開始編號遞增;

  4. size,涉及到ioctl第三個引數arg,佔據13bit或者14bit(體系相關,arm架構一般為14位),指定了arg的資料型別及長度,如果在驅動的ioctl實現中不檢查,通常可以忽略該引數。

通常而言,為了方便會使用巨集_IOC()衍生的介面來直接定義ioctl命令:

// include/uapi/asm-generic/ioctl.h

/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
_IO 定義不帶引數的ioctl命令
_IOW 定義帶寫引數的ioctl命令(copy_from_user)
_IOR 定義帶讀引數的ioctl命令(copy_to_user)
_IOWR 定義帶讀寫引數的ioctl命令

同時,核心還提供了反向解析ioctl命令的巨集介面:

// include/uapi/asm-generic/ioctl.h

/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

ioctl-test,例項分析

  本例假設一個帶暫存器的裝置,設計了一個ioctl介面實現裝置初始化、讀寫暫存器等功能。在本例中,為了攜帶更多的資料,ioctl的第三個可變引數為指標型別,指向自定義的結構體struct msg

1、ioctl-test.h,使用者空間和核心空間共用的標頭檔案,包含ioctl命令及相關巨集定義,可以理解為一份“協議”檔案,程式碼如下:

// ioctl-test.h

#ifndef __IOCTL_TEST_H__
#define __IOCTL_TEST_H__

#include <linux/ioctl.h>    // 核心空間
// #include <sys/ioctl.h>   // 使用者空間

/* 定義裝置型別 */
#define IOC_MAGIC  'c'

/* 初始化裝置 */
#define IOCINIT    _IO(IOC_MAGIC, 0)

/* 讀暫存器 */
#define IOCGREG    _IOW(IOC_MAGIC, 1, int)

/* 寫暫存器 */
#define IOCWREG    _IOR(IOC_MAGIC, 2, int)

#define IOC_MAXNR  3

struct msg {
    int addr;
    unsigned int data;
};

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2、ioctl-test-driver.c,字元裝置驅動,實現了unlocked_ioctl介面,根據上層使用者的cmd執行對應的操作(初始化裝置、讀暫存器、寫暫存器)。在接收上層cmd之前應該對其進行充分的檢查,流程及具體程式碼實現如下:

// ioctl-test-driver.c
......

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = test_open,
    .release = test_close,
    .read = test_read,
    .write = etst_write,
    .unlocked_ioctl = test_ioctl,
};

......

static long test_ioctl(struct file *file, unsigned int cmd, \
                        unsigned long arg)
{
    //printk("[%s]\n", __func__);

    int ret;
    struct msg my_msg;

    /* 檢查裝置型別 */
    if (_IOC_TYPE(cmd) != IOC_MAGIC) {
        pr_err("[%s] command type [%c] error!\n", \
            __func__, _IOC_TYPE(cmd));
        return -ENOTTY; 
    }

    /* 檢查序數 */
    if (_IOC_NR(cmd) > IOC_MAXNR) { 
        pr_err("[%s] command numer [%d] exceeded!\n", 
            __func__, _IOC_NR(cmd));
        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 IOCINIT:
        init();
        break;

    /* 讀暫存器 */
    case IOCGREG:
        ret = copy_from_user(&msg, \
            (struct msg __user *)arg, sizeof(my_msg));
        if (ret) 
            return -EFAULT;
        msg->data = read_reg(msg->addr);
        ret = copy_to_user((struct msg __user *)arg, \
                &msg, sizeof(my_msg));
        if (ret) 
            return -EFAULT;
        break;

    /* 寫暫存器 */
    case IOCWREG:
        ret = copy_from_user(&msg, \
            (struct msg __user *)arg, sizeof(my_msg));
        if (ret) 
            return -EFAULT;
        write_reg(msg->addr, msg->data);
        break;

    default:
        return -ENOTTY;
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

3、ioctl-test.c,執行在使用者空間的測試程式:

// ioctl-test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h> 

#include "ioctl-test.h"

int main(int argc, char **argv)
{

    int fd;
    int ret;
    struct msg my_msg;

    fd = open("/dev/ioctl-test", O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(-2);
    }

    /* 初始化裝置 */
    ret = ioctl(fd, IOCINIT);
    if (ret) {
        perror("ioctl init:");
        exit(-3);
    }

    /* 往暫存器0x01寫入資料0xef */
    memset(&my_msg, 0, sizeof(my_msg));
    my_msg.addr = 0x01;
    my_msg.data = 0xef;
    ret = ioctl(fd, IOCWREG, &my_msg);
    if (ret) {
        perror("ioctl read:");
        exit(-4);
    }

    /* 讀暫存器0x01 */
    memset(&my_msg, 0, sizeof(my_msg));
    my_msg.addr = 0x01;
    ret = ioctl(fd, IOCGREG, &my_msg);
    if (ret) {
        perror("ioctl write");
        exit(-5);
    }
    printf("read: %#x\n", my_msg.data);

    return 0;
}

相關推薦

linux ioctl1

一個字元裝置驅動通常會實現常規的開啟、關閉、讀、寫等功能,但在一些細分的情境下,如果需要擴充套件新的功能,通常以增設ioctl()命令的方式實現,其作用類似於“拾遺補漏”。在檔案I/O中,ioctl扮演著重要角色,本文將以驅動開發為側重點,從使用者空間到核心空間縱向分析ioc

linux入門1-ssh安裝配置及虛擬機基本使用

沒有 http ctr ble 輸入密碼 入門 p s start onf ssh配置 1,打開"終端窗口",輸入 "sudo apt-get update" --> 回車 --> "輸入當前登錄用戶的管理員密碼

linux中FTP服務搭建--1.匿名與權限

一行 ext enforce 實驗環境 沒有 allow umask pda yar 實驗環境:在redhat6.5中安裝配置FTP服務,並使用一臺win7系統訪問驗證。理論:FTP連接方式 支持兩種連接模式:主動模式(Port)和被動模式(Pasv),這兩種模式

Linux系統下的權限-1

實例 用戶 常用 表示 user chm 查看 運行 color 在Linux系統根下,通過使用ll 命令查看得出:Linux中常用權限有 r w x 如圖所示,權限共9位構成。(註:“-” 也表示一位)權限是賦給誰的?答:用戶(user)組(group)其他人(o

linux中apache服務1(企業級)(http\cgi\php\ssl)

curl -I www.jd.com 檢視網站用的哪些服務 curl -I www.taobao.com firewall-config runtime 當前允許的狀態 permanent 永久允許的 html超文字標記語言 yum install  httpd  htt

linux核心sysfs-1

sysfs 是 Linux 核心中設計較新的一種虛擬的基於記憶體的檔案系統,它的作用與 proc 有些類似,但除了與 proc 相同的具有檢視和設定核心引數功能之外,還有為 Linux 統一裝置模型作為管理之用。相比於 proc 檔案系統,使用 sysfs 匯出核心資料的方

pc端的企業網站(IT修真院test8)1-1

tom eight auto scrip 插件 html 樣式 繼承 代碼實現 這任務需求我們使用推特的前端框架bootstrap來實現。先放psd圖。 上傳這些圖片也蠻大的。為此我使用office picture manager壓縮了圖片。 方法:alt+p+o,然後

Linux特性

linux特性詳解 history 命令替換 命令別名 文件名統配 bash及其特性:shell: 外殼GUI:Gnome, KDE, XfceCLI: sh, csh, ksh, bash, tcsh, zsh Linux允許同一個用戶登錄多次root, student程序:進程 進程:

pc端的企業網站(IT修真院test8)1-4

isp city display hat borde 小圖標 test tro key 今天完成的事情:(1,偽元素:before,:after的使用。2.table的使用(collapse的使用)3rgba的高級運用) 今天我主要完成test8-3的頁面。 header

Nginx技術(1)

web服務器 nginxNginx Web服務應用:Nginx(engine x)是一個開源的,支持高並發的www服務和代理服務軟件。Nginx是俄羅斯人Igor Sysoev開發的,最初被應用到俄羅斯的大型網站(www.rambler.ru)上。後來作者將源代碼以類BSD許可證的形式開源出來供全球使用。在功

Linux LVM及創建

lvm 1. LVM基本創建及管理 2. LVM快照 3. LVM與RAID的結合使用:基於RAID的LVMLVM創建: 描述: LVM是邏輯盤卷管理(LogicalVolumeManager)的簡稱,它是Linux環境下對磁盤分區進行管理的一種機制,LVM是建立在硬盤和分區之上的一個邏輯層,來提高磁盤分

linux rsyslog

syslog priority facility rsyslog 概念和特性歷史日誌、歷史事件:時間、事件本身、日誌級別(根據時間的關鍵性程度)系統日誌服務:syslog有兩個進程syslogd(system負責用戶進程)、 klogd(kernel負責內核進程)centos7:rsyslog

Linux 命令(十)Shell腳本的數組

cti err art case lin start shell pre round 1、數組定義 [[email protected] ~]# a=(1 2 3 4 5 6 7 8) [[email protected]-IDC ~]# echo $

(轉)Linux命令-file

版本信息 ref 獲取文件 linux命令 過程 嘗試 file img 文件類型 Linux命令詳解-file 原文:https://www.cnblogs.com/Dodge/p/4278306.html file命令用來識別文件類型,也可用來辨別一些文件的編碼格

linux目錄

style 啟動過程 usr 位置 cpu信息 pos 過程 scripts strong 網卡的配置文件目錄 /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0

flask中jinjia2模板引擎使用1

模板 文本文 安全 檢查 引擎 分解 擴展名 結果 解釋 在之前的文章中我們介紹過flask調用jinja2模板的基本使用,這次我們來說一下jinjia2模板的使用 Jinja2 在其是一個 Python 2.4 庫之前,被設計 為是靈活、快速和安全的。 模板僅僅是文本文件

Docker 基礎技術之 Linux namespace

基本 mar $$ 裏的 sta 進程資源 進程間通信 開始 消息隊列 Docker 是“新瓶裝舊酒”的產物,依賴於 Linux 內核技術 chroot 、namespace 和 cgroup。本篇先來看 namespace 技術。 Docker 和虛擬機技術一樣,從操作系

linux管道

linux原文鏈接:http://blog.csdn.net/qq_38646470/article/details/79564392 #符號表示| 和管道特別形象。#作用:    管道是Linux中很重要的一種通信方式,是把一個程序的輸出直接連接到另一個程序的輸入

Linux命令(部分昨今兩天)

Linux命令詳解基本命令1.Linux的基本原則:1、由目的單一的小程序組成;組合小程序完成復雜任務;2、一切皆文件;3、盡量避免捕獲用戶接口;(盡量不和用戶進行交互,就是一個程序一但開始運行,就不需要用戶進行任何操作,如ls命令,ifconfig命令)4、配置文件保存為純文本格式;2.命令形式命令格式:命

Linux命令

Linux命令詳解路徑:絕對路徑:凡是以“/”開頭的輸入路徑的方式都是絕對路徑相對路徑:凡是以“.”或者“..”開頭的都是相對路徑查看服務器基本信息:cat /proc/cpuinfo 查看cpu信息cat /proc/meminfo 查看內存信息free 查看內存使用情況uptime 監控CPU情況unam