编程

内核源码树

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){
/*****/
}

if (likely(foo)){
/****/
}
  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){
task = list_entry(list,sruct task_struct,sibling);
/* 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,
irqreturn_t(*handler)(int,void *,struct pt_regs *),
unsighed long irqflags,
const char* devname,
void *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

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

默认安装的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

使用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技术。

首先,如果没有签名密钥,先生成密钥:

1
keytool -genkey -alias android.keystore -keyalg RSA -validity 20000 -keystore android.keystore

接着会提示你输入密码、地区、国家等等,根据提示输入即可。
我这里的用户名和密码都是android

然后使用密钥进行签名

1
jarsigner -keystore android.keystore -storepass android -keypass android XXX.apk android.keystore

keytool和jarsigner都是jdk自带的工具,配好环境变量就可以直接在命令行使用的。

https://startssl.com 这个网站可以给我们免费提供可信任的https证书,这里简单介绍一下配置的过程。
首先服务器需要安装openssl和apache的mod_ssl.so模块,并且需要在httpd.conf中开启这个模块。
完成上述操作之后就可以使用openssl生成你自己的证书了。
这里有些知识你需要了解一下。
https的整个服务中,你需要了解这些文件的作用:

server.key 服务器的私钥
server.crt 服务器的证书文件
server.csr 服务器证书请求文件
root.crt   根证书

这些文件是这样生成的

首先运行

1
openssl req -new -nodes -keyout chorder.net.key -out chorder.net.csr

生成服务器证书和服务器证书请求文件,过程中会要求输入很多有关证书的信息和密码。
这一步完成之后会生成两个文件,chorder.net.key和chorder.net.csr
这时候去startssl网站上注册帐号,然后提交自己生成的.csr文件,让startssl为你生成一个
服务器证书和根证书。
把两个.crt结尾的证书拷贝到服务器的/etc/pki/tls/certs/下,把.key和.csr文件拷贝到/etc/pki/tls/private/下。(只针对CentOS服务器,其他服务器请自行百度)。
另外还需要将/etc/pki/tls/下的cert.pem(如果没有则创建一个)链接指向到/etc/pki/tls/certs/root.crt
这样用火狐浏览器访问的时候才不会报 SEC_ERROR_UNKNOWN_ISSUER 这个错误。
这个cert.pem就是证书链,只有当你的服务器证书包含在startssl网站的证书链中,客户端才会认为你的证书是可信的。

最后,修改两个文件。

1 /etc/httpd/conf.d/ssl.conf

在该文件中修改以下内容:

指定服务器证书

1
SSLCertificateFile /etc/pki/tls/certs/chorder.net.crt

指定服务器私钥

1
SSLCertificateKeyFile /etc/pki/tls/private/chorder.net.key

指定服务器证书链

1
SSLCertificateChainFile /etc/pki/tls/certs/root-bundle.crt

2 /etc/httpd/conf/httpd.conf

为你的主机(我这里是虚拟主机)创建配置

我的配置如下

NameVirtualHost *:443
<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/pki/tls/certs/chorder.net.crt
    SSLCertificateKeyFile /etc/pki/tls/private/chorder.net.key
    SSLCertificateChainFile /etc/pki/tls/cert.pem
    ServerName chorder.net
    ServerAdmin ×××××
    DocumentRoot ×××××
    ErrorLog ×××××
    CustomLog ×××××
</VirtualHost>

其实就是http配置文件下多加了几行。
配置好这些以后,重启服务,完成。
另外,如果你希望访问你的域名直接跳转到https端口,可以写一段js来跳转,如果觉得
写代码太麻烦,可以在根目录的.htaccess文件中加入这两行来帮你自动跳转:

1
2
RewriteCond %{SERVER_PORT} !^443$
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R]

这样就将默认端口指定为443,并且将所有http的请求重写为https.

Your browser is out-of-date!

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

×