下面以eeprom用户程序调用ioctl函数的写操作为例追踪IIC子系统的调用过程。eeprom的用户测试是大部分开发板都自带的。看写一个字节数据的eeprom_write_byte函数的定义:

int eeprom_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data)
{
if(e->type == EEPROM_TYPE_8BIT_ADDR) {
__u8 buf[] = { mem_addr & 0x00ff, data };
return i2c_write_2b(e, buf);
} else if(e->type == EEPROM_TYPE_16BIT_ADDR) {
__u8 buf[] =
{ (mem_addr >> ) & 0x00ff, mem_addr & 0x00ff, data };
return i2c_write_3b(e, buf);
}
fprintf(stderr, "ERR: unknown eeprom type\n");
return -;
}

这里使用的是8位地址,因此调用的是i2c_write_2b函数,为什么是2b?这是eeprom规定的,写数据之前要先写地址。注意buf[0]=要写的地址,buf[1]=要写的字节数据。下面是i2c_write_2b函数的定义:

static int i2c_write_2b(struct eeprom *e, __u8 buf[])
{
int r;
// we must simulate a plain I2C byte write with SMBus functions
r = i2c_smbus_write_byte_data(e->fd, buf[], buf[]);
if(r < )
fprintf(stderr, "Error i2c_write_2b: %s\n", strerror(errno));
usleep();
return r;
}

就调用了i2c_smbus_write_byte_data函数,注意参数的含义,下面是它的定义:

static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command,
__u8 value)
{
union i2c_smbus_data data;
data.byte = value;
return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
I2C_SMBUS_BYTE_DATA, &data);
}

调用了i2c_smbus_access函数,继续追踪,看i2c_smbus_access函数的定义:

static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args; args.read_write = read_write;
args.command = command;
args.size = size;
args.data = data;
return ioctl(file,I2C_SMBUS,&args);
}

首先弄清楚参数的含义,file是使用open打开的文件。read_write表示是读操作还是写操作,这里是写,所以它的值为I2C_SMBUS_WRITE。command是要写的地址。size的值为I2C_SMBUS_BYTE_DATA。最后一个参数data.byte=要写的字节数据。

下面开始进入ioctl系统调用,最后会到达i2c-dev.c中的i2cdev_ioctl函数,看它的定义:

 static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg); switch (cmd) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
00000408 * can't use I2C_SLAVE, even when the device node is not
00000409 * bound to a driver. Only I2C_SLAVE_FORCE will work.
00000410 *
00000411 * Setting the PEC flag here won't affect kernel drivers,
00000412 * which will be using the i2c_client node registered with
00000413 * the driver model core. Likewise, when that client has
00000414 * the PEC flag already set, the i2c-dev driver won't see
00000415 * (or use) this setting.
00000416 */
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == ) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return ;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return ;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return ;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg); case I2C_RDWR:
return i2cdev_ioctl_rdrw(client, arg); case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg); case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
00000452 * value in units of 10 ms.
00000453 */
client->adapter->timeout = msecs_to_jiffies(arg * );
break;
default:
/* NOTE: returning a fault code here could cause trouble
00000458 * in buggy userspace code. Some old kernel bugs returned
00000459 * zero in this case, and userspace code might accidentally
00000460 * have depended on that bug.
00000461 */
return -ENOTTY;
}
return ;
}

比较简单,根据不同的cmd执行不同的分支。由于ioctl传下来的cmd是I2C_SMBUS,因此直接看444、445行,调用i2cdev_ioctl_smbus函数:

 static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
int datasize, res; if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
if ((data_arg.size != I2C_SMBUS_BYTE) &&
(data_arg.size != I2C_SMBUS_QUICK) &&
(data_arg.size != I2C_SMBUS_BYTE_DATA) &&
(data_arg.size != I2C_SMBUS_WORD_DATA) &&
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
dev_dbg(&client->adapter->dev,
"size out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.size);
return -EINVAL;
}
/* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
00000337 so the check is valid if size==I2C_SMBUS_QUICK too. */
if ((data_arg.read_write != I2C_SMBUS_READ) &&
(data_arg.read_write != I2C_SMBUS_WRITE)) {
dev_dbg(&client->adapter->dev,
"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.read_write);
return -EINVAL;
} /* Note that command values are always valid! */ if ((data_arg.size == I2C_SMBUS_QUICK) ||
((data_arg.size == I2C_SMBUS_BYTE) &&
(data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr,
client->flags, data_arg.read_write,
data_arg.command, data_arg.size, NULL); if (data_arg.data == NULL) {
dev_dbg(&client->adapter->dev,
"data is NULL pointer in ioctl I2C_SMBUS.\n");
return -EINVAL;
} if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
(data_arg.size == I2C_SMBUS_BYTE))
datasize = sizeof(data_arg.data->byte);
else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
(data_arg.size == I2C_SMBUS_PROC_CALL))
datasize = sizeof(data_arg.data->word);
else /* size == smbus block, i2c block, or block proc. call */
datasize = sizeof(data_arg.data->block); if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
}
if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
/* Convert old I2C block commands to the new
00000380 convention. This preserves binary compatibility. */
data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;
if (data_arg.read_write == I2C_SMBUS_READ)
temp.block[] = I2C_SMBUS_BLOCK_MAX;
}
res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
data_arg.read_write, data_arg.command, data_arg.size, &temp);
if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
}

一大堆的if判断。318行,调用copy_from_user函数将用户空间的数据拷贝到内核空间。这样data_arg的内容就与ioctl第三个参数的内容是一样的了。

322至335行,都是判断,一路走来,我们知道data_arg.size的值为I2C_SMBUS_BYTE_DATA,所以这里的if条件不会成立。

338至344行,如果既不是读操作又不是写操作,那肯定不行,返回出错。

348行,由于不满足第一个条件,所以不会执行if里的语句。

356至360行,我们的data_arg.data是不为NULL的,可以继续往下执行。

362行,data_arg.size == I2C_SMBUS_BYTE_DATA这个条件满足,所以执行364行的语句,因此datasize的值为1。

371行,由于满足data_arg.read_write == I2C_SMBUS_WRITE这个条件,所以执行375行语句,将data_arg.data的第一个字节拷贝到temp变量中。

378行,条件不满足,略过。

先看387行,条件不满足,因此就剩下385行的i2c_smbus_xfer函数,下面看它在drivers/i2c/i2c-core.c中的定义:

 s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
int try;
s32 res; flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) {
i2c_lock_adapter(adapter); /* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = , try = ; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
i2c_unlock_adapter(adapter);
} else
res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data); return res;
}

2076行,对于s3c6410的IIC控制器驱动来说,没有定义smbus_xfer函数,因此执行2093行的i2c_smbus_xfer_emulated函数,它的定义如下:

 static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data *data)
{
/* So we need to generate a series of msgs. In the case of writing, we
00001895 need to use only one message; when reading, we need two. We initialize
00001896 most things with sane defaults, to keep the code below somewhat
00001897 simpler. */
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+];
int num = read_write == I2C_SMBUS_READ ? : ;
struct i2c_msg msg[] = { { addr, flags, , msgbuf0 },
{ addr, flags | I2C_M_RD, , msgbuf1 }
};
int i;
u8 partial_pec = ;
int status; msgbuf0[] = command;
switch (size) {
case I2C_SMBUS_QUICK:
msg[].len = ;
/* Special case: The read/write field is used as data */
msg[].flags = flags | (read_write == I2C_SMBUS_READ ?
I2C_M_RD : );
num = ;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
/* Special case: only a read! */
msg[].flags = I2C_M_RD | flags;
num = ;
}
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ)
msg[].len = ;
else {
msg[].len = ;
msgbuf0[] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ)
msg[].len = ;
else {
msg[].len = ;
msgbuf0[] = data->word & 0xff;
msgbuf0[] = data->word >> ;
}
break;
case I2C_SMBUS_PROC_CALL:
num = ; /* Special case */
read_write = I2C_SMBUS_READ;
msg[].len = ;
msg[].len = ;
msgbuf0[] = data->word & 0xff;
msgbuf0[] = data->word >> ;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[].flags |= I2C_M_RECV_LEN;
msg[].len = ; /* block length will be added by
00001953 the underlying bus driver */
} else {
msg[].len = data->block[] + ;
if (msg[].len > I2C_SMBUS_BLOCK_MAX + ) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[]);
return -EINVAL;
}
for (i = ; i < msg[].len; i++)
msgbuf0[i] = data->block[i-];
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
num = ; /* Another special case */
read_write = I2C_SMBUS_READ;
if (data->block[] > I2C_SMBUS_BLOCK_MAX) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[]);
return -EINVAL;
}
msg[].len = data->block[] + ;
for (i = ; i < msg[].len; i++)
msgbuf0[i] = data->block[i-];
msg[].flags |= I2C_M_RECV_LEN;
msg[].len = ; /* block length will be added by
00001980 the underlying bus driver */
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[].len = data->block[];
} else {
msg[].len = data->block[] + ;
if (msg[].len > I2C_SMBUS_BLOCK_MAX + ) {
dev_err(&adapter->dev,
"Invalid block write size %d\n",
data->block[]);
return -EINVAL;
}
for (i = ; i <= data->block[]; i++)
msgbuf0[i] = data->block[i];
}
break;
default:
dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
} i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
if (i) {
/* Compute PEC if first message is a write */
if (!(msg[].flags & I2C_M_RD)) {
if (num == ) /* Write only */
i2c_smbus_add_pec(&msg[]);
else /* Write followed by read */
partial_pec = i2c_smbus_msg_pec(, &msg[]);
}
/* Ask for PEC if last message is a read */
if (msg[num-].flags & I2C_M_RD)
msg[num-].len++;
} status = i2c_transfer(adapter, msg, num);
if (status < )
return status; /* Check PEC if last message is a read */
if (i && (msg[num-].flags & I2C_M_RD)) {
status = i2c_smbus_check_pec(partial_pec, &msg[num-]);
if (status < )
return status;
} if (read_write == I2C_SMBUS_READ)
switch (size) {
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[];
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[] | (msgbuf1[] << );
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = ; i < data->block[]; i++)
data->block[i+] = msgbuf1[i];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
for (i = ; i < msgbuf1[] + ; i++)
data->block[i] = msgbuf1[i];
break;
}
return ;
}

函数很长,但是逻辑却很简单。1898行,定义msgbuf0数组用作写操作,里面放的是要写的数据,后面会看到。

1899行,定义msgbuf1数组用作读操作,这里讨论的是写操作,因此略过与读操作相关的内容。

1900行,因为read_write=I2C_SMBUS_WRITE,所以num的值为1。

1901行,定义2个message数组,同样,一个用作写,一个用作读。

1908行,msgbuf0[0] = command,即要写数据的地址。

1909行,switch(size),由于size的值为I2C_SMBUS_BYTE_DATA,所以1924行的条件成立。

1925行,条件不成立,因此直接到1928行,msg[0].len = 2,写一字节地址和写一个字节数据加起来刚好是2字节。1929行,msgbuf0[1] = data->byte,即要写入的数据。

2002至2015行,与错误检测相关的,略过它不会有什么影响。

先看2022至2050行,都是与读操作相关的,因此不说了。

再看2017行,调用i2c_transfer函数来进行传输,i2c_transfer函数的定义:

 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try; /* REVISIT the fault reporting model here is weak:
00001287 *
00001288 * - When we get an error after receiving N bytes from a slave,
00001289 * there is no way to report "N".
00001290 *
00001291 * - When we get a NAK after transmitting N bytes to a slave,
00001292 * there is no way to report "N" ... or to let the master
00001293 * continue executing the rest of this combined message, if
00001294 * that's the appropriate response.
00001295 *
00001296 * - When for example "num" is two and we successfully complete
00001297 * the first message but get an error part way through the
00001298 * second, it's unclear whether that should be reported as
00001299 * one (discarding status on the second message) or errno
00001300 * (discarding status on the first one).
00001301 */ if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = ; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
} /* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = , try = ; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
i2c_unlock_adapter(adap); return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}

一看,呆了眼,函数里面竟然有这么多注释。

1303行,master_xfer这个指针是有赋值的,因此执行if里面的语句。

1304至1311行,调试相关的,打印一些调试信息。

1313至1320行,锁住当前的适配器。

1324行,adap->retries的值在IIC控制器初始化的时候就设置为2,因此重试2次。

1325行,调用的是drivers/i2c/busses/i2c-s3c2410.c里的s3c24xx_i2c_xfer函数,它的定义:

 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret; for (retry = ; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); if (ret != -EAGAIN)
return ret; dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); udelay();
} return -EREMOTEIO;
}

Linux设备驱动剖析之IIC(三)的更多相关文章

  1. Linux设备驱动剖析之IIC(二)

    953行,适配器的编号大于MAX_ID_MASK是不行的,MAX_ID_MASK是一个宏,展开后的值为61. 957至968行,关于管理小整形ID数的,没怎么了解,略过. 974行,调用i2c_reg ...

  2. Linux设备驱动剖析之IIC(一)

    写在前面 由于IIC总线只需要两根线就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用.但是IIC总线有一个缺点,就是传输速率比较低.本文基于Linux-2.6.36版本 ...

  3. Linux设备驱动剖析之IIC(四)

    558行,又重试2次. 560行,调用s3c24xx_i2c_doxfer函数: static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, stru ...

  4. linux设备驱动归纳总结(三):7.异步通知fasync【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxx ...

  5. linux设备驱动归纳总结(三):6.poll和sellct【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxx ...

  6. linux设备驱动归纳总结(三):5.阻塞型IO实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-60025.html linux设备驱动归纳总结(三):5.阻塞型IO实现 xxxxxxxxxxxxxx ...

  7. linux设备驱动归纳总结(三):4.ioctl的实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59419.html linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽 ...

  8. linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】

    本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一. ...

  9. linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59417.html linux设备驱动归纳总结(三):2.字符型设备的操作open.close.rea ...

随机推荐

  1. firedac数据集数据序列为JSON

    firedac数据集数据序列为JSON FIREDAC数据库引擎充分地考虑了跨平台和跨语言的支持. 因此,FIREDAC数据集可以序列为BIN\XML\JSON,三种格式. firedac数据集数据序 ...

  2. Spring静态注入的三种方式

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/chen1403876161/article/details/53644024Spring静态注入的三 ...

  3. Chart:Amcharts

    ylbtech-Chart:Amcharts Amcharts ,是一个致力于图表组件开发的公司,公司地址在立陶宛首都维尔纽斯,2004年开始推出图表和地图组件. 1. 简介返回顶部 截至目前,amC ...

  4. C++并发编程 条件变量 condition_variable,线程安全队列示例

    1. 背景 c++11中提供了对线程与条件变量的更好支持,对于写多线程程序方便了很多. 再看c++并发编程,记一下学习笔记. 2. c++11 提供的相关api 3.1 wait wait用于无条件等 ...

  5. linux系统下创建oracle表空间和用户权限查询

    创建用户和表空间: 1.登录linux,以oracle用户登录(如果是root用户登录的,登录后用 su - oracle命令切换成oracle用户) 2.以sysdba方式来打开sqlplus,命令 ...

  6. SoapUI Pro Project Solution Collection-XML assert

    in soapui the XML object used here is from  org.w3c.dom package so you need to read this article car ...

  7. killall 、kill 、pkill 命令详解 【转】

    之前常用地kill 命令就是 kill -9 XXid;kill -15 XXid;pkill 进程名: 今天发现killall也有适用场景,killall命令对杀死进程组(一个进程中有多线程的情况) ...

  8. 应用程序默认安装在C盘后启动时提示权限不足想起的。。。

    最近不少经销商用户反映,在使用win 7的系统的电脑上安装我们的软件后,开启系统时提示权限不足,无法启动软件. 而在xp系统下则没有这个问题,原因在于我们将系统的默认安装路径选择在了C盘了,而win ...

  9. Eclipse环境安装Python插件PyDev

    转载自:http://blog.csdn.net/typa01_kk/article/details/49251247 clipse环境安装Python插件PyDev 软件准备,下载地址,先看安装,再 ...

  10. What is the name of the “--&gt;” operator?(Stackoverflow)

    Question: After reading Hidden Features and Dark Corners of C++/STL on comp.lang.c++.moderated, I wa ...