存档

2013年4月 的存档

QEMU 的 CPU 配置

2013年4月11日 没有评论

根据前面描述 CPU 的基本知识, 可以知道 CPU 有物理 CPU, 多核 CPU, 超线程 CPU 之分.

事实上, QEMU 支持所有这些配置, 下面一一举例来说明如何模拟这些 CPU.

基本的 CPU 模拟

下面的指令模拟了一个具有 1 个物理 CPU, 两个逻辑 CPU 的系统

$ qemu -enable-kvm -m 1024 ArchLinux.img -smp 2,sockets=1

在 guest 上看看 cpuinfo 的信息:

$ cat /proc/cpuinfo
processor   : 0
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 2
 
processor   : 1
physical id : 0
siblings    : 2
core id     : 1
cpu cores   : 2

可以看到两个逻辑 CPU 是双核的, 没有使用超线程技术.

指定核心数

模拟一个具有 1 个物理 CPU(双核), 四个逻辑 CPU 的系统. 此时为了满足双核 四线程的概念, 得启用超线程技术, 如下

$ qemu -enable-kvm -m 1024 ArchLinux.img -smp 4,sockets=1,cores=2
$ cat /proc/cpuinfo
processor   : 0
physical id : 0
siblings    : 4
core id     : 0
cpu cores   : 2
 
processor   : 1
physical id : 0
siblings    : 4
core id     : 0
cpu cores   : 2
 
processor   : 2
physical id : 0
siblings    : 4
core id     : 1
cpu cores   : 2
 
processor   : 3
physical id : 0
siblings    : 4
core id     : 1
cpu cores   : 2

指定 thread 数

模拟一个具有 2 个物理 CPU, 四个逻辑 CPU 的系统, 启用超线程技术, 每个核心两个 线程. 不难算出, 此时每个 CPU 都是单核的(4 = 2*2*1).

$ qemu -enable-kvm -m 1024 ArchLinux.img -smp 4,sockets=2,threads=2
$ cat /proc/cpuinfo
processor   : 0
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 1
 
processor   : 1
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 1
 
processor   : 2
physical id : 1
siblings    : 2
core id     : 0
cpu cores   : 1
 
processor   : 3
physical id : 1
siblings    : 2
core id     : 0
cpu cores   : 1

其它

事实上, QEMU 还有更强大的 CPU 的配置, 比如配置 CPU 指令级, 配置 NUMA, 等等, 这里不一一列举.

分类: QEMU 标签:

CPU 基本知识

2013年4月11日 没有评论

以下是 CPU 中比较容易混淆的概念:

socket

可以理解为主板上的一个插座, 用来连接物理 CPU 芯片到主板上, 简单讲, 有多少个 socket 就有多少个物理 CPU.

core

core 的概念涉及到 多核心技术 是将多个一样的 CPU 放置于一个封装内(或直接将两个 CPU 做成一个芯片), 每个这样的 CPU 叫做一个 core, 每个 core 是一个的独立的 执行单元.

thread

thread 涉及到 超线程技术 , 简单的讲, 就是把两个逻辑内核模拟成两个物理芯片, 让单个处理器都能使用线程级并行计算.

逻辑 CPU 数量

综上, CPU 的数量不能简单的通过物理 CPU 的数量(或者 socket 的数量) 来判断, 如果用了多核技术, 则需要计算全部 core 的数量, 如果用了超线程, 则需要加上 所有 CPU 线程. 所以每一个 CPU(包括 core 或者 thread) 都叫一个逻辑 CPU.

Linux 下的 CPU 计算

在 Linux 下, kernel 抛出了一个统计了 CPU 一般信息的文件 /proc/cpuinfo, 通过这个文件, 可以知道本机的 CPU 信息, 包括厂商, CPU 型号, CPU 缓存, 核心, 超线程等等信息.

下面以一台 intel 酷睿双核为例说明:

gentoo-company tmp # cat /proc/cpuinfo 
processor   : 0
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 2
 
processor   : 1
physical id : 0
siblings    : 2
core id     : 1
cpu cores   : 2

我省略的不必要的输出, 对以上信息做一个解释

processor
这个表示逻辑 CPU 的 ID
physical id
物理 CPU(socket) 的 ID, 具有相同 physical id 的逻辑 CPU 在同一个 CPU 封装内.
siblings
同一个 CPU 封装(socket)里的逻辑 CPU 数量, 这个数字表示在该物理 CPU 里面有多少个逻辑 CPU
core id
核心 ID, 具有相同 core id 的逻辑 CPU 在同一个 core 里, 即是使用 了超线程的逻辑 CPU
cpu cores
CPU 核心数, 在该物理 CPU 内封装的 core 数目.

通过上面的说明, 该机器有一个物理 CPU(只有一个 physical id), 用了双核技术(cpu cores 为2), 没有使用超线程技术(没有相同的 core id)

分类: Other 标签:

Port glib to android

2013年4月1日 25 条评论

glib]] 是 linux 下非常基础的库, 大部分 linux 下的软件 都依赖于它, 比如 gstreamer, gtk 等等. 由于最近我在准备 hack spice, 准备把它 port 到 android 上, 而 libspice 又依赖于 glib, 所以需要把 glib 移植到 android 上.

所幸几年前就 hack 过大量程序到 ARM 和 blackfin 平台上, 所以过程还算顺利.

下载相应的文件

需要用到的源代码有以下几个:

libiconv
1.14, 从 这里 下载
gettext
0.18.2, 从 这里 下载
libffi
3.0.12, 从 这里 下载
glib
2.34.3, 从 这里 下载

由于有的版本(比如 libiconv-1.14, 旧的 glib)暂时不支持自动探测 android host, 所以需要对探测的脚本做一些修改, 这里使用最简单的方法, 使用 gnu 网站上最新的 探测脚本替换不支持的脚本, 先把脚本下载下来, 需要是替换

$  wget -O /tmp/config.sub "git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD"
$  wget -O /tmp/config.guess "git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD"

编译环境

这里 根据操作系统的版本下载相应的 toolchain版本, 我下载的是 android-ndk-r8d-linux-x86.tar.bz2, 解压到合适的目录, 然后进入该目录, 安装 toolchain, (在这里我将 toolchain 安装在 ${HOME}/Develop/android-toolchain, 使用 gcc-4.7, android-14 的 platform

$ tar xf android-ndk-r8d-linux-x86.tar.bz2
$ cd android-ndk-r8d
$ build/tools/make-standalone-toolchain.sh --platform=android-14 \
--toolchain=arm-linux-androideabi-4.7 \
--install-dir=${HOME}/Develop/android-toolchain

在 bash 的配置文件里面配置 toolchain 的相关路径

$ echo "export SYSROOT=${HOME}/Develop/android-toolchain/sysroot" >> ${HOME}/.bashrc
$ echo 'export PATH=${PATH}:${HOME}/Develop/android-toolchain/bin' >> ${HOME}/.bashrc
$ source ${HOME}/.bashrc

compile libiconv

libiconv 目前的最新版本是 1.14, 还不支持 android, 需要做一些 hack

将上面下载的 config.sub 和 config.guest 文件替换原有的文件, 因为目前还不支持 自动探测 android host.

$ cp /tmp/config.sub build-aux/config.sub
$ cp /tmp/config.sub libcharset/build-aux/config.sub
$ cp /tmp/config.guess build-aux/config.guess
$ cp /tmp/config.guess libcharset/build-aux/config.guess

另外, 由于 libiconv 自带的 stdint.h 和 android toolchain 的冲突, 导致使用 time_t 等几个结构体的时候会出现类型未申明的情况, 比如:

In file included from /root/Develop/android-toolchain/sysroot/usr/include/sys/time.h:33:0,
                 from /root/Develop/android-toolchain/sysroot/usr/include/time.h:32,
                 from ./time.h:40,
                 from ./stdint.h:518,
                 from /root/Develop/android-toolchain/bin/../lib/gcc/arm-linux-androideabi/4.7/include-fixed/sys/types.h:43,
                 from ./fcntl.h:46,
                 from careadlinkat.h:23,
                 from areadlink.c:27:
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:20:2: error: unknown type name 'time_t'
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:26:2: error: unknown type name 'time_t'
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:27:2: error: unknown type name 'suseconds_t'

在 configure 中预定义 gl_cv_header_working_stdint_h=yes 可以避开这个问题.

以下是编译的具体指令:

$ gl_cv_header_working_stdint_h=yes ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static
$ make -j5
$ make install

现在, 应该可以在 ${SYSROOT}/usr/lib/ 下看到相关的库已经安装了

$ ls ${SYSROOT}/usr/lib/libiconv*

compile gettext

android toolchai 的 passwd 结构体没有 pw_gecos 成员. 会报以下错误:

msginit.c: In function 'get_user_fullname':
msginit.c:1084:21: error: 'struct passwd' has no member named 'pw_gecos'

以下的小 patch 简单的避免这个问题(其实就是简单粗暴地给 fullname 赋值, 避免访问 pwd->pw_gecos):

--- msginit.c   2012-12-04 14:28:58.000000000 +0800
+++ msginit.c.new   2013-04-01 11:59:54.054980294 +0800
@@ -1081,7 +1081,11 @@
       char *result;

       /* Return the pw_gecos field, up to the first comma (if any).  */
+#ifndef __ANDROID__
       fullname = pwd->pw_gecos;
+#else
+fullname = "android";
+#endif
       fullname_end = strchr (fullname, ',');
       if (fullname_end == NULL)
         fullname_end = fullname + strlen (fullname);

以下是编译的具体指令:

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static --disable-java --disable-native-java
$ make -j5
$ make install

libffi

编译没什么问题, 一次通过

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static
$ make -j5
$ make install

compile glib

glib 的编译稍微麻烦一点.

这里我选择相对新一点的 2.34.3 版本, 这个版本的探测脚本已经支持 android host 了.

另外, 在编译前的配置时, ARM 上的编译器会在检查一些特性时失败, 采用下面的方法 避免这类失败: 详情请参考源码目录下的 docs/reference/glib/html/glib-cross-compiling.html

新建一个文件 android.cache, 写入以下内容.

# file android.cache
ac_cv_type_long_long=yes
glib_cv_stack_grows=no
glib_cv_uscore=no
ac_cv_func_posix_getpwuid_r=no
ac_cv_func_posix_getgrgid_r=no

配置编译的指令(test 模块编译不过去, 没关系, 我永不到, disable 之):

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static --cache-file=android.cache --disable-modular-tests

编译的时候会出现各种各样的错误:

gstrfuncs.c: In function 'g_ascii_strtod':
gstrfuncs.c:718:30: error: 'struct lconv' has no member named 'decimal_point'
gstrfuncs.c: In function 'g_ascii_formatd':
gstrfuncs.c:942:30: error: 'struct lconv' has no member named 'decimal_point'

gutils.c:840:8: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:840:25: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:846:35: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:749:12: warning: unused variable 'logname' [-Wunused-variable]
gutils.c:748:10: warning: unused variable 'error' [-Wunused-variable]

glocalfileinfo.c:1097:21: error: 'struct passwd' has no member named 'pw_gecos'

gresolver.c:1133:14: error: 'T_TXT' undeclared (first use in this function)
gresolver.c:1133:14: note: each undeclared identifier is reported only once for each function it appears in
gresolver.c:1135:14: error: 'T_SOA' undeclared (first use in this function)
gresolver.c:1137:14: error: 'T_NS' undeclared (first use in this function)
gresolver.c:1139:14: error: 'T_MX' undeclared (first use in this function)

同样, 简单粗暴的 hack.(由于 patch 比较打, 我放在后面的 gist 上).

不同版本可能会遇到各种各样不同的编译错误, 这里需要具体问题具体分析, 比如 某些结构体, 宏没有申明的, 可以手动添加, 一些测试模块编译不过去, 可以把测试 模块去掉, 例如上面的 configure 时的–disable-modular-tests 参数.

测试

使用 glib 获取外网 IP 的 test 代码, JNI 和 NDK 的使用请看我其它的 android NDK 开发指南(抱歉还没有).

// file GetIPAddr.java
package org.mathslinux.glib_demo;
public class GetIPAddr {
    static {
        System.loadLibrary("ipaddr");
    }
 
     public static native String getAddr();
}
 
//  MainActivity.java
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_main);
    }
 
    public void onClick(View v) {
        EditText text = (EditText) this.findViewById(R.id.editText1);
        text.setText(GetIPAddr.getAddr());
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        this.getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}
/* file ip.c */
#include 
#include 
#include 
#include "ip.h"
static gchar *get_addr()
{
    char url[128] = {0};
 
    g_type_init();
    GSocketClient *client = g_socket_client_new();
    snprintf(url, sizeof(url), "%s:%s%s", "http", "//", "cip.cc");
    GSocketConnection *c = g_socket_client_connect_to_uri(
        client, url, 80, NULL, NULL);
    if (c) {
        GSocket *socket = NULL;
        g_object_get(c, "socket", &socket, NULL);
        if (socket) {
            char buf[2048] = {0};
            char *str = g_malloc0(1024);
            char *p, *q;
            char *send = "GET / HTTP/1.1\r\nUser-Agent: curl/7.29.0\r\nHost: cip.cc\r\nAccept: */*\r\n\r\n";
            g_socket_send(socket, send, strlen(send) + 1, NULL, NULL);
            g_socket_receive(socket, buf, sizeof(buf), NULL, NULL);
            p = strstr(buf, "IP");
            q = strstr(p, "\n");
            if (p && q) {
                char ip[32] = {0};
                snprintf(ip, q - p + 2, "%s", p);
                strcat(str, ip);
            }
 
            p = strstr(buf, "地址");
            q = strstr(p, "\n");
            if (p && q) {
                char addr[1024] = {0};
                snprintf(addr, q - p + 2, "%s", p);
                strcat(str, addr);
            }
            g_socket_close(socket, NULL);
            return str;
        }
    }
    g_object_unref(client);
    return NULL;
}
 
JNIEXPORT jstring JNICALL Java_org_mathslinux_glib_1demo_GetIPAddr_getAddr
(JNIEnv *je, jclass jc)
{
    char *addr = get_addr();
    return (*je)->NewStringUTF(je, addr);
}

秀一张截图:
glib_on_android

Others

glib 的 patch 我贴在 gist 上 请访问: 我的 gist

分类: android 标签: