tools/vm/page-types.c 解析使用

December 9th, 2014 by JasonLe's Tech 1,524 views

page-types.c这个代码位于kernel源码下的tool/vm/page-type.c

我们可以通过使用makefile编译这个程序,通过这个程序我们可以查找每个特定进程的page frame number  记住这个pfn只是一个index而已,我们如果想取得真实的物理地址需要使用pfn*page_size,当然这个是page frame 的物理起始地址。

这个程序是位于用户态,通过查找/proc/pid/pagemap 和 /proc/pid/maps来找到pfn的。

这个程序的功能比较强大,我们使用的仅仅是指定一个$./page-type -p pid

首先mian()中先要解析参数,然后通过parse_number()来获取用户输入的pid,然后我们知道当指定了一个pid,也就意味着我们打开一个正在运行的pid的maps

[root@localhost 13352]# ls
attr       clear_refs       cpuset   fd       limits    mountinfo   ns         oom_score_adj  root       stack   syscall
autogroup  cmdline          cwd      fdinfo   loginuid  mounts      numa_maps  pagemap        sched      stat    task
auxv       comm             environ  gid_map  maps      mountstats  oom_adj    personality    sessionid  statm   uid_map
cgroup     coredump_filter  exe      io       mem       net         oom_score  projid_map     smaps      status  wchan
00400000-00401000 r-xp 00000000 fd:02 4330673                            /home/lzz/mca-ras/src/tools/simple_process/simple_process
00600000-00601000 r--p 00000000 fd:02 4330673                            /home/lzz/mca-ras/src/tools/simple_process/simple_process
00601000-00602000 rw-p 00001000 fd:02 4330673                            /home/lzz/mca-ras/src/tools/simple_process/simple_process
019b8000-019d9000 rw-p 00000000 00:00 0                                  [heap]
33c1a00000-33c1a20000 r-xp 00000000 fd:01 2622088                        /usr/lib64/ld-2.18.so
33c1c1f000-33c1c20000 r--p 0001f000 fd:01 2622088                        /usr/lib64/ld-2.18.so
33c1c20000-33c1c21000 rw-p 00020000 fd:01 2622088                        /usr/lib64/ld-2.18.so
33c1c21000-33c1c22000 rw-p 00000000 00:00 0
33c1e00000-33c1fb4000 r-xp 00000000 fd:01 2622743                        /usr/lib64/libc-2.18.so
33c1fb4000-33c21b3000 ---p 001b4000 fd:01 2622743                        /usr/lib64/libc-2.18.so
33c21b3000-33c21b7000 r--p 001b3000 fd:01 2622743                        /usr/lib64/libc-2.18.so
33c21b7000-33c21b9000 rw-p 001b7000 fd:01 2622743                        /usr/lib64/libc-2.18.so
33c21b9000-33c21be000 rw-p 00000000 00:00 0
7f3fe081a000-7f3fe081d000 rw-p 00000000 00:00 0
7f3fe083c000-7f3fe083e000 rw-p 00000000 00:00 0
7fff87571000-7fff87592000 rw-p 00000000 00:00 0                          [stack]
7fff875fe000-7fff87600000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

我们可以看到这个进程每个段,包括BSS CODE HEAP STACK等。然后哦page-types.c中的parse_pid读取这个表里面的信息。
关于这个表每个列的含义看这个http://stackoverflow.com/questions/1401359/understanding-linux-proc-id-maps

我们主要关注头两行,vm_start vm_end。这时我们要获取虚拟地址的page number:

pg_start[nr_vmas] = vm_start / page_size;
pg_end[nr_vmas] = vm_end / page_size;

至于这个page_size可以通过page_size = getpagesize();获取。这里我们要注意unsigned long的取值范围是0-2^64-1,-1也就是2^64-1

然后我们进入walk_task()函数,因为process可能会存在匿名映射,所以我们需要使用filebacked_path[i]判断文件地址。

进入walk_vma()后。
这时我们需要读取/proc/pid/pagemap,这个我们用常规打不开,所以我们只好通过vm index读取的方式获得pages和pfn的相关信息。

详细我们查看

http://stackoverflow.com/questions/17021214/decode-proc-pid-pagemap-entry
https://www.kernel.org/doc/Documentation/vm/pagemap.txt

关注

* Bits 0-54 page frame number (PFN) if present
* Bit 63 page present
下面就是通过编程的方式获取这个的0-54位。总的来所就是使用位操作:

#define PFN_MASK (((1LL)<<55)-1)  //意味着0-54位都为1
*phy=(buf&PFN_MASK)*page_size+vir%page_size

核心函数:

        for (i = 0; i < pages; i++) {
//          printf("%lx\n",buf[i]);
            pfn = pagemap_pfn(buf[i]);
            if (pfn) {
                printf("%lx", pfn);
                printf("\t0x%lx",pfn*page_size);
                walk_pfn(index + i, pfn, 1, buf[i]);
            }
        }

我们可以看到pfn只是一个index而已,真正的物理地址需要pfn*page_size的方式,后面的各种属性是通过读取/proc/kpageflags获得。结果如下:

/home/lzz/mca-ras/src/tools/simple_process/simple_process
93a12	0x93a12000	referenced,uptodate,lru,active,mmap,private

/home/lzz/mca-ras/src/tools/simple_process/simple_process
54d07	0x54d07000	uptodate,lru,active,mmap,anonymous,swapbacked

/home/lzz/mca-ras/src/tools/simple_process/simple_process
9aa43	0x9aa43000	uptodate,lru,active,mmap,anonymous,swapbacked

[heap]
91f74	0x91f74000	uptodate,lru,active,mmap,anonymous,swapbacked
60fb3	0x60fb3000	uptodate,lru,active,mmap,anonymous,swapbacked
...............

 

我们看到物理地址只是在后面添加了3个0,其实也就是12位,正好是一个4k page的页内offset。

Linux中的虚拟地址转换物理地址实现

November 26th, 2014 by JasonLe's Tech 1,724 views

Linux使用一种4级页表机制,即页全局目录(Page Global Directory)、页上级目录(Page Upper Directory)、页中间目录(Page Middle Directory)和页表(Page Table)。在这种分页机制下.

一个完整的线性地址被分为五部分:页全局目录、页上级目录、页中间目录、页表和偏移量,但是对于每个部分所占的位数则是不定的,这跟系统所在的体系架构有关。

Linux分别采用pgd_t、pud_t 、pmd_t和pte_t四种数据结构来表示页全局目录项、页上级目录项、页中间目录项和页表项。这四种数据结构本质上都是无符号长整型,Linux为了更严格数据类型检查,将无符号长整型分别封装成四种不同的页表项。

static void get_pgtable_macro(void)
{
	printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
	printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
	printk("PUD_SHIFT = %d\n", PUD_SHIFT);
	printk("PMD_SHIFT = %d\n", PMD_SHIFT);
	printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);

	printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
	printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
	printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
	printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);

	printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}

static unsigned long vaddr2paddr(unsigned long vaddr)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long paddr = 0;
        unsigned long page_addr = 0;
	unsigned long page_offset = 0;

	pgd = pgd_offset(current->mm, vaddr);
	printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
	printk("pgd_index = %lu\n", pgd_index(vaddr));
	if (pgd_none(*pgd)) {
		printk("not mapped in pgd\n");
		return -1;
	}

	pud = pud_offset(pgd, vaddr);
	printk("pud_val = 0x%lx\n", pud_val(*pud));
	if (pud_none(*pud)) {
		printk("not mapped in pud\n");
		return -1;
	}

	pmd = pmd_offset(pud, vaddr);
	printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
	printk("pmd_index = %lu\n", pmd_index(vaddr));
	if (pmd_none(*pmd)) {
		printk("not mapped in pmd\n");
		return -1;
	}

	pte = pte_offset_kernel(pmd, vaddr);
	printk("pte_val = 0x%lx\n", pte_val(*pte));
	printk("pte_index = %lu\n", pte_index(vaddr));
	if (pte_none(*pte)) {
		printk("not mapped in pte\n");
		return -1;
	}

	//页框物理地址机制 | 偏移量
	page_addr = pte_val(*pte) & PAGE_MASK;
	page_offset = vaddr & ~PAGE_MASK;
	paddr = page_addr | page_offset;
	printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
        printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);

	return paddr;
}

通过代码我们发现线性地址寻值过程:pgd_t -> pud_t ->pmd_t ->  pte_t  

具体Dmesg打印信息:

dmesg.log

目前在X86_64环境下,虽然支持64位,但是系统实际上目前远远使用不了如此大的内存,真正使用的只有48位,即

0xffff000000000000

 

然后使用上面的宏函数进行移位操作,拼接出下一级的页表物理基地址。然后再和后面的pud,pmd,pte部分地址相加,得到下一级的物理基地址。直到最后拿到页框的物理地址与pte或,找到真实的物理地址。

参考:

arch/x86/include/asm/pgtable_64_types.h

 

Linux下安装LaTeX环境

November 24th, 2014 by JasonLe's Tech 1,667 views

由于我使用的是Fedora 20 ,可以直接使用yum机制安装。Ubuntu/Debian 用户可以使用apt-get安装。

Linux主要存在的问题是众多fonts文件的缺失,所以当我们编译中文pdf时,经常会出现fonts cannot found的现象。本文主要解决这个问题:

1.从别处拷贝需要的字体,从Windows的C:/Windows/Fonts下拷贝最常使用的几种字体:msyh.ttf(微软雅黑)  simfang.ttf(仿宋)  simhei.ttf(黑体)  simkai.ttf(楷体)  simsun.ttc(宋体)。
拷贝到Linux的/usr/share/fonts/winfonts/目录下
#chmod a+x  /usr/share/fonts/winfonts/

# cd /usr/share/fonts/winfonts/
# mkfontscale
# mkfontdir
# fc-cache

拷贝完成之后一定要注意使用chmod给字体权限,为X字体创建可裁剪的索引,在一个目录中创建X字体文件的索引,建立字体信息缓存文件。

使用fc-list 命令查看:

➜  TIR_CN git:(master) ✗ fc-list :lang=zh-cn
/usr/share/fonts/winfonts/simsun.ttc: 新宋体,NSimSun:style=Regular
/usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc: 文泉驿点阵正黑,文泉驛點陣正黑,WenQuanYi Zen Hei Sharp:style=Regular
/usr/share/fonts/adobe-source-han-sans-cn/SourceHanSansCN-Regular.otf: 思源黑体 CN,Source Han Sans CN,Source Han Sans CN Regular,思源黑体 CN Regular:style=Regular
/usr/share/fonts/google-droid/DroidSansFallback.ttf: Droid Sans:style=Regular
/usr/share/fonts/winfonts/simkai.ttf: 楷体,KaiTi:style=Regular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta
/usr/share/fonts/winfonts/simfang.ttf: 仿宋,FangSong:style=Regular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta
/usr/share/fonts/adobe-source-han-sans-cn/SourceHanSansCN-ExtraLight.otf: 思源黑体 CN,Source Han Sans CN,Source Han Sans CN ExtraLight,思源黑体 CN ExtraLight:style=ExtraLight,Regular
/usr/share/fonts/adobe-source-han-sans-cn/SourceHanSansCN-Normal.otf: 思源黑体 CN,Source Han Sans CN,Source Han Sans CN Normal,思源黑体 CN Normal:style=Normal,Regular
/usr/share/fonts/cjkuni-uming/uming.ttc: AR PL UMing TW MBE:style=Light
/usr/share/fonts/winfonts/simsun.ttc: 宋体,SimSun:style=Regular
/usr/share/fonts/winfonts/simhei.ttf: 黑体,SimHei:style=Regular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta
/usr/share/fonts/adobe-source-han-sans-cn/SourceHanSansCN-Medium.otf: 思源黑体 CN,Source Han Sans CN,Source Han Sans CN Medium,思源黑体 CN Medium:style=Medium,Regular
/usr/share/fonts/gnu-free/simhei.ttf: 黑体,SimHei:style=Regular
........

/usr/share/fonts/winfonts/simsun.ttc: 宋体,SimSun:style=Regular
我们可以看到中文后面的那个SimSum就是在linux中的字体符号.

我们打开/usr/share/texlive/texmf-dist/tex/latex/ctex/fontset/ctex-xecjk-winfonts.def
修改我们缺少的字体。

% ctex-xecjk-winfonts.def: Windows 的 xeCJK 字体设置,默认为六种中易字体
% vim:ft=tex

\setCJKmainfont[BoldFont={SimHei},ItalicFont={KaiTi}]
  {SimSun}
\setCJKsansfont{SimHei}
\setCJKmonofont{FangSong}

\setCJKfamilyfont{zhsong}{SimSun}
\setCJKfamilyfont{zhhei}{SimHei}
\setCJKfamilyfont{zhkai}{KaiTi}
\setCJKfamilyfont{zhfs}{FangSong}
% \setCJKfamilyfont{zhli}{LiSu}
% \setCJKfamilyfont{zhyou}{YouYuan}

\newcommand*{\songti}{\CJKfamily{zhsong}} % 宋体
\newcommand*{\heiti}{\CJKfamily{zhhei}}   % 黑体
\newcommand*{\kaishu}{\CJKfamily{zhkai}}  % 楷书
\newcommand*{\fangsong}{\CJKfamily{zhfs}} % 仿宋
% \newcommand*{\lishu}{\CJKfamily{zhli}}    % 隶书
% \newcommand*{\youyuan}{\CJKfamily{zhyou}} % 幼圆

\endinput

然后大功告成,可以使用xelatex生成pdf文件了。

参考文档

Configure Manager Rules

November 18th, 2014 by JasonLe's Tech 4,977 views

CM的职责就是对与git 进行管理,将不合格的commit拒绝在仓库以外。

详细开发流程

但是每周CM的职责除了合并开发成员的branch外,还要进行commit的汇报,比如commit多少,merge多少,reject多少等等。

比如我们将个git log >/tmp/git.log文件中,然后进行分析。

比如我们统计git log 中的commit 信息 可以使用 grep commit git.log | wc -l 方式计算commit行数。

$grep commit git.log | wc -l
$grep Merge git.log | wc -l

因为git log 的数据非常整齐,可以使用vim的块操作

输入v进入visual模式,然后可以移动光标选取一段文本。

d———->剪贴

y———->复制

p———->粘贴

对于经常登陆的机子我们可以生成dsa

use ”ssh-keygen -t dsa” to generate a key
then use ”ssh-copy-id /home/lzz/.ssh/id_dsa.pub user@server IP”
Ok, now you can ssh and git pull/push without password

对于一些system如果ssh-copy-id 无法使用,我们可以手动将/home/lzz/.ssh/id_dsa.pub 手动拷贝到目标机器的目标用户目录下,
比如/home/user/.ssh/id_dsa_pub.A. 然后将这个id_dsa_pub.A >> authority 后面。

这些是CM的日常工作,除了这些,对于master branch与 local branch的backup也非常重要。
比如我有两台机子都要备份,我们可以把资料相互备份然后传输到对方的机子上。我们知道两台主机数据同时损坏的概率非常下。

对于每台主机,CM应该记录到以下几点:

security incidents
power-fail
server off-line
filesystem overruns (filesystem full)
dmesg warnings/errors
abnormal operational behavior

backup_policy

cm_rules

对于一些备份的例行任务,我们可以使用crontab -e 命令创建定时任务,关于这个网上的使用比较多,我们可以参考
http://www.cnblogs.com/peida/archive/2013/01/08/2850483.html

RHEL系(Fedora,CentOS) rpm打包

November 6th, 2014 by JasonLe's Tech 1,884 views

现在Linux下越来越倾向于打包二进制安装文件,rpm,deb都是这种类型。

但是作为内核开发人员我们要对程序进行调试,就要有debug-info package,但是默认内核人是不提供debug-info的包,所以需要我们自动生成。

一般我们要用root用户进行编译,但是编译rpm包,至少需要20GB的临时文件空间!

一键操作:make rpm ———–>自动身成rpmtree,kernel.spec文件,自动编译。

——————————

详细定制:

下面开始创建:

1.用所在的用户输入rpmdev-setuptree,然后会在该用户的目录下生成${HOME}/rpmbuild/SOURCES ,${HOME}/rpmbuild/SPECS 和 ${HOME}/rpmbuild/BUILD 。

2.在多数源玛目录下会自动带有kernel.spec的文件,将这个文件放到${HOME}/rpmbuild/SPECS中,将kernel-3.14.8.tar.gz 放入${HOME}/rpmbuild/SOURCES中,然后在SOURCE中放入.config,

默认生成的spec带着”%debug_package %{nil}” 这就意味着不会生成带debug_info的包。如果需要,我们就要注释掉。

cp .config ~/rpmbuild/SOURCES/config-`uname -m`-generic

3.切换到SPECS目录下。

建立所有内核配置:
rpmbuild -bb –target=`uname -m` kernel.spec

关闭指定的内核配置(为了更快的建立):
rpmbuild -bb –without <option> –target=`uname -m` kernel.spec

其中“ option ”的有效值包括xen、smp、up、pae、kdump、debug 和 debuginfo。指定 –without debug 会剔除内核中的调试代码,指定 –without debuginfo 会禁止建立 kernel-debuginfo 包。

只建立一个特定的内核:
rpmbuild -bb –with <option> –target=`uname -m` kernel.spec
“ option ”的有效值包括 xenonly、 smponly 和 baseonly 。

例如,只建立 kernel 和 kernel-devel 包的命令是:

rpmbuild -bb –with baseonly –without debuginfo –target=`uname -m` kernel.spec
建立时包含固件,用如下命令:

rpmbuild -bb –with baseonly –with firmware –without debuginfo  –target=`uname -m` kernel.spec
建立的过程需要很长时间。会在屏幕上打印大量的信息。这些信息可以被忽略,除非建立过程因为一个 error 而停止。如果成功完成建立过程,一个新的内核包会出现在 ~/rpmbuild/RPMS 目录。

 

参考:

https://fedoraproject.org/wiki/Building_a_custom_kernel
https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn
http://blog.csdn.net/justlinux2010/article/details/9905425
http://www.linuxfly.org/post/130/