Linux枚举pci显卡设备

1         获取显卡列表

显卡属于PCI设备,可以通过枚举PCI设备,然后使用一些过滤条件将显卡设备筛选出来。Linux没有Windows那样直接枚举显卡的函数,只有借用shell脚本或者读取PCI设备配置空间来获取PCI设备信息。

1.1        利用lspci命令

lspci命令执行后效果如下:

各字段含义:

例:00:02.0 VGA compatible  [0300]: Intel Corporation4 Series Chipset Integrated Graphics Controller [8086:2e12] (rev 03)

 

00:02.0:总线号:设备号.功能号

VGA compatible controller:设备类名

[0300]:设备类号

Intel Corporation:厂商名称

4 Series Chipset Integrated GraphicsController:设备名称

[8086:2e12]:[Vendor ID:Device ID]

(rev 03):版本号

 

利用lspci命令枚举出所有PCI设备后,可以利用VGA、Graphics等与显卡相关的关键字将显卡条目过滤出来。若知道显卡的Vendor ID和Device ID可将显卡条目精确过滤出来。

 

注:从程序中获取lspci输出见附件1

1.2        读取PCI配置空间

每个PCI设备都有最多256个连续的配置空间.配置空间中包含了设备的厂商ID,设备ID,IRQ,设备存储区信息等。在x86上,保留了0xCF8~0xCFF的8个寄存器。实际上就是对应地址为0xCF8的32位寄存器和地址为0xCFC的32位寄存器。在0xCF8寄存中写入要访问设备对应的总线号,设备号、功能号和寄存器号组成的一个32位数写入0xCF8,然后从0xCFC上就可以取出对应PCI设备的信息。

PCI设备的配置空间如下:

Linux枚举pci显卡设备_第1张图片

通过该方法可以枚举出所有PCI设备的Vendor ID、Device ID和ClassID,通过这3个值,可以从/usr/share/hwdata/pci.ids数据文件中检索出设备的具体描述信息,可以从http://pciids.sourceforge.net/获取最新的pci.ids,以便识别最新的显卡设备。[8086:2e12]可以在pci.ids中查询到如下信息:

8086 Intel Corporation

       2e12  4 Series Chipset Integrated GraphicsController

注:枚举PCI设备C程序示例见附件2

2         区别集成和独立显卡

从应用层暂时没有找到可以智能区别集成和独立显卡,若从驱动层与设备交互可能可以智能地识别集成和独立显卡,技术难道较大。若主机硬件不是经常更换的话,建议用以下三个方法来区别,推荐第三种。

2.1        从描述信息来区别

例:00:02.0 VGAcompatible controller [0300]: Intel Corporation 4 Series Chipset IntegratedGraphics Controller [8086:2e12] (rev 03)

可以从Intel Corporation上知道该显卡是集成显卡,若是独立显卡一般都有NVIDIA或者ATI等关键字(由于没有AMD的CPU,暂不知道AMD的核显如何描述)。此方法需要管理员人工识别。

2.2        从显存大小来区别

集成显卡的显存一般都小,而独立显卡的显存目前至少都是512MB、1GB不等,因此也可以从此参数来区分。

2.3        从Vendor ID、Device ID来区分

每个PCI设备的设备号都是全球唯一的,显卡设备也一样,因此,只要我们知道了显卡的设备号,即[Vendor ID:Device ID],不仅能够从一大堆PCI设备中精确筛选出显卡设备,还能够区分集成显卡和独立显卡。

若主机显卡不是经常更换的话,可以用事先获取的设备号去匹配主机上的PCI设备,此方法最简单,但是每次更换主机显卡,都需要更新区分代码,比较麻烦。

另外一种方法就是建立一个显卡[Vendor ID:Device ID]数据库(区分集成和独立显卡),程序可以直接读取数据库即可从PCI设备中识别出集成显卡和独立显卡。因为显卡的型号也不是很多,这种方法是可行的。若觉得麻烦,可以只创建常用的显卡的ID数据库,也是比较快的。数据库的实现方式可以用文本文件或其他可编辑文本记录,这样在更新数据库时,不需要重新编译代码。

 

附件1

使用popen

  在学习unix编程的过程中,发现系统还提供了一个popen函数,可以非常简单的处理调用shell,其函数原型如下:

  FILE*popen(const char *command, const char *type);

  该函数的作用是创建一个管道,fork一个进程,然后执行shell,而shell的输出可以采用读取文件的方式获得。采用这种方法,既避免了创建临时文件,又不受输出字符数的限制,推荐使用。

  popen使用FIFO管道执行外部程序。

  #include<stdio.h>

  FILE*popen(const char *command, const char *type);

  intpclose(FILE *stream);

  popen 通过type是r还是w确定command的输入/输出方向,r和w是相对command的管道而言的。r表示command从管道中读入,w表示 command通过管道输出到它的stdout,popen返回FIFO管道的文件流指针。pclose则用于使用结束后关闭这个指针。

  下面看一个例子:

  #include<sys/types.h>

  #include<unistd.h>

  #include<stdlib.h>

  #include<stdio.h>

  #include<string.h>

  int main(void )

  {

  FILE   *stream;

  FILE    *wstream;

  char   buf[1024];

  memset( buf, '\0', sizeof(buf));//初始化buf,以免后面写如乱码到文件中

  stream = popen( "ls -l","r" ); //将“ls -l”命令的输出 通过管道读取(“r”参数)到FILE*stream

  wstream = fopen("test_popen.txt", "w+"); //新建一个可写的文件

  fread( buf, sizeof(char),sizeof(buf), stream); //将刚刚FILE* stream的数据流读取到buf中

  fwrite( buf, 1, sizeof(buf),wstream );//将buf中的数据写到FILE   *wstream对应的流中,也是写到文件中

  pclose( stream );

  fclose( wstream );

  return 0;

  }

  [root@localhostsrc]# gcc popen.c

  [root@localhostsrc]# ./a.out

  [root@localhostsrc]# cat test_popen.txt

  总计 128

  -rwxr-xr-x 1root root 5558 09-30 11:51 a.out

  -rwxr-xr-x 1root root 542 09-30 00:00 child_fork.c

  -rwxr-xr-x 1root root 480 09-30 00:13 execve.c

  -rwxr-xr-x 1root root 1811 09-29 21:33 fork.c

  -rwxr-xr-x 1root root 162 09-29 18:54 getpid.c

  -rwxr-xr-x 1root root 1105 09-30 11:49 popen.c

  -rwxr-xr-x 1root root 443 09-30 00:55 system.c

  -rwxr-xr-x 1root root    0 09-30 11:51 test_popen.txt

  -rwxr-xr-x 1root root 4094 09-30 11:39 test.txt

 

附件2

/*

 *Enum all pci device via the PCI config register(CF8 and CFC).

 */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/io.h>

 

#define PCI_MAX_BUS 255 /* 8 bits (0 ~ 255)*/

#define PCI_MAX_DEV 31 /* 5 bits (0 ~ 31)*/

#define PCI_MAX_FUN 7 /* 3 bits (0 ~ 7) */

 

#define CONFIG_ADDRESS 0xCF8

#define CONFIG_DATA 0xCFC

 

#define PCICFG_REG_VID 0x00 /* Vendor id, 2bytes */

#define PCICFG_REG_DID 0x02 /* Device id, 2bytes */

#define PCICFG_REG_CMD 0x04 /* Commandregister, 2 bytes */

#define PCICFG_REG_STAT 0x06 /* Statusregister, 2 bytes */

#define PCICFG_REG_RID 0x08 /* Revision id,1 byte */

 

 

void list_pci_devices()

{

       unsignedint bus, dev, fun;

       unsignedint addr, data;

 

       for(bus = 0; bus <= PCI_MAX_BUS; bus++)

       {

              for(dev = 0; dev <= PCI_MAX_DEV; dev++)

              {

                     for(fun = 0; fun <= PCI_MAX_FUN; fun++)

                     {

                            addr= 0x80000000L | (bus<<16) | (dev<<11) | (fun<<8);

                            outl(addr,CONFIG_ADDRESS);

                            data= inl(CONFIG_DATA);

 

                            /*Identify vendor ID */

                            if((data != 0xFFFFFFFF) && (data != 0))

                            {

                                   printf("%02X:%02X:%02X", bus, dev, fun);

                                   printf("%04X:%04X",data&0xFFFF, data>>16);

                                   addr= 0x80000000L | (bus<<16) | (dev<<11) | (fun<<8) |PCICFG_REG_RID;

                                   outl(addr,CONFIG_ADDRESS);

                                   data= inl(CONFIG_DATA);

                                   printf("[%04X]", data>>16);

                                   if(data&0xFF)

                                   {

                                          printf("(rev %02X)\n", data&0xFF);

                                   }else

                                   {

                                          printf("\n");

                                   }

                            }

                     }

              }// end device

       }// end bus

}

 

int main()

{

       intret;

 

       /*Enable r/w permission of all 65536 ports */

       ret= iopl(3);

       if(ret < 0)

       {

              perror("ioplset error");

              return1;

       }

 

       list_pci_devices();

 

       /*Disable r/w permission of all 65536 ports */

       ret= iopl(0);

       if(ret < 0)

       {

              perror("ioplset error");

              return1;

       }

 

       return0;

}

你可能感兴趣的:(Linux枚举pci显卡设备)