编程

Linux内核编程一直是我很想掌握的一个技能。如果问我为什么,我也说不上来。
也许是希望有一天自己的名字也出现在内核开发组的邮件列表里?或是内核发行文件的CREDITS上?
也许是吧。其实更多的,可能是对于底层的崇拜,以及对于内核的求索精神。
想到操作系统的繁杂,想到软件系统之间的衔接,内心觉得精妙的同时,更是深深的迷恋。
所以从这篇文章开始,我要真正的走进Linux内核里了,让代码指引我,去奇妙的世界一探究竟。

在这篇文章中,一起来对内核说Hello World。

本次的编程环境:

  • CentOS 6.8
  • Linux centos 2.6.32-573.8.1.el6.x86_64

没有安装内核的,可能需要安装一下内核源码包

  • kernel-devel-2.6.32-642.4.2.el6.x86_64

    yum install kernel-devel-2.6.32-642.4.2.el6.x86_64

安装好之后,这个版本内核可以在/usr/src/linux找到。

然后话不多说,首先看代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//20160904
//kernel_hello_world.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init lkp_init(void){
printk("Hello,World! --from the kernel space...\n");
return 0;
}

static void __exit lkp_cleanup(void){
printk("Goodbye,World! --leaving kernel space...");
}

module_init(lkp_init);
module_exit(lkp_cleanup);

以上代码是kernel_hello_world.c内容。
作为内核模块,在编译的时候,Makefile文件这样写:

1
2
#File:Makefile
obj-m += kernel_hello_world.o

然后可以通过这条命令来编译:

1
make -C /usr/src/linux SUBDIRS=$PWD modules

编译好以后,目录下面的文件可能是这样子:

kernel_hello_world.ko.unsigned  kernel_hello_world.o  Module.symvers
kernel_hello_world.c   kernel_hello_world.mod.c        Makefile
kernel_hello_world.ko  kernel_hello_world.mod.o        modules.order

有这么多文件被生成,其中kernel_hello_world.ko就是本次编译出来的内核模块文件,在Linux内核中有很多这样的模块,它们可能充当着不同的角色,可能是驱动,也可能是各种设备。

这个模块会在/var/log/message文件中打印一行字,即Hello,World! –from the kernel space…

可以使用insmod kernel_hello_world.ko来将这个模块载入到内核,使用lsmod来查看是否已经加载,使用rmmod kernel_hello_world.ko来卸载这个模块。

可以tail /var/log/message来看一下是否成功执行了呢?

Hello,Kernel.

本次的编程环境:

  • CentOS 6.8
  • Linux centos 2.6.32-573.8.1.el6.x86_64

在内核的源代码中定义了很多进程和进程调度相关的内容。其实Linux内核中所有关于进程的表示全都放在进程描述符这个庞大的结构体当中,关于这个结构体的内容和定义,可以在内核的linux/sched.h文件中找到。
现在就来通过编程实现对进程描述符的操作,主要是读取。至于修改等操作,将在后面的内容中提到。
通过对进程描述符的读取,可以获取进程的一切内容,包括进程的ID,进程的地址空间等等。

不多说,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//20160912
//currentptr.c

#include <linux/tty.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/module.h>

/*Function To Write Msg To TTY*/
void tty_write_message(struct tty_struct *tty,char *msg){
if(tty && tty->ops->write){
tty->ops->write(tty,msg,strlen(msg));
}
return;
}

static int my_init(void){
char *msg="Hello tty!\n";
tty_write_message(current->signal->tty,msg);
printk("Hello -- from the kernel...\n");
printk("Parent pid: %d(%s)\n",current->parent->pid,current->parent->comm);
printk("Current pid: %d(%s)\n",current->pid,current->comm);
printk("Current fs: %d\n",current->fs);
printk("Current mm: %d\n",current->mm);
return 0;
}

static void my_cleanup(void){
printk("Goodbye -- from the kernel...\n");
}

module_init(my_init);
module_exit(my_cleanup);

以上的代码,主要就是通过引入内核头文件,进而引用进程描述符中的指针,并通过这种方式获取当前进程和相关进程的描述信息。
Makefile文件如下:

#Makefile
obj-m += currentptr.o

编译的指令:

make -C /usr/src/linux SUBDIRS=$PWD modules

然后通过insmod把模块装载进内核,首先tty输出了Hello tty!
同时在/var/log/message中,模块打印出了这些内容:

Sep 12 10:35:36 centos kernel: Hello -- from the kernel...
Sep 12 10:35:36 centos kernel: Parent  pid: 2235(bash)
Sep 12 10:35:36 centos kernel: Current pid: 13197(insmod)
Sep 12 10:35:36 centos kernel: Current fs: 927961856
Sep 12 10:35:36 centos kernel: Current mm: 932613056

分别是这个进程的相关信息。
对于进程描述符的定义,在本次实验用来编译的内核源码包(kernel-devel-2.6.32-642.4.2.el6.x86_64)中,
进程描述符具体定义在include/linux/sched.h的1326行往后。

需要参考的进程具体信息都在其中,可随时参考,以备不时之需。

以Debian系发行版Kali为例

首先,安装fcitx输入法框架和fcitx-pinyin作为默认的拼音输入法:

1
apt-get install fcitx fcitx-pinyin

安装好之后,系统应该会多出这两个图标(在gnome桌面下)

之后,将系统的输入源切换到fcitx,运行

1
im-config

或是在菜单中,点击这个红圈圈住的图标:

一路下一步,在最后一步选择fcitx

切换好之后,在fcitx中配置pinyin输入法:

选择pinyin即可,不需要选择google拼音(感觉并没有fcitx-pinyin好用)
然后重启系统,或是重启X11系统。

然后就可以用啦,默认是Ctrl+空格切换输入法:

如果不想用Ctrl+空格切换输入法,可以改为使用shift切换输入法,同样是在fcitx中配置:

之前研究Linux设备驱动时做的零零散散的笔记,整理出来,方便以后复习。

驱动程序的的角色

  • 提供机制

例如:
unix图形界面分为X服务器和窗口会话管理器,X服务器理解硬件及提供统一的接口给用户程序,窗口管理器实现了特别的策略但对硬件一无所知。

目标:实现对策略透明

  • 划分内核

进程管理
负责创建和销毁进程,并处理它们与外部的联系(输入和输出)。
实现了多个进程在一个单个或几个CPU之上的抽象。

内存管理
为每一个进程在有限的可用资源上建立了虚拟地址空间。

文件系统
在非结构化的硬件之上建立了一个结构化的文件系统。

设备控制
全部设备的控制操作都由特定的寻址设备相关的代码来进行。

网络
系统负责在程序和网络接口之间递送数据报文。

可加载模块

Linux特性:可以在运行时扩展由内核提供的特性,可以在系统正在运行的时候增加内核的功能(也可以去除)。
每块可以在运行时添加到内核的代码被称为一个模块。通过insmod和rmmod程序去连接。

设备和模块的分类

字符设备

字符(char)设备是一种可以当作字节流来存取的设备。这样的驱动常常实现open,close,read,write系统调用。
例如:文本控制台(/dev/console),串口(/dev/ttyS0)

块设备

通过位于/dev目录的文件系统节点来存取,可以驻有文件系统。与字符设备的区别在于内部管理数据的方式上–块设备允许一次传送任意数据的字节。

网络接口

负责发送和接收数据报文

安全问题

小心对待输入,未初始化的内存等,从内核获取的任何内存应当清零或者在其可用之前进行初始化。

版本编号

Linux系统中使用的每一个软件包存有自己的发行版本号,它们之间存在相互依赖性。

版权条款

字符驱动

scull
Simple Character Utility for Loading Localities

设备编号

[root@centos ~]$ ll /dev
total 0                                        主编号,次编号
drwxr-xr-x. 2 root    root         640 May 12 22:24 block
crw-------. 1 root    root     10, 234 May 12 22:24 btrfs-control
drwxr-xr-x. 3 root    root          60 May 12 22:24 bus
lrwxrwxrwx. 1 root    root           3 May 12 22:24 cdrom -> sr0
drwxr-xr-x. 3 root    root          80 May 12 22:24 cpu
crw-rw----. 1 root    root     10,  62 May 12 22:24 crash
drwxr-xr-x. 6 root    root         120 May 12 22:24 disk
brw-rw----. 1 root    disk    253,   0 May 12 22:24 dm-0
drwxr-xr-x. 2 root    root          60 May 12 22:24 dri
lrwxrwxrwx. 1 root    root           3 May 12 22:24 fb -> fb0
crw-rw----. 1 root    root     29,   0 May 12 22:24 fb0
crw-rw-rw-. 1 root    root      1,   7 May 12 22:24 full

主编号标识设备相连的驱动,次编号被内核用来决定引用哪个设备。

一直想给安装一个缩略图点击弹出的插件,但是找了找几乎都是用的php来做的,插件的使用和安装极其繁琐,于是上网查了些demo,自己实现了一个纯js的图片弹出插件。

实现的思路是通过编写hook图片的onclick事件的函数,在函数中对body追加div元素,再将传入的图片对象放入元素中,同时再监听div的onclilck事件,当捕捉到点击,再关闭(其实是隐藏)弹出的div。
通过在函数初始化的时候收集页面所有的img元素,再为每个img元素增加onclick=”picHook(this)”这条属性,这样当图片在被点击时,这个函数就能自动创建div蒙板背景,并获取被点击图片的宽度和高度,同时生成一个新的和图片一样大小的div来显示图片。当蒙板再次被点击时,hook事件再次响应,并将蒙板和图片div的style置为none,弹出的图片就被关闭了。
说起来很简单,做起来就更简单了,简单到只需要一个函数即可实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<script>
function picHook(pic){
/*图片对象*/
var imgs = document.getElementsByTagName("img");
/*前景div*/
var light = document.getElementById('light') || document.createElement("div");
/*背景div*/
var bg = document.getElementById('bg') || document.createElement("div");
/*图片放大*/
var s_pic = document.getElementById('s_pic') || document.createElement("img");
/*css对象*/
var css = document.createElement("style");
/*css样式*/
var csstext = '\
.pic_bg{\
position: absolute;\
margin:0 auto; \
top: 0%;\
left: 0%;\
right: 0%;\
width: 100%;\
padding-bottom: 1000%;\
background-color: black;\
z-index:1001;\
opacity:.80;\
filter: alpha(opacity=80);\
overflow:scroll;\
}\

.pic_div {\
margin-bottom: auto;\
position: fixed;\
left:30%;\
top:20%;\
width: 100%;\
padding-top:25px;\
margin-left:-250px;\
margin-top:-100px;\
z-index:1002;\
}';
/*收集页面所有图片对象*/
for(i=0; i<imgs.length;i++){
imgs[i].setAttribute("onclick", "picHook(this)" );
}
css.type = "text/css";

/*关闭图像*/
if( !pic ){
bg.style.display = light.style.display = "none";
}

/*ie兼容*/
if(css.styleSheet){
css.styleSheet.cssText = csstext;
}else{
css.appendChild(document.createTextNode(csstext));
}

s_pic.setAttribute("id", "s_pic");
s_pic.setAttribute("src", pic.src);
s_pic.setAttribute("width","70%");
s_pic.setAttribute("height","65%");
s_pic.setAttribute("margin","0 auto");
s_pic.style.display = 'block';
light.setAttribute("id", "light");
light.setAttribute("class", "pic_div");
light.style.display = 'block';
light.appendChild(s_pic);
light.setAttribute("onclick", "picHook()");
bg.setAttribute("id", "bg");
bg.setAttribute("class", "pic_bg");
bg.setAttribute("onclick", "picHook()");
bg.style.display = light.style.display;
document.getElementsByTagName("head")[0].appendChild(css);
document.body.appendChild(bg);
document.body.appendChild(light);
}
</script>

将这段代码保存在页面的head中,再将body的onload事件绑定到picHook()函数,你的页面中就也可以实现图片点击弹出大图啦。
还存在一点小bug,主要是因为我不太熟悉css,导致div的样式做的有点难看。
css的样式我是直接声明在js里的,这样就不用再另外创建css文件了。
强迫症,没办法。
等有时间再琢磨琢磨css,优化下样式。

默认安装的MySQL数据库,无法远程连接。
登录MySQL之后,运行

1
SELECT user,host from mysql.user;

如果只有一条记录,说明是这个原因。
将下面的脚本保存成user.sql,登录MySQL,运行:

1
2
3
use mysql;
source user.sql;
flush privileges;

Notice: 会重置MySQL user表,并且将root用户密码设置为空。

脚本内容: 点这里直接下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
USE mysql;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '',
`Password` char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
`Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Reload_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Shutdown_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Process_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`File_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Show_db_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Super_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Repl_slave_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Repl_client_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Create_user_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`ssl_type` enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '',
`ssl_cipher` blob NOT NULL,
`x509_issuer` blob NOT NULL,
`x509_subject` blob NOT NULL,
`max_questions` int(11) unsigned NOT NULL DEFAULT '0',
`max_updates` int(11) unsigned NOT NULL DEFAULT '0',
`max_connections` int(11) unsigned NOT NULL DEFAULT '0',
`max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges';

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('localhost', 'root', '', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', '', '', '', '0', '0', '0', '0');
INSERT INTO `user` VALUES ('127.0.0.1', 'root', '', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', '', '', '', '0', '0', '0', '0');
INSERT INTO `user` VALUES ('%', 'root', '', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', '', '', '', '0', '0', '0', '0');
flush privileges;

Python
先按F5,之后将下面的命令保存,再设置快捷键。

1
cmd /k  c:\python27\python "$(FULL_CURRENT_PATH)" & PAUSE & EXIT

我使用的是Ctrl+F9
之后按Ctrl+s保存更改,再按Ctrl+F7即可快速在cmd中运行代码,方便调试。

Ruby

1
cmd /k  C:\Ruby22-x64\bin\ruby "$(FULL_CURRENT_PATH)" & PAUSE & EXIT

定义类

定义类的过程就是定义类的属性的过程。类的属性就是类的静态属性的简称,指类内包含的各项数据。类的服务被称为成员函数或方法。

继承(extends)

通过定义继承方法,子类可以获得父类的所有属性和方法。

接口(implements)

说明当前类中实现了哪个接口定义的功能和方法,是Java语言中实现多重继承的一种机制

修饰符

  • 访问控制符
  • 抽象类说明符
  • 最终说明符

类的属性

描述了类内部的信息,又称为类的静态属性。类属性为简单变量。

【修饰符】 变量类型 变量名【=变量初值】
【修饰符】 类名 对象名 【=new 类名(实际参数列表)】

若使用另一个类的对象作为当前类所定义的类的属性,要保证该对象所在的类在当前类中是可已被当前类所引用的

类属性的修饰符

  • 访问控制符
  • 静态修饰符static
  • 最终修饰符final
  • 易失修饰符volatile
  • 过渡修饰符transient

类的方法(成员函数)

用来规定类属性上的操作,实现类内部功能的机制,也是类与外界交互的窗口。

声明方法的语法:
【修饰符】返回值类型 方法名(参数列表)
Trows 例外名1,例外名2,… {
方法体:
局部变量声明;
语句序列;
}

方法的修饰符

  • 访问控制符
  • 静态修饰符static
  • 抽象修饰符abstract
  • 最终修饰符final
  • 同步修饰符synchronous
  • 本地修饰符native

类的构造函数

特殊之处:

  • 构造函数的方法名与类名相同
  • 构造函数没有返回类型
  • 构造函数的主要作用是完成对类对象的初始化工作

Java在声明类时,可以不定义构造函数,系统会自动为该类生成一个默认的构造函数,此时这个构造函数的名字与类名相同,没有任何形式参数,也不完成任何操作。

在创建一个类的新对象的同时,系统会自动调用该类的构造函数为新对象初始化。

类的访问控制符只有一个public

属性和方法的访问控制符有:

  • public
  • private
  • protected
  • private protected

对于同一个包中的类可以不需任何说明,方便的互相访问和引用。

在不同包中的类,只有他们都声明为public时,然后再在程序头部声明import后才可以被访问和引用相应的类。

publici修饰的类的属性称为公共属性,如果公共属性属于一个公共类,则可以被所有的其他类使用。

public修饰符会造成安全性和数据封装性下降,应尽量减少public属性的使用。

缺省访问控制符规定该类只能被同一个包中的类访问和引用,而不可以被其他包中的类引用,这种特性称为包访问性。

类内的属性和方法如果没有访问控制符来限定,也说明他们具有包访问性,可以被同一个包中的其他类所访问和调用。

用private修饰的属性或方法只能被该类自身所访问和修改,而不能被任何其他类,包括该类的子类来获取和引用。

procted修饰的属性可以被该类自身和包中的类访问。

静态属性

被static修饰的属性称为静态属性,静态属性是一个公共的存储单元。任何一个类的对象访问它时取到的都是相同的值,任何一个类的对象去修改它时,都是在对同一个内存单元做操作。

static修饰符修饰的属性是属于类的公共属性

static修饰符修饰的方法是属于整个类的方法

不用static修饰符修饰的方法是属于某个具体对象或实例的方法

声明一个方法的static至少有三重含义:

  • 调用这个方法时,应该使用类名做前缀,而不是某一个具体的对象名
  • 非static的方法是属于某个对象的方法,在这个对象创建时对象的方法在内存中拥有自己的专用代码段,而static的方法是属于整个类的,它在内存中的代码段将随着类的定义而分配和装载,不被任何一个对象专有。
  • static方法只能处理static类型的数据

静态初始化器

是由关键字static引导的一对大括号括起的语句组。

静态初始化器与构造函数的区别:
构造函数是对每个新创建的对象初始化,而静态初始化器是对每个类进行初始化。

构造函数是在用new运算符产生新对象时由系统自动执行,而静态初始化器则是在它所属的类加载入内存时由系统调用运行的。

不同于构造函数,静态初始化器不是方法,没有方法名,返回值和参数列表。

抽象类

当一个类被生命为abstract时,这个类被称为抽象类。

所谓抽象类就是没有具体实例对象的类。

抽象类是它所有子类的公共属性的集合。

使用抽象类的一大优点就是可以利用这些公共属性来提高开发和维护程序的效率。

最终类,最终属性,最终方法,终结器

  • 最终类

  • 如果一个类被final修饰符所修饰和限定,说明这个类不可能拥有子类

  • 最终属性

final就是用来修饰常量的修饰符,一个类的成员变量如果被声明为final,那么它的取值在程序的执行过程中都不会改变,也就是一个常量

用final修饰符说明常量时:

需要说明常量的数据类型

需要同时指出常量的具体取值

因为所有类的对象的常量成员,其数值都固定一致,为了节省空间,常量通常都被声明为static。

final修饰符所修饰的类方法,是功能和内部语句都不能被更改的最终方法,即是不能被当前类的子类重载的方法。

final方法固定所对应的具体操作,防止子类对父类关键方法的错误的重定义,保证了程序的安全性和正确性。

所有已被private修饰符限定为私有的方法,以及所包含在final类中的方法,都被缺省的认为是final的。

  • 终结器
  • 终结器是回收对象时执行的方法。

protected void finalize(){}

终结器是一个名为finalize的方法,没有产生列表和返回值。

volatile修饰符

被volatile修饰的类的属性可能同时被几个线程控制和修改,通常用来修饰受外部输入的属性。

使用Phantomjs访问网页并截图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var page = require('webpage').create();

page.onResourceRequested = function(request) {
console.log('Request ' + JSON.stringify(request, undefined, 4));
console.log( '---------------------------------------------------------------------' );
};
page.onResourceReceived = function(response) {
console.log('Receive ' + JSON.stringify(response, undefined, 4));
console.log( '---------------------------------------------------------------------' );
};

page.open('https://www.baidu.com', function(status) {
console.log("Status: " + status);
if(status === "success") {
page.render('example.png');
}
phantom.exit();
});


example.png

example.png

Phantomjs已经接近废弃,如今推荐使用google的headless技术。

环境:
Windows 10
Python 2.7.10

0x01 安装PyQt4
在这个页面下载,注意选对版本。
https://riverbankcomputing.com/software/pyqt/download
我选择的版本是 PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x64.exe

0x02 编写测试脚本

1
2
3
4
5
6
7
8
9
import sys
from PyQt4 import QtGui

app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
widget.resize(250, 150)
widget.setWindowTitle('PyQt')
widget.show()
sys.exit(app.exec_())

如果成功运行并弹出一个空白的窗口,说明PyQt4已经安装上了。

0x03 使用PyQt4的QtWebKit实现解析Dom

待续。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×