linux ioctl与ptrace(转载+整理)

来源:互联网 发布:手机淘宝看实名认证 编辑:程序博客网 时间:2024/05/20 01:10

#include <sys/ptrace.h>

 

intptrace(int request, intpid, intaddr, intdata);

 

 

描述

 

Ptrace提供了一种父进程可以控制子进程运行,并可以检查和改变它的核心image。它主要用于实现断点调试。一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。

 

Request参数决定了系统调用的功能:

 

PTRACE_TRACEME

 

本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。

 

PTRACE_PEEKTEXT, PTRACE_PEEKDATA

 

从内存地址中读取一个字节,内存地址由addr给出。

 

PTRACE_PEEKUSR

 

从USER区域中读取一个字节,偏移量为addr。

 

PTRACE_POKETEXT, PTRACE_POKEDATA

 

往内存地址中写入一个字节。内存地址由addr给出。

 

PTRACE_POKEUSR

 

往USER区域中写入一个字节。偏移量为addr。

 

PTRACE_SYSCALL, PTRACE_CONT

 

重新运行。

 

PTRACE_KILL

 

杀掉子进程,使它退出。

 

PTRACE_SINGLESTEP

 

设置单步执行标志

 

PTRACE_ATTACH

 

跟踪指定pid进程。

 

PTRACE_DETACH

 

结束跟踪

 

Intel386特有:

 

PTRACE_GETREGS

 

读取寄存器

 

PTRACE_SETREGS

 

设置寄存器

 

PTRACE_GETFPREGS

 

读取浮点寄存器

 

PTRACE_SETFPREGS

 

设置浮点寄存器

 

init进程不可以使用此函数

 

 

返回值

 

成功返回0。错误返回-1。errno被设置。

 

 

错误

 

EPERM

 

特殊进程不可以被跟踪或进程已经被跟踪。

 

ESRCH

 

指定的进程不存在

 

EIO

 

请求非法

 

ptrace系统函数。ptrace提供了一种使父进程得以监视和控制其它进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现断点调试和系统调用的跟踪。使用ptrace,你可以在用户层拦截和修改系统调用(sys call).

 

功能详细描述

1)   PTRACE_TRACEME

形式:ptrace(PTRACE_TRACEME,0 ,0 ,0)

 

描述:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。

 

 

2) PTRACE_PEEKTEXT, PTRACE_PEEKDATA

形式:ptrace(PTRACE_PEEKTEXT, pid, addr,data)

 

ptrace(PTRACE_PEEKDATA, pid, addr, data)

 

描述:从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。

 

 

3) PTRACE_POKETEXT, PTRACE_POKEDATA

形式:ptrace(PTRACE_POKETEXT, pid, addr,data)

 

ptrace(PTRACE_POKEDATA, pid, addr, data)

 

描述:往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。

 

 

4) TRACE_PEEKUSR

形式:ptrace(PTRACE_PEEKUSR, pid, addr, data)

 

描述:从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。

 

 

5)  PTRACE_POKEUSR

形式:ptrace(PTRACE_POKEUSR, pid, addr, data)

 

描述:往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。

 

 

6)  PTRACE_CONT

形式:ptrace(PTRACE_CONT, pid, 0, signal)

 

描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。

 

 

7) PTRACE_SYSCALL

形式:ptrace(PTRACE_SYS, pid, 0, signal)

 

描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。

 

 

8)  PTRACE_KILL

形式:ptrace(PTRACE_KILL,pid)

 

描述:杀掉子进程,使它退出。pid表示被跟踪的子进程。

 

 

9)   PTRACE_SINGLESTEP

形式:ptrace(PTRACE_KILL, pid, 0, signle)

 

描述:设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。

 

 

10) PTRACE_ATTACH

形式:ptrace(PTRACE_ATTACH,pid)

 

描述:跟踪指定pid进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。

 

 

11) PTRACE_DETACH

形式:ptrace(PTRACE_DETACH,pid)

 

描述:结束跟踪。pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。

 

 

12) PTRACE_GETREGS

形式:ptrace(PTRACE_GETREGS, pid, 0, data)

 

描述:读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。

 

 

13) PTRACE_SETREGS

形式:ptrace(PTRACE_SETREGS, pid, 0, data)

 

描述:设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。

 

 

14) PTRACE_GETFPREGS

形式:ptrace(PTRACE_GETFPREGS, pid, 0, data)

 

描述:读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有浮点协处理器387的所有寄存器的值。

 

 

15) PTRACE_SETFPREGS

形式:ptrace(PTRACE_SETREGS, pid, 0, data)

 

描述:设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有浮点协处理器387的所有寄存器的值。

 

 

 

一、什么是ioctl
      ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数
      如下:
      intioctl(intfd,indcmd, …);
      其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和 cmd的意义相关的。
      ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支 持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。
二、ioctl的必要性
      如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。
      所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要 做的事情。
三、ioctl如何实现
      这是一个很麻烦的问题,我是能省则省。要说清楚它,没有四五千字是不行的,所以我这里是不可能把它说得非常清楚了,不过如果有读者对用户程序怎么和驱动程序联系起来感兴趣的话,可以看我前一阵子写的《write的奥秘》。读者只要把write换成ioctl,就知道用户程序的ioctl是怎么和驱动程序中的ioctl实现联系在一起的了。
      我这里说一个大概思路,因为我觉得《Linux设备驱动程序》这本书已经说的非常清楚了,但是得化一些时间来看。
      在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事 情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。 命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,这样才不会将正确的命令发给错误的设备,或者是把错误的命令发给正确的设备,或者是把错误的
      命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将是非常困难的事情。
      所以在Linux核心中是这样定义一个命令码的:
      ____________________________________
      | 设备类型 | 序列号 | 方向 |数据尺寸|
      |----------|--------|------|--------|
      | 8 bit    |  8 bit |2 bit |8~14bit|
      |----------|--------|------|--------|

      这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以 Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。
      这些宏我就不在这里解释了,具体的形式请读者察看Linux核心源代码中的和,文件里给 出了这些宏完整的定义。这里我只多说一个地方,那就是"幻数"。
      幻数是一个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一个数字是一样的,只是更加利于记忆和理解。就是这样,再没有更复杂的了。更多的说了也没有,读者还是看一看源代码吧,推荐各位阅读《Linux 设备驱动程序》所带源代码中的short一例,因为它比较短小,功能比较简单,可以看明白ioctl的功能和细节。
四、cmd参数如何得出
      这里确实要说一说,cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switch{case}结构进行相应的操作。
      要透彻理解,只能是通过阅读源代码,我这篇文章实际上只是一个引子。Cmd参数的组织还是比较复杂的,我认为要搞熟它还是得花不少时间的,但是这是值得的,驱动程序中最难的是对中断的理解。

 五、小结
      ioctl其实没有什么很难的东西需要理解,关键是理解cmd命令码是怎么在用户程序里生成并在驱动程序里解析的,程序员最主要的工作量在switch{case}结构中,因为对设备的I/O控制都是通过这一部分的代码实现的。

原创粉丝点击