编程


快捷键 操作
Ctrl + a 移到命令行首
Ctrl + e 移到命令行尾
Ctrl + f 按字符前移(右向)
Ctrl + b 按字符后移(左向)
Alt + f 按单词前移(右向)
Alt + b 按单词后移(左向)
Ctrl + xx 在命令行首和光标之间移动
Ctrl + u 从光标处删除至命令行首
Ctrl + k 从光标处删除至命令行尾
Ctrl + w 从光标处删除至字首
Alt + d 从光标处删除至字尾
Ctrl + d 删除光标处的字符
Ctrl + h 删除光标前的字符
Ctrl + y 粘贴至光标后
Alt + c 从光标处更改为首字母大写的单词
Alt + u 从光标处更改为全部大写的单词
Alt + l 从光标处更改为全部小写的单词
Ctrl + t 交换光标处和之前的字符
Alt + t 交换光标处和之前的单词
Alt + Backspace 与 Ctrl + w 类似 重新执行命令
Ctrl + r 逆向搜索命令历史
Ctrl + g 从历史搜索模式退出
Ctrl + p 历史中的上一条命令
Ctrl + n 历史中的下一条命令
Alt + . 使用上一条命令的最后一个参数 控制命令
Ctrl + l 清屏
Ctrl + o 执行当前命令,并选择上一条命令
Ctrl + s 阻止屏幕输出
Ctrl + q 允许屏幕输出
Ctrl + c 终止命令
Ctrl + z 挂起命令 Bang (!)命令
!! 执行上一条命令
!blah 执行最近的以 blah 开头的命令,如 !ls
!blah:p 仅打印输出,而不执行
!$ 上一条命令的最后一个参数,与 Alt + . 相同
!$:p 打印输出 !$ 的内容
!* 上一条命令的所有参数
!*:p 打印输出 !* 的内容
^blah 删除上一条命令中的 blah
^blah^foo 将上一条命令中的 blah 替换为 foo
^blah^foo^ 将上一条命令中所有的 blah 都替换为 foo


创建本地分支

1
git branch NAME

切换本地分支

1
git checkout NAME

查看远程分支

1
git branch -r

推送本地分支到远程仓库

1
git push origin NAME

引用子模块

1
2
3
git submodule add RESPOSITORIES PATH
# RESPOSSITORIES 是仓库地址
# PATH是本地路径,默认是当前目录

克隆带有子模块的项目

1
2
3
4
git clone XXX
cd XXX
git submodule init
git submodule update

跟踪其他分支

1
2
3
4
5
git remote add upstream XXX.git
git fetch upstream
git checkout master
git rebase upstream/master
git push -f origin master



一直想给安装一个缩略图点击弹出的插件,但是找了找几乎都是用的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,优化下样式。



定义类

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

继承(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修饰的类的属性可能同时被几个线程控制和修改,通常用来修饰受外部输入的属性。



Debian是我日常使用的桌面系统,这里记录了我在使用Debian和其他Linux时所有的问题和解决办法,以及一些其他的心得体会。
向Debian致敬!


找回桌面系统关机按钮

在/etc/polkit-1/localauthority/50-local.d/新建文件50-admin.pkla,写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
[disable suspend]   
Identity=unix-user:*
Action=org.freedesktop.upower.suspend
ResultAny=no
ResultInactive=no
ResultActive=no
[disable hibernate]
Identity=unix-user:*
Action=org.freedesktop.upower.hibernate
ResultAny=no
ResultInactive=no
ResultActive=no


无线不能使用的解决方法

安装rfkill工具

install rfkill```
1
2
3
4

然后运行:

```rfkill list all

可以查看到网卡设备的状态如下:

1
2
3
4
5
6
0: dell-wlan: Wireless LAN   
Soft blocked: no
Hard blocked: no
1: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no

根据软件或者硬件的状态解锁。


wireshark设置普通用户权限

1
2
3
4
5
6
sudo groupadd wireshark
sudo usermod -a -G wireshark YOUR_USER_NAME
sudo chgrp wireshark /usr/bin/dumpcap
sudo chmod 750 /usr/bin/dumpcap
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap
sudo getcap /usr/bin/dumpcap

Adobe flash player的正确安装姿势

首先下载flash player,保存为flash.tar.gz
then:

1
2
3
tar -zxvf flash.tar.gz
mv usr/* /usr/
mv libflashplayer.so /usr/lib/mozilla/plugins/

重启浏览器即可。


修复Debian8 pptp不能连接的问题

报错内容:(/var/log/syslog)

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
Sep 15 14:58:21 debian NetworkManager[13550]: ** Message: pppd started with pid 14188
Sep 15 14:58:21 debian pppd[14188]: Plugin /usr/lib/pppd/2.4.6/nm-pptp-pppd-plugin.so loaded.
Sep 15 14:58:21 debian NetworkManager[13550]: Plugin /usr/lib/pppd/2.4.6/nm-pptp-pppd-plugin.so loaded.
Sep 15 14:58:21 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (plugin_init): initializing
Sep 15 14:58:21 debian pppd[14188]: pppd 2.4.6 started by root, uid 0
Sep 15 14:58:21 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (nm_phasechange): status 3 / phase 'serial connection'
Sep 15 14:58:21 debian pppd[14188]: Using interface ppp0
Sep 15 14:58:21 debian pppd[14188]: Connect: ppp0 <--> /dev/pts/0
Sep 15 14:58:21 debian NetworkManager[13550]: Using interface ppp0
Sep 15 14:58:21 debian NetworkManager[13550]: Connect: ppp0 <--> /dev/pts/0
Sep 15 14:58:21 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
Sep 15 14:58:21 debian NetworkManager[13550]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 57)
Sep 15 14:58:21 debian NetworkManager[13550]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/15
Sep 15 14:58:21 debian NetworkManager[13550]: <info> devices added (path: /sys/devices/virtual/net/ppp0, iface: ppp0)
Sep 15 14:58:21 debian NetworkManager[13550]: <info> device added (path: /sys/devices/virtual/net/ppp0, iface: ppp0): no ifupdown configuration found.
Sep 15 14:58:52 debian pppd[14188]: LCP: timeout sending Config-Requests
Sep 15 14:58:52 debian pppd[14188]: Connection terminated.
Sep 15 14:58:52 debian avahi-daemon[588]: Withdrawing workstation service for ppp0.
Sep 15 14:58:52 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (nm_phasechange): status 11 / phase 'disconnect'
Sep 15 14:58:52 debian NetworkManager[13550]: <info> devices removed (path: /sys/devices/virtual/net/ppp0, iface: ppp0)
Sep 15 14:58:52 debian pptp[14193]: nm-pptp-service-14185 warn[decaps_hdlc:pptp_gre.c:216]: pppd may have shutdown, see pppd log
Sep 15 14:58:52 debian pppd[14188]: Modem hangup
Sep 15 14:58:52 debian pppd[14188]: Exit.
Sep 15 14:58:52 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (nm_phasechange): status 1 / phase 'dead'
Sep 15 14:58:52 debian NetworkManager[13550]: ** Message: nm-pptp-ppp-plugin: (nm_exit_notify): cleaning up
Sep 15 14:58:52 debian NetworkManager[13550]: ** (nm-pptp-service:14185): WARNING **: pppd exited with error code 16

依次输入如下命令:

1
2
3
modprobe nf_nat_pptp
modprobe nf_conntrack_pptp
modprobe nf_conntrack_proto_gre

下次启动还会失效,所以要编辑/etc/modules-load.d/modules.conf文件,添加如下三行:

1
2
3
nf_nat_pptp
nf_conntrack_pptp
nf_conntrack_proto_gre


减少开机等待时间

  • 减少grub等待时间:
    修改/etc/default/grub,将
    1
    GRUB_TIMEOUT=5

改为 (grub等待超时改成1秒):

1
GRUB_TIMEOUT=1

然后运行

1
# update-grup

  • 减少开关机超时
    修改/etc/systemd/system.conf,修改开关机超时时间为5s:
    1
    2
    DefaultTimeoutStartSec=5s
    DefaultTimeoutStopSec=5s

重启


使用随机MAC地址

出于隐私需求,不想暴露真实mac地址,把下面这个脚本的内容加入/etc/init.d,就可以在每次开机的时候为网卡随机设置一个mac地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
#Chorder
#2016/12/08

mhash=`date +%s|md5sum`

ifconfig eth0 down
ifconfig eth0 hw ether `echo ${mhash:$((RANDOM % 13)):2}:\
${mhash:$((RANDOM % 13)):2}:${mhash:$((RANDOM % 13)):2}:\
${mhash:$((RANDOM % 15)):2}:${mhash:$((RANDOM % 15)):2}:\
${mhash:$((RANDOM % 15)):2} `
ifconfig eth0 up

ifconfig wlan0 down
ifconfig wlan0 hw ether `echo ${mhash:$((RANDOM % 13)):2}:\
${mhash:$((RANDOM % 13)):2}:${mhash:$((RANDOM % 13)):2}:\
${mhash:$((RANDOM % 15)):2}:${mhash:$((RANDOM % 15)):2}:\
${mhash:$((RANDOM % 15)):2} `
ifconfig wlan0 up


gnome删除多余的菜单文件夹

运行

1
gsettings set org.gnome.desktop.app-folders folder-children ['']


CentOS删除旧内核

1
for k in `rpm -qa | grep kernel |grep -v \`uname -r\``;do yum remove $k;done

鼠标滚轮方向调整

修改 ~/.Xmodmap 文件

如果想要鼠标逆序滚动,就将其中的内容改为

pointer = 1 2 3 4 5 6 7 8 9 10 11 12

否则,就将内容改为:

pointer = 1 2 3 5 4 6 7 8 9 10 11 12

4和5代表的是鼠标滚轮方向的映射。


Debian9 GNOME 允许root登录

修改三个文件:

  1. /etc/gdm3/daemon.conf

在Security节,加入

1
AllowRoot=true

  1. /etc/pam.d/gdm-password

注释掉下面这一行

1
auth required pam_succeed_if.so_user != root quiet_success

  1. /etc/pam.d/gdm-autollgoin

注释掉下面这一行

1
auth required pam_succeed_if.so_user != root quiet_success

重启。



内核源码树

arch        特定体系结构的源码
block        Crypto API
crypto        内核源码文档
drivers        设备驱动程序
firmware    
fs        VFS和各种文件系统
include        内核头文件
init        内核引导和初始化
ipc        进程间通信代码
kernel        像调度程序这样的核心子系统
lib        通用内核函数
Makefile    
Makefile.common
mm        内存管理子系统和VM
Module.symvers
net        网络子系统
samples
scripts        编译内核所用的脚本
security    Linux安全模块
sound        语音子系统
System.map
tools
usr        早期用户空间代码
virt

编译内核

配置内核(不同的选项)

make config
make menuconfig
make xconfig
make gconfig

创建默认配置

make defconfig
make oldconfig

编译

make

记录编译信息

make >../log.txt

忽略编译信息

make >/dev/null

衍生多个编译作业

make -j[任务数量]

如双核处理器上,每个处理器衍生两个作业

make -j4

安装内核

把arch/i386/boot/bzImage拷贝到/boot
依照vmlinuz-version来命名
编辑/boot/grub/grub.conf文件,为新内核建立新的启动项
使用LILO的系统则编辑/etc/lilo.conf,然后运行lilo

安装模块

make modules_install

内核开发的特点

  1. 没有libc库

大部分常用的C库函数在内核中都已经得到了实现

  1. GNU C

内联函数

把对时间要求比较高而本身长度又比较短的函数定义成内联函数

1
static inline void test(unsigned long tail_size)

内联函数必须在使用前就定义好,一般在头文件或者文件头中定义内联函数。

内联汇编

分支声明(为了优化)

1
2
3
4
5
6
7
8
9
likely()
unlikely()
if(unlikely(foo){
true/*****/
}

if (likely(foo)){
true/****/
}
  1. 没有内存保护机制
  2. 不要轻易在内核中使用浮点数
  3. 内核空间具有容积小而固定的栈
  4. 同步和并发
  5. 可移植性

进程管理

进程是处于执行期的程序以及它所包含的资源的总称

  1. 进程描述符及任务结构
  • 内核把进程存放在任务队列中。任务队列是各双向循环链表,链表中的每一项都是类型为task_struct,称为进程描述符的结构,定义在<linux/sched.h>文件中。

  • 进程描述符中包含一个具体进程的所有信息

  • task_struct在32位机器上有1.7k字节,其中包含的数据能完整的描述一个正在执行的程序。(打开的文件,进程的地址空间,挂起的信号,进程的状态还有其他更多信息)

  1. 分配进程描述符

通过slab分配器分配task_struct结构

  1. 进程描述符的存放

内核通过一个唯一的进程标识值或PID类标识每个进程,PID最大默认值为32768(short int的最大值),可以修改/proc/sys/kernle/pid_max来提高上限。

  1. 进程状态

进程描述符中的state域描述了进程的当前状态,系统中每个进程都必须处于五种状态中的一种。

  • TASK_RUNNING(运行)
  • TASK_INTERRUPTIBLE(可中断)
  • TASK_UNINTERRUPTIBLE(不可中断)
  • TASK_ZOMBIE(僵死)
  • TASK_STOPPED(停止)
  1. 设置当前进程状态
1
2
3
set_task_state(task,state);
/*将任务task的状态设置为state*/
set_current_state(state) <=> set_stask_state(current,state)
  1. 进程上下文

进程家族树

  • 所有的进程都是PID为1的init进程的后台

  • 内核在系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行其他的相关程序

  • 进程间的关系存放在进程描述符中,每个tack_struct都包含一个指向其父进程task_struct,叫做parent的指针

获取其父进程的进程描述符:

1
struct task_struct *my_parent = ourrent->parent;

访问子进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct task_struct *task;
struct list_head *list;

list_for_each(list,$current->children){
truetask = list_entry(list,sruct task_struct,sibling);
true/* task 现在指向当前的某个子进程*/
}

struct task_struct *task;
for (task =current;task != $init_task; task= task->parent)
/* task 现在指向init */

}

系统调用

  • API.POSIX.C
  • 系统调用(系统调用号)
  • 系统调用处理程序

    int $0x80(十进制128) system_call()
    第128号异常处理程序
    系统调用号通过eax寄存器传递给内核
    call *sys_call_table(,%eax,4)
    系统调用表的表项是以32位类型存放的,所以内核需要将给定的系统调用号乘以4,然后查询
    参数传递:ebx,ecx,edx,esi和edi按照顺序存放前五个参数

  • 系统调用的实现

  • 系统调用上下文

    • entry.s(系统调用表)
  • 中断和中断处理程序

    • IRQ:中断请求
    • ISR:中断服务例程
  • 注册中断处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//request_irq()成功执行会返回0
int request_irq(unsigned int irq,
trueirqreturn_t(*handler)(int,void *,struct pt_regs *),
trueunsighed long irqflags,
trueconst char* devname,
truevoid *dev_id)

/*
参数说明:
irq:要分配的中断号
handler:指针,指向处理这个中断的世纪中断处理程序。
handler函数的原型是特定的,接受三个参数,并有一个类型为irqresutn_t的返回值。
irqflags:可以为0,也可以为下列标志的位掩码
SA_INTERRUPT:表示给定的中断处理程序是一个快速中断处理程序
SA_SAMPLE_RANDOM:
SA_SHIRQ:
devname:与中断相关的ASCII文本表示法.
dev_id:
*/


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

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



Your browser is out-of-date!

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

×