V4l2的结构体 --- ioctl【转】
在应用程序获取视频数据的流程中,都是通过 ioctl 命令与驱动程序进行交互,常见的 ioctl 命令有:
VIDIOC_QUERYCAP /* 获取设备支持的操作 */
VIDIOC_G_FMT /* 获取设置支持的视频格式 */
VIDIOC_S_FMT /* 设置捕获视频的格式 */
VIDIOC_REQBUFS /* 向驱动提出申请内存的请求 */
VIDIOC_QUERYBUF /* 向驱动查询申请到的内存 */
VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列 */
VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
VIDIOC_STREAMON /* 打开视频流 */
VIDIOC_STREAMOFF /* 关闭视频流 */
VIDIOC_QUERYCTRL /* 查询驱动是否支持该命令 */
VIDIOC_G_CTRL /* 获取当前命令值 */
VIDIOC_S_CTRL /* 设置新的命令值 */
VIDIOC_G_TUNER /* 获取调谐器信息 */
VIDIOC_S_TUNER /* 设置调谐器信息 */
VIDIOC_G_FREQUENCY /* 获取调谐器频率 */
VIDIOC_S_FREQUENCY /* 设置调谐器频率 */
1、struct v4l2_capability 与 VIDIOC_QUERYCAP
VIDIOC_QUERYCAP 命令通过结构 v4l2_capability 获取设备支持的操作模式:
struct v4l2_capability {
__u8 driver[]; /* i.e. "bttv" */
__u8 card[]; /* i.e. "Hauppauge WinTV" */
__u8 bus_info[]; /* "PCI:" + pci_name(pci_dev) */
__u32 version; /* should use KERNEL_VERSION() */
__u32 capabilities; /* Device capabilities */
__u32 reserved[];
};
其中域 capabilities 代表设备支持的操作模式,常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一个视频捕捉设备并且具有数据流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。
2、struct v4l2_format 与 VIDIOC_G_FMT、VIDIOC_S_FMT、VIDIOC_TRY_FMT
通常用 VIDIOC_S_FMT 命令通过结构 v4l2_format 初始化捕获视频的格式,如果要改变格式则用 VIDIOC_TRY_FMT 命令:
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
__u8 raw_data[]; /* user-defined */
} fmt;
};
其中
enum v4l2_buf_type {
V4L2_BUF_TYPE_VIDEO_CAPTURE = ,
V4L2_BUF_TYPE_VIDEO_OUTPUT = ,
V4L2_BUF_TYPE_VIDEO_OVERLAY = ,
...
V4L2_BUF_TYPE_PRIVATE = 0x80,
}; struct v4l2_pix_format {
__u32 width;
__u32 height;
__u32 pixelformat;
enum v4l2_field field;
__u32 bytesperline; /* for padding, zero if unused */
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv; /* private data, depends on pixelformat */
};
3、struct v4l2_requestbuffers 与 VIDIOC_REQBUFS
VIDIOC_REQBUFS 命令通过结构 v4l2_requestbuffers 请求驱动申请一片连续的内存用于缓存视频信息:
struct v4l2_requestbuffers {
__u32 count;
enum v4l2_buf_type type;
enum v4l2_memory memory;
__u32 reserved[];
};
其中
enum v4l2_memory {
V4L2_MEMORY_MMAP = ,
V4L2_MEMORY_USERPTR = ,
V4L2_MEMORY_OVERLAY = ,
};
count 指定根据图像占用空间大小申请的缓存区个数,type 为视频捕获模式,memory 为内存区的使用方式。
4、struct v4l2_buffer与 VIDIOC_QUERYBUF
VIDIOC_QUERYBUF 命令通过结构 v4l2_buffer 查询驱动申请的内存区信息:
struct v4l2_buffer {
__u32 index;
enum v4l2_buf_type type;
__u32 bytesused;
__u32 flags;
enum v4l2_field field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence; /* memory location */
enum v4l2_memory memory;
union {
__u32 offset;
unsigned long userptr;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
5、enum v4l2_buf_type 与 VIDIOC_STREAMON、VIDIOC_STREAMOFF
这两个命令使用的只是一个整形数据,即 v4l2_buf_type,一般只要指定其值为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即可。
6、struct v4l2_queryctrl 与 VIDIOC_QUERYCTRL
VIDIOC_QUERYCTRL 命令通过结构 v4l2_queryctrl 查询驱动是否支持该 id 代表的命令,并返回该命令的各种参数:
struct v4l2_queryctrl {
__u32 id; /* 命令编号 */
enum v4l2_ctrl_type type; /* 命令值的类型 */
__u8 name[]; /* 命令名称*/
__s32 minimum; /* 最小的命令值 */
__s32 maximum; /* 最大的命令值 */
__s32 step; /* 命令值变化的步长 */
__s32 default_value; /* 默认的命令值 */
__u32 flags; /* 命令的标志 */
__u32 reserved[]; /* 命令值的位图表示 */
};
其中
enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_INTEGER = , /* 整形 */
V4L2_CTRL_TYPE_BOOLEAN = , /* 真值 */
V4L2_CTRL_TYPE_MENU = , /* 菜单 */
V4L2_CTRL_TYPE_BUTTON = , /* 无值 */
V4L2_CTRL_TYPE_INTEGER64 = , /* 后面三种不常用 */
V4L2_CTRL_TYPE_CTRL_CLASS = ,
V4L2_CTRL_TYPE_STRING = ,
};
命令的标志取值如下:
/* Control flags */
#define V4L2_CTRL_FLAG_DISABLED 0x0001
#define V4L2_CTRL_FLAG_GRABBED 0x0002
#define V4L2_CTRL_FLAG_READ_ONLY 0x0004
#define V4L2_CTRL_FLAG_UPDATE 0x0008
#define V4L2_CTRL_FLAG_INACTIVE 0x0010
#define V4L2_CTRL_FLAG_SLIDER 0x0020
#define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040 /* Query flag, to be ORed with the control ID */
#define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
id 是命令的编号,常见的命令有两种:一种以 V4L2_CID_BASE 为起始值,是公用命令;一种以 V4L2_CID_PRIVATE_BASE 为起始值,是私有命令。在一般的应用中命令值可见如下:
V4L2_CID_CONTRAST (V4L2_CID_BASE+) /* 对比度调节 */
V4L2_CID_SATURATION (V4L2_CID_BASE+) /* 饱和度调节 */
V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+) /* 音量调节 */
V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+) /* 静音设置 */
V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+) /* 白平衡调节 */
V4L2_CID_GAMMA (V4L2_CID_BASE+) /* 伽马值调节 */
V4L2_CID_EXPOSURE (V4L2_CID_BASE+) /* 曝光度调节 */ V4L2_CID_PRIVATE_ATXX_FLASH (V4L2_CID_PRIVATE_BASE + ) /* 闪光灯控制 */
V4L2_CID_PRIVATE_ATXX_FRAME (V4L2_CID_PRIVATE_BASE + ) /* 帧率调节 */
type 为命令值的类型(总共有7中类型的值),name 是命令的名称,reserved 则是命令值的位图表示,驱动会将所有的命令值都以 bit 的形式写到 64 位的域中,上层应用查询时可以根据位图判断命令支持的值。
7、struct v4l2_control 与 VIDIOC_G_CTRL、VIDIOC_S_CTRL
VIDIOC_S_CTRL 或 VIDIOC_G_CTRL 命令通过结构 v4l2_control 设置或者获取 id 命令的值:
struct v4l2_control { __u32 id; __s32 value; };
8、struct v4l2_tuner 与 VIDIOC_G_TUNER、VIDIOC_S_TUNER
VIDIOC_S_TUNER 或 VIDIOC_G_TUNER 命令通过结构 v4l2_tuner 设置调谐器的信息:
struct v4l2_tuner {
__u32 index; /* 调谐器编号,由应用程序设置 */
__u8 name[]; /* 调谐器名称 */
enum v4l2_tuner_type type; /* 调谐器类型 */
__u32 capability; /* 调谐器支持的操作 */
__u32 rangelow; /* 最低频率值,单位为62.5Hz或者62.5KHz */
__u32 rangehigh; /* 最高频率值 */
__u32 rxsubchans; /* 接收的音频信号类型 */
__u32 audmode; /* 当前音频播放形式 */
__s32 signal; /* 信号强度 */
__s32 afc; /* 自动频率控制 */
__u32 reserved[]; /* 保留备用 */
};
其中
enum v4l2_tuner_type {
V4L2_TUNER_RADIO = , /* 调频收音机 */
V4L2_TUNER_ANALOG_TV = , /* 模拟电视高频头 */
V4L2_TUNER_DIGITAL_TV = , /* 数字电视高频头 */
};
其中域 type 有三种类型;capability 域一般为 V4L2_TUNER_CAP_LOW,表明频率调节的步长是62.5Hz,如果没有这个标志位则步长为62.5KHz;rangelow 与 rangehigh 是调谐器可以调频率的最高值和最低值,但都以步长为单位表示;rxsubchans 表示调谐器接收的音频信号类型,常见值有 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO 即单声道与立体声;audmode 表示以何种方式播放声音,常见值有 V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO,即以单声道还是立体声的方式播放;signal 为当前信号强度,一般取值范围为 0 - 65535。
9、struct v4l2_frequency 与 VIDIOC_G_FREQUENCY、VIDIOC_S_FREQUENCY
VIDIOC_S_FREQUENCY 或 VIDIOC_G_FREQUENCY 命令通过结构 v4l2_frequency 设置或获取当前频率值:
struct v4l2_frequency {
__u32 tuner; /* 调谐器编号 */
enum v4l2_tuner_type type; /* 调谐器类型 */
__u32 frequency; /* 调谐器频率 */
__u32 reserved[];
};
注意:frequency 的值是以62.5Hz 或者 62.5KHZ 为单位的。
附:_IO、_IOR、_IOW、_IOWR 宏的使用说明
驱动程序中 ioctl 函数传递的变量 cmd 是应用程序向驱动程序请求处理的命令。cmd 除了用于区别不同命令的数值,还可包含有助于处理的几种信息。cmd 的大小为 32 bit,共分 4 个域:
bit29 ~ bit31: 3bit 为 “读写” 区,作用是区分是读命令还是写命令。
bit16 ~ bit28:13bit 为 "数据大小" 区,表示 ioctl 中的 arg 变量传递的数据大小;有时候为 14bit 即将 bit29 覆盖。
bit8 ~ bit15: 8bit 为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
bit0 ~ bit7: 8bit 为 "序号" 区,是区分命令的命令顺序序号。
魔数(magic number)
魔数范围为 0~255 。通常,用英文字符 'A' ~ 'Z' 或者 'a' ~ 'z' 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助参数。设备驱动程序可以通过宏 _IOC_TYPE (cmd) 来获取魔数。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。
基数(序号)
基数用于区别各种命令。通常,从 0开始递增,相同设备驱动程序上可以重复使用该值。例如,读和写命令中使用了相同的基数,设备驱动程序也能分辨出来,原因在于设备驱动程序区分命令时使用 switch ,且直接使用命令变量 cmd 值。创建命令的宏生成的值由多个域组合而成,所以即使是相同的基数,也会判断为不同的命令。设备驱动程序想要从命令中获取该基数,就使用宏 _IOC_NR (cmd)。
下面我们看一下上述宏在内核中的原型:
下面我们看一下上述宏在内核中的原型
:
/*
* Our DIR and SIZE overlap in order to simulteneously provide
* a non-zero _IOC_NONE (for binary compatibility) and
* 14 bits of size as on i386. Here's the layout:
*
* 0xE0000000 DIR 3bit
* 0x80000000 DIR = WRITE bit31
* 0x40000000 DIR = READ bit30
* 0x20000000 DIR = NONE bit29
* 0x3FFF0000 SIZE (overlaps NONE bit) 13bit
* 0x0000FF00 TYPE 8bit
* 0x000000FF NR (CMD) 8bit
*/
/* 各个域的长度 */
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 13 /* Actually 14, see below. */
#define _IOC_DIRBITS 3
/* 各个域的掩码 */
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_XSIZEMASK ((1 << (_IOC_SIZEBITS+1))-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
/* 各个域的偏移 */
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS) /* 8 */
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS) /* 16 */
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS) /* 29 */
/* 读写域的值 */
#define _IOC_NONE 1U
#define _IOC_READ 2U
#define _IOC_WRITE 4U #define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \ /* 读写方向左移 29bit */
((type) << _IOC_TYPESHIFT) | \ /* 幻数左移 8bit */
((nr) << _IOC_NRSHIFT) | \ /* 命令序号 */
((size) << _IOC_SIZESHIFT)) /* 参数大小左移 16bit */
/* 宏原型,这里将会根据传递的数据类型取其长度 */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/* 获取各个域的值 */
#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)
这里特别说明一下 _IO 宏,该宏没有可传递的变量,只用于发送命令。这是因为变量需要可变数据,只作为命令(比如 reset)使用时,没有必要判断设备上的数据,因此设备驱动程序没有必要执行文件相关的处理。在 v4l2 中使用示例如下:
#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability)
#define VIDIOC_RESERVED _IO('V', 1)
#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
#define VIDIOC_STREAMON _IOW('V', 18, int)
v4l2 中对上述宏命令的处理在 video_ioctl2 函数中:
static unsigned long cmd_input_size(unsigned int cmd)
{
#define CMDINSIZE(cmd, type, field) \
case VIDIOC_##cmd: \
return offsetof(struct v4l2_##type, field) + \ /* 域的偏移 */
sizeof(((struct v4l2_##type *))->field); /* 域的长度 */ switch (cmd) {
CMDINSIZE(ENUM_FMT, fmtdesc, type);
CMDINSIZE(G_FMT, format, type);
...
CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
default:
return _IOC_SIZE(cmd); /* 剩下的是需要全部拷贝的命令 */
}
} long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
{
char sbuf[]; /* 在栈中分配128个字节空间用来储存命令的参数 */
void *mbuf = NULL;
void *parg = NULL; /* 参数存放的首地址 */
long err = -EINVAL;
int is_ext_ctrl;
size_t ctrls_size = ;
void __user *user_ptr = NULL; ...
/* 判断是否包含读写命令,如果是则将用户空间的参数值拷贝到内核 */
if (_IOC_DIR(cmd) != _IOC_NONE) {
/* 判断参数大小是否超过128字节 */
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* 如果超过128字节则从堆中申请 */
mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
} err = -EFAULT;
/* 如果包含写命令 */
if (_IOC_DIR(cmd) & _IOC_WRITE) {
/* 计算需要拷贝的有效数据长度,有的命令不需要全部拷贝 */
unsigned long n = cmd_input_size(cmd);
/* 从用户空间拷贝参数值 */
if (copy_from_user(parg, (void __user *)arg, n))
goto out; /* 将剩下的空间清零 */
if (n < _IOC_SIZE(cmd))
memset((u8 *)parg + n, , _IOC_SIZE(cmd) - n);
} else {
/* 如果是只读命令则将整个buffer清零 */
memset(parg, , _IOC_SIZE(cmd));
}
} ...
/* 调用 v4l2_ioctl_ops 的成员函数处理命令 */
err = __video_do_ioctl(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
...
if (err < )
goto out; out_ext_ctrl:
/* 如果包含读命令则将参数值拷贝到用户空间 */
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
} out:
kfree(mbuf);
return err;
}
EXPORT_SYMBOL(video_ioctl2);
然后我们在 struct v4l2_file_operations 中将 ioctl 成员设置为 video_ioctl2 即可
V4l2的结构体 --- ioctl【转】的更多相关文章
- 23、V4L2应用编写及各个ioctl涉及结构体说明分析
常用的结构体在内核目录include/linux/videodev2.h中定义 struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFSstruct ...
- 二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写
一.V4L2框架主要结构体分析 V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口. V4L2整体框架如下图: 图中主要包括两层 ...
- 获取网络接口信息——ioctl()函数与结构体struct ifreq、 struct ifconf
转载请注明出处:windeal专栏 Linux 下 可以使用ioctl()函数 以及 结构体 struct ifreq 结构体struct ifconf来获取网络接口的各种信息. ioctl 首先看 ...
- 【AT91SAM3S】SAM3S-EK Demo工程中,LCD驱动程序的加载(函数指针结构体)
为了调试LCD,在英倍特的板子上烧Atmel的sam3s-ek_demo_1.4_source示例代码.LCD显示正常了,却找不到LCD的驱动究竟在哪. 花了好久,追踪到了这个执行过程. 进入main ...
- ARM-Linux S5PV210 UART驱动(3)----串口核心层、关键结构体、接口关系
尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实 ...
- cdev成员结构体file_operations文件操作结构的分析
struct file_operations{ struct module *owner; // 指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES loff_t ...
- file_operations结构体解析 1
注:学了这么长时间了,还没有好好看看 file_operations机构体,这其中还有很多的东西,当你学着学着的时候,就会用到这里面的一些系统调用对应的函数了,我在网上搜索之后,记录如下,一边将来查看 ...
- 两个结构体ifconf和ifreq
用ioctl获得本地ip地址时要用到两个结构体ifconf和ifreq,它们对于大多数人来说都是比较陌生的,这里给大家一种比较简单的理解方法,当然只一种帮助理解的方法,在描述中可能会有一些地方与真实定 ...
- struct ifreq结构体与ip,子网掩码,网关等信息
总结一下,今天学习的关于通过socket,ioctl来获得ip,netmask等信息,其中很多内容参照了很多网上的信息,我会一一列出的 我用的这个函数,就是下面这个函数,其中的有一些全局变量,很好懂, ...
随机推荐
- CS:APP3e 深入理解计算机系统_3e C Programming Lab实验
queue.h: /* * Code for basic C skills diagnostic. * Developed for courses 15-213/18-213/15-513 by R. ...
- 【java】实例化对象的3种方式:new、clone、反射
实例化对象的3种方式:new.clone.反射
- 【JMeter】source("文件路程")和${变量}同时出现会报错
source("D:\\apache-jmeter-3.0\\testcase\\java\\Test.java"); //${journeyLen} 以上两句在JMeter脚本里 ...
- Django的设计模式
MVC模式 MVC将应用程序分解为三个组成部分:mode(模型).view(视图).control(控制器),其中: M 管理应用程序的状态(通常存储到数据库中),并榆树改变状态的行为(或者叫&quo ...
- iOS UICollectionView(转一) XIB+纯代码创建:cell,头脚视图 cell间距
之前用CollectionViewController只是皮毛,一些iOS从入门到精通的书上也是泛泛而谈.这几天好好的搞了搞苹果的开发文档上CollectionViewController的内容,亲身 ...
- (转)iOS-Runtime知识点整理
runtime简介 因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时.也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译后的 ...
- CSS3关于过渡效果的问题
首先trasition:transform只是单单表示后面只要有含有的tranform的所有属性可以参与动画,而trasition:all表示后面所有动画属性都可以参动画,当父容器有relative时 ...
- vue2.0路由变化1
路由的步骤 1.定义组件 var Home={ template:'<h3>我是主页</h3>' }; var News={ template:'<h3>我是新闻& ...
- Linux第七节随笔 diff /uniq /stat
linux第七讲(上)1.diff link 作用:diff命令能比较单个文件或者目录内容.如果指定比较的是文件,则只有当输入为文本文件时才有效.以逐行的方式,比较文本文件的异同处. 如果指定比较的是 ...
- 3、debian8安装和处理
本博文仅作本人操作过程的记录,留作备忘.自强不息 QQ1222698 本文写于2016年1月10日09:35:45,首先向debian的创始人Ian Murdock表示沉痛惦念! http://bai ...