IntelliJ IDEA 2016.1.1的Struts2项目中引入Junit4.12单元测试

使用IntelliJ IDEA 2016.1建立Strut2工程并使用Tomcat调试建立项目后,想引入Junit进行TDD的开发,总结如下:

1.点击工程右上方的"Project Structure"图标

select_structure

2.在弹出的界面中的左侧选择"Libraries",然后如下图所示,按照编号的顺序,依次点击,输入数据。注意,如果此时"OK"按键是灰色的,那么需要点击右侧的放大镜图标,让IntelliJ IDEA去服务器上搜索一下,等他搜索完成了,那么下面的"OK"按键自然是可以点击的了。

maven_add_junit4

3.点击"OK"按键

choose_modules

4.此时发现提示存在一个"Problem",点击然后按照下图展示修复这个问题。

click_success_problems

fix_junit4_problem

fix_junit4_problem_select_menu

点击"OK",关闭这个页面。

5.在工程的"src"目录下面新建名为"test"的目录,他的子目录与源代码的目录相同即可,IntelliJ IDEA会自动把这个目录当作单元测试的目录,如下图所示:

UnitTestForder

测试代码如下:

import org.junit.Before;
import org.junit.After;
import org.junit.Test;

public class testBasic {
    @Before
    public void before() throws Exception {

    }

    @Test
    public void myTest(){

    }

    @After
    public void after() throws Exception {
    }
}

然后右击测试文件,会出现如下图的菜单选项DebugTestFile

有时候他不会出现这些菜单,那说明还没有测试函数,添加一个空的@Test测试函数就可以了,比如下面的样子:

import org.junit.Test; 
import org.junit.Before; 
import org.junit.After; 

/** 
* Basic Tester. 
* 
* @author <Authors name> 
* @since <pre>五月 10, 2016</pre> 
* @version 1.0 
*/ 
public class BasicTest { 

    @Before
    public void before() throws Exception {
    }

    @Test
    public void myTest(){

    }

    @After
    public void after() throws Exception {
    }
}

也可以使用JunitGenerator V2.0这个插件来自动生成单元测试代码。
打开IntelliJ IDEA工具,Alt+Ctrl+S,弹出窗口如下:IDEASettings

在文本框中输入Plugins进行插件搜索设置。

IdeaPluginsInstall

点击按钮,从插件资源库中安装新的插件。

从插件资源库中搜索JunitGenerator V2.0版本,点击右侧的Install按钮.

JunitGeneratorV2Install

安装完成后,点击"Restart IntelliJ IDEA"重启IntelliJ IDEA。

JunitGeneratorV2InstallRestartIdea

现在可通过此工具自动完成test类的生成了,在需要进行单元测试的类中Alt+Insert

AutoGeneratorJunit

生成的自动测试代码如下图所示

AutoJunitSouces

char*-vector

 

1.如果是想要read到vector中,首先给vector分配足够的大小,之后使用&V[0]即可

std::vector<char> buffer(lSize);
// 如果vector已经存在的话,使用resize方法
// buffer.resize(lSize)
std::fread(&buffer[0], 1, buffer.size(), pFile);

2.非read情况,想要将vector<char>转为char*的话

局部使用可以直接

reinterpret_cast<char*> (&buf[0]);

c++ 11后支持

reinterpret_cast<char*>(buf.data());

非局部需要把vector拷贝到char*,首先给char*分配内存,然后拷贝

char* cbuffer = (char*)malloc(lSize * sizeof(char));

std::copy(buffer.begin(), buffer.end(), cbuffer);

从零创建WordPress自定义插件

序曲


使用WordPress统计插件WP Statistics之后,经常会出现某个IP的访问突然出现比较多的情况,那么这个时候就需要了解这个IP访问的是哪个页面,是否发生了访问攻击。但是遗憾的是,WP Statistics并没有根据IP地址查询访问记录的功能,因此,我们就自己写一个查询插件好了。

创建插件的文件和文件夹


WordPress插件存储在wp-content/plugins/文件夹中,而我们的新建文件也要存放在这个文件夹中。一般情况下,如果所制作的插件非常简单,可直接把所有代码放在一个PHP文件中,然后把其放在wp-content/plugins/文件夹中。但是,我们这里要制作的插件要使用两个文件(一个是主要的插件文件,另一个为执行管理页面的文件),因此,我们需要把新创建的文件另放在一个文件夹中,我们这里把这个文件夹命名为wp-statistics-visitor-query

创建插件的功能文件


我们就要创建插件主要文件了,我们把其命名为wp-statistics-visitor-query.php。当然,你也可把其命名为其它名称,这并不重要。

然后再文件的头部增加插件的描述信息,如果不增加描述信息,WordPress是找不到这个插件的。增加的内容如下:

<?php
/*
Plugin Name: WP Statistics Visitors Query
Plugin URI: http://www.mobibrw.com
Description: Plugin for query WP Statistics Visitors
Author: LongSky
Version: 1.0
Author URI: http://www.mobibrw.com
*/
?>

这样操作完成后,就可以在插件管理界面中找到名字为"WP Statistics Visitors Query"的插件了。

使用行为钩子(Action Hook)


虽然插件现在已在管理面板显示,但是由于它只含有header信息,因此它并没有其它功用。现在,我们就来增加它的功能吧。
WordPress允许用户把插件代码放在模板的任意位置,包括页面的空间位置及页面创建过程中的逻辑位置。在此,我们将进一步了解后者,逻辑位置——即行为钩子。

行为钩子

我们可把行为钩子视为回调函数。WordPress执行某项操作时,如,显示页脚,它就会让插件来执行自己的代码并且要在确切的时间运行。
为了方便大家理解,我们以my_plugin这个普通插件为例,当系统显示页脚时,这个插件就会执行mp_footer()函数。因此,显示页脚时,使用名为add_action()这个特殊的函数,我们就会告知WordPress调用mp_footer()函数:

add_action()函数把行为钩子名称作为其第一个参数,同时把必须执行的函数名称作为其第二个参数。我们将把此函数调用添加到插件的主要文件(即包括header信息的文件),通常把它放在需要执行的函数代码正下方(本例中即放在mp_footer()下)。可在WordPress Codex查看所有可用的行为钩子 。

创建插件管理页面

我们要先创建新菜单条目并把其放置在设置菜单中。

WordPress提供了新建菜单可调用的钩子(即admin_menu),因此,这也是我们创建菜单条目的最佳地点。

既然已确认需使用的行为钩子,我们接着就要定义行为钩子运行时要调用的函数了,我们把其称为visitors_query_admin_actions()。代码显示如下:

<?php
function visitors_query_admin_actions() {
}
add_action('admin_menu', 'visitors_query_admin_actions');
?>

正如您所看到的,我们已创建了visitors_query_admin_actions()函数,并用add_action()函数让其与admin_menu行为钩子结合。接下来,我们就要给visitors_query_admin_actions()函数添加一些代码以创建真正的菜单条目了。

WordPress其它许多操作类似,添加新菜单条目也非常简单,只需调用一个函数就可完成!把新菜单条目添加到设置菜单需要使用add_options_page()函数,然后把以下代码添加到visitors_query_admin_actions()函数中。

<?php
function ip_admin() {
}

function visitors_query_admin_actions() {
	add_options_page("Query Visitor From IP", "Query Visitor From IP", 1, "Query Visitor From IP", "ip_admin");
}
add_action('admin_menu', 'visitors_query_admin_actions');
?>

此时刷新管理页面,设置页面下就已包括新建菜单条目了。
QueryVisitorFromIP
WordPress中的每个菜单都使用不同的函数来添加其子菜单条目。例如,如果要给工具添加子菜单条目,我们就应该使用add_management_page()函数,而非设置页面使用的add_options_page()。更多信息,请查看WordPress Codex中的添加管理菜单
重新回到新添加的代码,或许您已注意到了最后一个参数。它即是请求新增菜单条目时,系统将调用的函数,我们也将使用它来创建插件的管理页面。
我们最好把此页面功能放在单独的文件中,并给其命名为query_visitor_import_admin.php

<?php
function ip_admin() {
	include('query_visitor_import_admin.php');
}

// 只有管理员才能使用这个功能,基本安全防护
function is_administrator() {
	if( current_user_can( 'manage_options' ) ) { return True; }
	return False;  // 非管理员
}

function visitors_query_admin_actions() {
	if(is_administrator()) {
		add_options_page("Query Visitor From IP", "Query Visitor From IP", 1, "Query Visitor From IP", "ip_admin");
	}
}

add_action('admin_menu', 'visitors_query_admin_actions');
?>

此时点击设置菜单下的链接,系统将显示空白页面,这是因为query_visitor_import_admin.php文件现在没有任何内容。
接下来我们配置页面中的内容,如下:

<?php
        function ip_query($ipaddress) {
                $q = "select referred from wp_statistics_visitor where ip='$ipaddress';";
                global $wpdb;
                $rows = $wpdb->get_results($q,ARRAY_N);
                $i=0;
                while ($i< count($rows)){
                        $row = $rows[$i];
                        /*echo var_dump($row[0]);*/
                        echo "<br/>";
                        echo $row[0];
                        $i++;
                }
        }
?>
		
<div class="wrap">
	<form name="visitors_query_form" method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
		<p><?php _e("IP Address: " ); ?><input type="text" name="visitors_ip" value="" size="20"><?php _e(" ex: 192.168.1.1" ); ?></p>
		<hr />
		<p class="submit">
			<input type="submit" name="Submit" value="Query" />
		</p>
	</form>
	<hr />
	<p>
		<?php
			$ipaddress = $_POST['visitors_ip'];
			_e("Query $ipaddress :");
		?>
	</p>
	<hr />
	<div>
                <?php
                        $ipaddress = $_POST['visitors_ip'];
                        if(is_administrator()) {
                                ip_query($ipaddress);
                        } else {
                                echo "only administrator can use this function";
                        } 
                 ?>
	</div>	
</div>

如此调整之后,需要禁用一下插件,然后再启用,否则可能不会生效。

代码详解


熟悉HTML和PHP的人都会理解以上代码,但我们这里仍将简单解释一下。

  • 我们首先用wrap类创建一个div,它是一个标准的WordPress类,使新建页面能够和WordPress管理版块的其它页面风格一致。
  • 表单将使用POST方法回发数据。这意味着表单数据将由同一页面接收,这样以来,我们就可添加数据库更新代码到同一文件。
  • 查询数据库的时候使用WordPress自带的全局数据库对象$wpdb来访问数据库。
  • 为了安全,我们只允许管理员权限的人员调用我们的插件功能。

参考链接


从零创建WordPress自定义插件

简易hash函数

unsigned long hash(unsigned char *str)
{
unsigned long hash = 5381;
int c;

while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

return hash;
}

用这个函数hash几张图片,得到了同样的值,应该是遇到了0,改进一下

把while循环改成for循环就可以了,不过需要知道字符串的长度

unsigned long hash(unsigned char *str, int size)
{
unsigned long hash = 5381;
int i;

for (i = 0; i < size; i++)
{
int c = (int)str[i];
hash = hash * 33 + c;
}

return hash;
}

提高WEB页面加载速度

从输入url,到打开页面发生了什么?

引用知乎上一个比较好玩的答案:

你发现快要过年了,于是想给你的女朋友买一件毛衣,你打开了www.taobao.com。这时你的浏览器首先查询DNS服务器,将www.taobao.com转换成ip地址。不过首先你会发现,你在不同的地区或者不同的网络(电信、联通、移动)的情况下,转换后的ip地址很可能是不一样的,这首先涉及到负载均衡的第一步,通过DNS解析域名时将你的访问分配到不同的入口,同时尽可能保证你所访问的入口是所有入口中可能较快的一个(这和后文的CDN不一样)。

你通过这个入口成功的访问了www.taobao.com的实际的入口ip地址。这时你产生了一个PV,即Page View,页面访问。每日每个网站的总PV量是形容一个网站规模的重要指标。淘宝网全网在平日(非促销期间)的PV大概是16-25亿之间。同时作为一个独立的用户,你这次访问淘宝网的所有页面,均算作一个UV(Unique Visitor用户访问)。最近臭名昭著的12306.cn的日PV量最高峰在10亿左右,而UV量却远小于淘宝网十余倍,这其中的原因我相信大家都会知道。

因为同一时刻访问www.taobao.com的人数过于巨大,所以即便是生成淘宝首页页面的服务器,也不可能仅有一台。仅用于生成www.taobao.com首页的服务器就可能有成百上千台,那么你的一次访问时生成页面给你看的任务便会被分配给其中一台服务器完成。这个过程要保证公正、公平、平均(暨这成百上千台服务器每台负担的用户数要差不多),这一很复杂的过程是由几个系统配合完成,其中最关键的便是LVS,Linux Virtual Server,世界上最流行的负载均衡系统之一,正是由目前在淘宝网供职的章文嵩博士开发的。

经过一系列复杂的逻辑运算和数据处理,用于这次给你看的淘宝网首页的HTML内容便生成成功了。对web前端稍微有点常识的童鞋都应该知道,下一步浏览器会去加载页面中用到的css、js、图片等样式、脚本和资源文件。但是可能相对较少的同学才会知道,你的浏览器在同一个域名下并发加载的资源数量是有限制的,例如ie6-7是两个,ie8是6个,chrome各版本不大一样,一般是4-6个。我刚刚看了一下,我访问淘宝网首页需要加载126个资源,那么如此小的并发连接数自然会加载很久。所以前端开发人员往往会将上述这些资源文件分布在好多个域名下,变相的绕过浏览器的这个限制,同时也为下文的CDN工作做准备。

据不可靠消息,在双十一当天高峰,淘宝的访问流量最巅峰达到871GB/S。这个数字意味着需要178万个4mb带宽的家庭宽带才能负担的起,也完全有能力拖垮一个中小城市的全部互联网带宽。那么显然,这些访问流量不可能集中在一起。并且大家都知道,不同地区不同网络(电信、联通等)之间互访会非常缓慢,但是你却发现很少发现淘宝网访问缓慢。这便是CDN,Content Delivery Network,即内容分发网络的作用。淘宝在全国各地建立了数十上百个CDN节点,利用一些手段保证你访问的(这里主要指js、css、图片等)地方是离你最近的CDN节点,这样便保证了大流量分散已经在各地访问的加速。
这便出现了一个问题,那就是假若一个卖家发布了一个新的宝贝,上传了几张新的宝贝图片,那么淘宝网如何保证全国各地的CDN节点中都会同步的存在这几张图片供用户使用呢?这里边就涉及到了大量的内容分发与同步的相关技术。淘宝开发了分布式文件系统TFS(taobao file system)来处理这类问题。

好了,这时你终于加载完了淘宝首页,那么你习惯性的在首页搜索框中输入了'毛衣'二字并敲回车,这时你又产生了一个PV,然后,淘宝网的主搜索系统便开始为你服务了。它首先对你输入的内容基于一个分词库进行的分词操作。众所周知,英文是以词为单位的,词和词之间是靠空格隔开,而中文是以字为单位,句子中所有的字连起来才能描述一个意思。例如,英文句子I am a student,用中文则为:“我是一个学生”。计算机可以很简单通过空格知道student是一个单词,但是不能很容易明白“学”、“生”两个字合起来才表示一个词。把中文的汉字序列切分成有意义的词,就是中文分词,有些人也称为切词。我是一个学生,分词的结果是:我 是 一个 学生。

进行分词之后,还需要根据你输入的搜索词进行你的购物意图分析。用户进行搜索时常常有如下几类意图:(1)浏览型:没有明确的购物对象和意图,边看边买,用户比较随意和感性。Query例如:”2010年10大香水排行”,”2010年流行毛衣”, “zippo有多少种类?”;(2)查询型:有一定的购物意图,体现在对属性的要求上。Query例如:”适合老人用的手机”,”500元 手表”;(3)对比型:已经缩小了购物意图,具体到了某几个产品。Query例如:”诺基亚E71 E63″,”akg k450 px200″;(4)确定型:已经做了基本决定,重点考察某个对象。Query例如:”诺基亚N97″,”IBM T60″。通过对你的购物意图的分析,主搜索会呈现出完全不同的结果来。

之后的数个步骤后,主搜索系统便根据上述以及更多复杂的条件列出了搜索结果,这一切是由一千多台搜索服务器完成。然后你开始逐一点击浏览搜索出的宝贝。你开始查看宝贝详情页面。经常网购的亲们会发现,当你买过了一个宝贝之后,即便是商家多次修改了宝贝详情页,你仍然能够通过‘已买到的宝贝’查看当时的快照。这是为了防止商家对在商品详情中承诺过的东西赖账不认。那么显然,对于每年数十上百亿比交易的商品详情快照进行保存和快速调用不是一个简单的事情。这其中又涉及到数套系统的共同协作,其中较为重要的是Tair,淘宝自行研发的分布式KV存储方案。

然后无论你是否真正进行了交易,你的这些访问行为便忠实的被系统记录下来,用于后续的业务逻辑和数据分析。这些记录中访问日志记录便是最重要的记录之一,但是前边我们得知,这些访问是分布在各个地区很多不同的服务器上的,并且由于用户众多,这些日志记录都非常庞大,达到TB级别非常正常。那么为了快速及时传输同步这些日志数据,淘宝研发了TimeTunnel,用于进行实时的数据传输,交给后端系统进行计算报表等操作。

你的浏览数据、交易数据以及其它很多很多的数据记录均会被保留下来。使得淘宝存储的历史数据轻而易举的便达到了十数甚至更多个PB(1PB=1024 TB=1048576GB)。如此巨大的数据量经过淘宝系统1:120的极限压缩存储在淘宝的数据仓库中。并且通过一个叫做云梯的,由2000多台服务器组成的超大规模数据系统不断的进行分析和挖掘。

从这些数据中淘宝能够知道小到你是谁,你喜欢什么,你的孩子几岁了,你是否在谈恋爱,喜欢玩魔兽世界的人喜欢什么样的饮料等,大到各行各业的零售情况、各类商品的兴衰消亡等等海量的信息。

上文中绝大多数是一些WEB后端技术,那么前端能做什么呢?

1.减少HTTP请求数

什么是HTTP请求数,一个css-link标签,一个script标签,一个img-src,都需要一次HTTP请求。当然css-link、script很多时候是同步的,且block页面首次渲染,img是异步的。

页面加载中,大多数时间用在HTTP请求上,如果能减少HTTP请求数,就能大幅提高性能。

1)从设计入手,更少的元素显示更丰富的页面内容。

2)css,js文件合并/压缩,很多优秀的工具如grunt可以很方便的完成这件事。

3)CSS Sprites技术,即把所有背景图放到一张图片内,使用CSS技术:background-image,background-position来显示图片的不同部分。

<div>
    <span id="image1" class="nav"></span>
    <span id="image2" class="nav"></span>
    <span id="image3" class="nav"></span>
    <span id="image4" class="nav"></span>
    <span id="image5" class="nav"></span>
</div>
.nav {
    width: 50px;
    height: 50px;
    display: inline-block;
    border: 1px solid #000;
    background-image: url('E:/1.png');
}
#image1 {
        background-position: 0 0;
}
#image2 {
        background-position: -95px 0;
}
#image3 {
        background-position: -185px 0;
}
#image4 {
        background-position: -275px 0;
}
#image5 {
        background-position: -366px -3px;
}

但这个只适用于图片元素紧挨在一起,而且定位比较繁琐,很容易出错,且不能指定手工形状。

2.减少DNS查找次数

3.避免跳转

值得一提的一个现象,如真正的url是 http://xxx.xxx.xx/,而我们输入了http://xxx.xxx.xx,这就会造成一次跳转。

4.可缓存的AJAX

5.延迟加载

确定一下页面哪些内容是页面首次渲染时必须加载的,哪些内容稍微延后加载效果会更好?

6.预加载

预加载是浏览器引擎的一种技术,如页面加载,遇到了js脚本,就需要同步的去解析/执行JS。这时候,开一个线程,遍历一下页面中其他需要下载的元素,去下载它们。

业务预加载:业务的预加载是指在空闲的时候(onload之后),可以使用JS脚本去加载那些尚未显示出来的内容,一般是img,也可以是css和js。

7.减少DOM数量

8.根据域名划分页面内容

9.尽量少的iframe

10.使用CDN

11.为文件头指定Expires和Cache-Contorl

对于静态内容:设置文件头过期时间Expires的值为“Never expire”(永不过期)
对于动态内容:使用恰当的Cache-Control文件头来帮助浏览器进行有条件的请求

12.Gzip

13.使用Etag

14.使用flush

15.使用GET来完成AJAX请求

16.css置于顶部

17.不使用css表达式

18.使用外部js和css

19.js置于页面底部

20.减少JS对DOM的访问

21.优化图像

22.不要缩放图像

http://lehsyh.iteye.com/blog/2111265

 

 

Windows 7在dia 0.97.2 中输入中文

Windows 7电脑上装了dia 0.97.2,结果画图时不能输入中文,能把输入法调出来,但是输入的字却消失不见。折腾半天,最后发现十分简单。。。
菜单里选择"输入法"->"简单",就可以了,不能用默认的 "系统(Windows IME)"。
在Ubuntu下却要用 "系统(SCIM)",选 "简单" 的话,连输入法都调不出来。

参考链接


在dia中输入中文

HP ProLiant MicroServer Gen8使用Super GRUB2 Disk从TF卡启动光驱位安装的Debian 8.3

简介


MicroServer Gen8属于HPE(Hewlett Packard Enterprise,惠普企业级产品)而不是HP,MicroServer Gen8的支持页面(如驱动下载)在HPE,官网首页是http://www.hpe.com,不是http://www.hp.com

HPE大约从2015年起变更了服务支持策略,普通驱动可以无限制下载,但是BIOS、SPP更新等可能需要用主机序列号注册、且在主机质保期内方能下载,超期就只能等待别人搬运分享了。

MicroServer Gen8在AHCI模式时,五个SATA和普通主板的功能一样,唯一不同的是MS G8的BIOS不能选择用哪个硬盘启动。
它会尝试从SATA1引导,如果SATA1没有连接硬盘,则尝试从SATA2引导,以此类推。
可是SATA1~4是硬盘笼子;通常人们都是将4个3.5寸仓库盘放到笼子里面;然后通过SATA5连接一个2.5寸硬盘(放置在9.5mm光驱位置)做系统盘。当五个硬盘都连接时,BIOS仅尝试从SATA1引导,结果出现引导失败。

解决方法就是通过安装一个U盘或MicroSD卡,从而间接引导SATA5接口上的系统盘。

网上给出的方法都是引导Windows系统的,而我们安装的如果是Linux系统的话,则无法简单的使用这些方法来引导系统的。
自己探索了一下,通过使用Rufus来使用并且修改Super GRUB2 Disk的方式来启动Debian的方法。

解决方法


1.安装Debian Linux系统

只插上光驱位置上的硬盘,然后安装Debian Linux系统,只有这样,才能正常安装系统,否则会出现无法安装到正确的磁盘上面。

2.开启SATA AHCI模式

通过 Intelligent Provisioning安装根本就找不到 tf卡(在 bios中可以把默认的 Dynamic HP Smart Array改成 SATA AHCI模式后就可以顺利安装了,但这样 raid功能也没了)

sata-mode-in-bios

3.下载必须的软件

下载Super GRUB2 Disk最新的镜像文件,官网为http://www.supergrubdisk.org/,一定要下载hybrid版本。
下载Rufus最新的版本,官网地址为https://rufus.akeo.ie/
也可以在本网站下载我使用的版本Super GRUB2 Diskrufus-3.10

4.安装Super GRUB2 Disk到SD卡

按照下图的步骤处理:

如果使用2.8.x版本,参考下图(注意,如果使用最新的2.0.4版本的Super GRUB2 Disk,只能使用3.11.x版本的Rufus

Rufus-ISO-Select
Rufus 2.8.886

只有如下选项才能保证可以在Windows中可以正常访问修改Super GRUB2 Disk已经安装到SD卡上的内容。

Rufus-ISO-Select-ISOHybrid

如果使用3.10.x版本(已知`Rufus 3.11.1678`写入的数据无法引导系统,只能使用`3.10.x`版本),参考下图

Rufus 3.10.1647

只有如下选项才能保证可以在`Windows`中可以正常访问修改`Super GRUB2 Disk`已经安装到`SD`卡上的内容。

5.增加Gen8的启动配置文件

在刻录好的SD卡的`\boot\grub\`(Super GRUB2 Disk 2.02)或者`\boot\grub\sgd`(Super GRUB2 Disk 2.04)目录下创建一个名为Gen8.cfg的配置文件,内容如下:

# Super Grub Disk - Gen8.cfg

set option_title=$"Gen8"

function run_option {
  echo "Starting Gen8 ..."
  # for uuid
  #search --set=root --fs-uuid 763A-9CB6
  # for file 
  search --set=root --file /boot/grub/grubenv
  # debian 12.5 for file 
  #search --set=root --file /boot/grub/unicode.pf2
  set prefix=(${root})/boot/grub
  normal
}

一般建议是通过指定磁盘的uuid的方法来启动系统,如果能够确定系统磁盘上存在一个唯一的文件,也可以通过简单的指定文件的方法来让GRUB2来搜索的方式找到启动磁盘。

对于 Super GRUB2 Disk 2.02
修改\boot\grub\main.cfg,在
process_main_option "${prefix}/language_select.cfg"
这行代码下面增加
process_enable "${prefix}/Gen8.cfg" rootmenu

对于 Super GRUB2 Disk 2.04
修改\boot\grub\sgd\main.cfg,在
process_main_option "${sg2d_directory}/language_select.cfg"
这行代码下面增加
process_enable "${sg2d_directory}/Gen8.cfg" rootmenu

并且打开被注释掉的set timeout=10项目,让系统自动启动,否则需要手工点击一下回车。
修改后的内容如下:

# Super Grub Disk Main Configuration file
# Copyright (C) 2009,2010,2011,2012,2013,2014,2015  Adrian Gibanel Lopez.
#
# Super Grub Disk is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Super Grub Disk is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Super Grub Disk.  If not, see <http://www.gnu.org/licenses/>.

# Configure gfxterm, but allow it to be disabled by holding shift during boot.
# gfxterm is required to display non-ASCII translations.

loadfont "$prefix/unifont.pf2"
if keystatus --shift; then
  disable_gfxterm=true
  # export disable_gfxterm is needed so that the setting will persist even after
  # a "configfile /boot/grub/main.cfg" (which is what language_select.cfg does after
  # you select a new language)
  export disable_gfxterm

  # The following strings are intentionally not made translateable.
  echo "It has been detected that the shift key was held down. Because of this SG2D"
  echo "will use VGA text mode rather than gfxterm. This will cause display problems"
  echo "when using some non-English translations."
  echo
  echo "Press escape to continue to the main menu"
  sleep --interruptible 9999
fi

if [ "$disable_gfxterm" != true ]; then
  insmod all_video
  gfxmode=640x480
  if terminal_output --append gfxterm
  then
      terminal_output --remove console
  fi
fi

# Export the variables so that they persist when loading a new menu.
export menu_color_normal
export menu_color_highlight
export menu_color_background
export bwcolor

function set_sgd_colors {
  if [ "$bwcolor" = "yes" ]; then
    menu_color_normal=white/black
    menu_color_highlight=black/white
    menu_color_background=black/white
  else
    menu_color_normal=white/brown
    menu_color_highlight=white/blue
    menu_color_background=yellow/cyan
  fi
}

set_sgd_colors

# Set secondary_locale_dir to the directory containing SG2D specific mo files.
# This makes grub aware of translations for SG2D specific strings.
secondary_locale_dir="${prefix}/sgd_locale/"

insmod part_acorn
insmod part_amiga
insmod part_apple
insmod part_bsd
insmod part_gpt
insmod part_msdos
insmod part_sun
insmod part_sunpc

function process_main_option {

  set option_cfg="$1"
  source "${option_cfg}"
  menuentry "${option_title}" "${option_cfg}" {
    set chosen=""
    export chosen
    set sourced_cfgs="${2}"
    export sourced_cfgs
    configfile "${prefix}/processoption.cfg"
  }

}

function process_option {

  set option_cfg="$1"
  source "${option_cfg}"
  menuentry "${finaloption_tab_str}${option_title}" "${option_cfg}" {
    set chosen=""
    export chosen
    set sourced_cfgs="${2}"
    export sourced_cfgs
    configfile "${prefix}/processoption.cfg"
  }

}

function process_enable {

  set option_cfg="$1"
  set forced_prefix="$2"
  if [ "$forced_prefix" = "rootmenu" ]; then
    menu_prefix_str=""
  else
    menu_prefix_str="${finaloption_tab_str}"
  fi
  source "${option_cfg}"
  menuentry "${menu_prefix_str}${option_title}" "${option_cfg}" {
    set chosen=""
    export chosen
    set sourced_cfgs="${2}"
    export sourced_cfgs
    configfile "${prefix}/processenable.cfg"
  }

}

function submenu_title {

  menuentry "${secondoption_prefixtab_str}${chosen}${secondoption_postfixtab_str}" {
    sleep 1s
  }

}

# Timeout for menu
set timeout=10

# Set default boot entry as Entry number 2 (counting from 0)
set default=2

# Init Super Grub2 Disk variables
insmod regexp
regexp -s "sg2d_dev_name" '^\((.*)\).*$' "$prefix"
rmmod regexp
export sg2d_dev_name

# Get the version number for this Super GRUB2 Disk release
source "${prefix}/version.cfg"

# Get design variables
source "${prefix}/design.cfg"

menuentry "               ====---==- Super Grub2 Disk $sgrub_version  -==---==== " {
  # Set pager=1 so ls output doesn't scroll past the top of the screen
  # but restore $pager to its previous value when finished
  set oldpager="${pager}"
  set pager=1

  cat /boot/grub/AUTHORS
  cat /boot/grub/COPYING

  set pager="${oldpager}"
  unset oldpager
  echo $"Press escape to return to the main menu"
  sleep --interruptible 9999
}

process_main_option "${prefix}/language_select.cfg"

process_enable "${prefix}/gen8.cfg" rootmenu

# Everything
menuentry $"Detect and show boot methods" {
  configfile "${prefix}/everything.cfg"
}

process_enable "${prefix}/enableraidlvm.cfg" rootmenu

process_enable "${prefix}/enablenative.cfg" rootmenu

submenu $"Boot manually""${three_dots_str}" {

  submenu_title

  process_option "${prefix}/osdetect.cfg"

  process_option "${prefix}/cfgextract.cfg"

  process_option "${prefix}/cfgdetect.cfg"

  process_option "${prefix}/menulstdetect.cfg"

  process_option "${prefix}/grubdetect.cfg"

  process_option "${prefix}/diskpartchainboot.cfg"

  process_option "${prefix}/autoiso.cfg"

  source "${prefix}/return.cfg"

}

submenu $"Extra GRUB2 functionality""${three_dots_str}" {

  submenu_title

  process_enable "${prefix}/enablelvm.cfg"

  process_enable "${prefix}/enableraid.cfg"

  process_enable "${prefix}/enableencrypted.cfg"

  process_enable "${prefix}/enablenative.cfg"

  process_enable "${prefix}/enableserial.cfg"

  process_enable "${prefix}/searchfloppy.cfg"

  process_enable "${prefix}/searchcdrom.cfg"

  process_enable "${prefix}/searchsgd.cfg"

  source "${prefix}/return.cfg"

}
menuentry $"Print devices/partitions" {
  # Set pager=1 so ls output doesn't scroll past the top of the screen
  # but restore $pager to its previous value when finished
  set oldpager="${pager}"
  set pager=1

  ls -l

  set pager="${oldpager}"
  unset oldpager

  echo $"Press escape to return to the main menu"
  sleep --interruptible 9999
}

menuentry $"Color ON/OFF" {
  if [ "$bwcolor" = 'yes' ]; then
    bwcolor=no
  else
    bwcolor=yes
  fi

  set_sgd_colors
}

submenu $"Exit""${three_dots_str}" {

  submenu_title

  process_option "${prefix}/halt.cfg"
  process_option "${prefix}/reboot.cfg"

  source "${prefix}/return.cfg"
}

# If it exists, source $prefix/sgd_custom.cfg. This follows the same idea as
# grub-mkconfig generated grub.cfg files sourcing $prefix/custom.cfg, though
# it's less needed here since one could add custom code to this file directly
# whereas their distro might automatically overwrite /boot/grub/grub.cfg on
# kernel upgrades. The main motivation for adding this was the vmtest script
# which I use heavily during Super GRUB2 Disk development, but this feature
# might also be useful to others.
if [ -e "$prefix/sgd_custom.cfg" ]; then
  source "$prefix/sgd_custom.cfg"
fi

6.插上SD卡,断电,然后冷重启

7.常见问题

注意:如果安装升级的是`ubuntu 20.04.1`,系统启动的时候,会出现`error: symbol 'grub_calloc' not found`。

如下图:

但是奇怪的是,如果在系统启动的时候选择`Detect and show boot methods`,显示出的任何菜单,都可以正常启动系统,如下:

这个问题是因为`ubuntu 20.04.1`系统使用的是新版本的`grub2`引导系统,启动配置信息需要进行调整,修改我们创建的`\boot\grub\Gen8.cfg`(Super GRUB2 Disk 2.02)或者`\boot\grub\sgd\Gen8.cfg`(Super GRUB2 Disk 2.04),内容调整为如下:

# Super Grub Disk - Gen8.cfg

set option_title=$"Gen8"

function run_option {
  echo "Starting Gen8 ..."
  #for uuid
  #search --set=root --fs-uuid 763A-9CB6
  #for file
  insmod xfs
  search --set=root --file /boot/grub/grubenv
  # debian 12.5 for file 
  #search --set=root --file /boot/grub/unicode.pf2
  configfile /boot/grub/grub.cfg
}

参考链接


  1. 制作HP MicroServer Gen8可用的ESXi 5.x SD/TF卡启动盘
  2. HP ProLiant MicroServer Gen8迷你服务器 汇总贴:降噪、升级、改造
  3. Rescatux & Super Grub2 Disk
  4. Rufus
  5. HP ProLiant MicroServer Gen8 资源:SPP 2016.04、iLO4 2.40
  6. Gen8通过TF卡启动光驱位硬盘的新办法(增减硬盘无需改动)
  7. GRUB
  8. GNU GRUB Manual 2.04
  9. grub boot error : "symbol 'grub_calloc' not found

gitlab: MergeRequest、reset

Merge request

什么是Merge request,多人开发项目时,发起将一个远程分支merge到另一个分支(一般为主分支)的请求。

merge request步骤:

1.如果开发完了某个模块的功能,需要提交到线上。

2.首先,git fetch --all,仓库代码图拉下来,把线上的代码更新后合并到自己的本地分支上。

3.解决冲突

4.再次合并代码,没有问题后,git push origin 本地分支名。这样就会在远程仓库创建一个remotes/origin/本地分支名 的分支。

5.gitlab上,,进入mergeRequest页面,选择newMergeRequest(右上角绿色按钮)。merge

6.选择要merge的source分支,CONTINUE。

7.填写描述,添加reviwer(重要)。

8.等待结果,如果有冲突,需要从头再来。

Reset

bg2014061202

 

git reset 撤销命令

reset有三种模式

git reset --soft HEAD 本地文件不变,撤销掉commit,不撤销index

git reset --hard HEAD 本地文件改为HEAD,所有commit/index都修改掉

git reset HEAD     本地文件不变,撤销掉commit/index

 

如果只想恢复某一个文件,只需要

git checkout filename

恢复所有文件到当前HEAD

git checkout .

 

Ubuntu 14.04系统Apache 2.4.7版本使用mod_headers过滤HTTP响应头中的WP-Super-Cache字段

服务器上面安装了WP-Super-Cache后,服务器的响应报文中会自动增加一个WP-Super-Cache字段,这个字段会暴露服务器的一些细节,而WP-Super-Cache的设置中又没有找到去掉这个字段的设置选项。

如下图:

WP-Super-Cache-Http-Response

比较简单的解决方法就是,使用Apache2自带的mod_headers模块,通过修改.htacess配置文件的方式来去掉这个响应信息。

1.在网站目录下的.htacess文件中增加如下语句

<IfModule mod_headers.c>
	Header always unset WP-Super-Cache
</IfModule>

2.启用mod_headers模块

$ sudo a2enmod headers

3.重启Apache2服务

$ sudo service apache2 restart