<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    qileilove

    blog已經轉移至github,大家請訪問 http://qaseven.github.io/

    Java Zip文件解壓縮

    為了解壓縮zip都折騰兩天了,查看了許多谷歌百度來的code,
      真實無語了,絕大多數是不能用的。這可能跟我的開發環境有關吧。
      我用的是Ubuntu14.04,eclipse 用的是STS3.5,jdk81.8.0_20
      經過兩天的努力檢驗了無數的code終于讓我找到一個還能用的可以解決中文亂碼問題。
      這個項目用maven構建的依賴jar坐標如下
      <!-- 用于zip文件解壓縮 -->
      <dependency>
      <groupId>ant</groupId>
      <artifactId>ant</artifactId>
      <version>1.7.0</version>
      </dependency>
      代碼如下:
    package com.uujava.mbfy.test;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import org.apache.tools.zip.ZipOutputStream;
    /**
    * @author k
    * @date 2014年10月7日 上午8:04:51
    * @Description: TODO(用于壓縮和解壓縮zip文件)
    * 尚須解決bug:
    * 這里采用硬編碼這定文件編碼,是否有辦法讀取壓縮文件時判斷起內部文件名編碼。
    */
    public final class ZipUtils {
    public static void main(String[] args) throws Exception {
    // ZipFile("/home/k/Documents/testzip/寬屏透明html5產品展示.zip", "/home/k/Documents/testzip/index.html");
    unZipFile("/home/k/Documents/testzip/寬屏透明html5產品展示.zip",
    "/home/k/Documents/testzip/zip");
    }
    public static void zip(ZipOutputStream out, File f, String base,
    boolean first) throws Exception {
    if (first) {
    if (f.isDirectory()) {
    out.putNextEntry(new org.apache.tools.zip.ZipEntry("/"));
    base = base + f.getName();
    first = false;
    } else
    base = f.getName();
    }
    if (f.isDirectory()) {
    File[] fl = f.listFiles();
    base = base + "/";
    for (int i = 0; i < fl.length; i++) {
    zip(out, fl[i], base + fl[i].getName(), first);
    }
    } else {
    out.putNextEntry(new org.apache.tools.zip.ZipEntry(base));
    FileInputStream in = new FileInputStream(f);
    int b;
    System.out.println(base);
    while ((b = in.read()) != -1) {
    out.write(b);
    }
    in.close();
    }
    }
    @SuppressWarnings("unchecked")
    public static void unZipFileByOpache(org.apache.tools.zip.ZipFile zipFile,
    String unZipRoot) throws Exception, IOException {
    java.util.Enumeration e = zipFile.getEntries();
    System.out.println(zipFile.getEncoding());
    org.apache.tools.zip.ZipEntry zipEntry;
    while (e.hasMoreElements()) {
    zipEntry = (org.apache.tools.zip.ZipEntry) e.nextElement();
    InputStream fis = zipFile.getInputStream(zipEntry);
    if (zipEntry.isDirectory()) {
    } else {
    File file = new File(unZipRoot + File.separator
    + zipEntry.getName());
    File parentFile = file.getParentFile();
    parentFile.mkdirs();
    FileOutputStream fos = new FileOutputStream(file);
    byte[] b = new byte[1024];
    int len;
    while ((len = fis.read(b, 0, b.length)) != -1) {
    fos.write(b, 0, len);
    }
    fos.close();
    fis.close();
    }
    }
    }
    public static void ZipFile(String zipFileName, String inputFileName)
    throws Exception {
    org.apache.tools.zip.ZipOutputStream out = new org.apache.tools.zip.ZipOutputStream(
    new FileOutputStream(zipFileName));
    out.setEncoding("gbk");// 設置的和文件名字格式一樣或開發環境編碼設置一樣的話就能正常顯示了
    File inputFile = new File(inputFileName);
    zip(out, inputFile, "", true);
    System.out.println("zip done");
    out.close();
    }
    public static void unZipFile(String unZipFileName, String unZipPath)
    throws Exception {
    org.apache.tools.zip.ZipFile zipFile = new org.apache.tools.zip.ZipFile(
    unZipFileName, "gbk");
    unZipFileByOpache(zipFile, unZipPath);
    System.out.println("unZip Ok");
    }
    }

    posted @ 2014-10-09 10:28 順其自然EVO 閱讀(181) | 評論 (0)編輯 收藏

    關于測試人員的職業發展

     近期由于項目組人手不夠,需要招聘一些測試人員。本周及上周陸陸續續面試了十多個應征者,工作年限在2年~9年之間,但無一滿意。期間,種種感嘆,回想起去年面試六十余人僅有3人滿足要求,如有鯁在喉,還是吐槽一下。如有不對請大家也狂噴我。
      我的要求高么?
      我的要求其實是:有還算不錯的溝通能力,熟悉常見軟件開發流程,有一定的需求分析、用例設計能力,會基本的linux和sql操作能力。有一些代碼能力會加分。這是長期與現實妥協的結果。如果人還算機靈,其實我很愿意花時間來培養他們。
      面試結果
      令人惋惜的是,一個合適的人真的很難找。更令人惋惜的是,我看到好多入行很多年的同行,能力并沒有跟隨工作年限一同增長,有些做了五六年的人有時候給人感覺竟然還不如一個入行一兩年的年輕人。最令人遺憾的是,大部分同學竟然沒有一個明確的職業發展思路,即使有,也沒有經過深入一些的思考,而是人云亦云。
      面試的一些細節:
      因為從事的工作是業務密集型的,有的業務邏輯非常復雜,我們特意準備了一份不錯的需求(考慮到應試者沒有行業背景,給出了詳盡的專業說明和例子),并根據這份需求出了幾道用例設計的題。只有不到四分之一的應試者給出了讓人相對滿意的答案。我們內部評估這份需求的時候,認為只要有過一兩年的用例設計經驗,應該能答的不錯。
      我一般會根據簡歷問一些問題,看看簡歷的真實性。也會問一些基礎的測試知識,查看應試者的專業素質。
      常見的問題:
      說說你常用的測試方法? 百分之九十的人只能答出等價類和邊界值。只有少數人可以講出其它測試用例設計方法,但深入問,從沒有一個人能有令人滿意的回答.
      給一個非常簡單的小例子,例如登陸操作,讓應試者回答如何使用等價類方法設計用例。但讓人吃驚的是仍然只有不到五分之一能夠給出比較滿意的答案。
      陳述一個缺陷的生命周期(你們是怎么管理bug的?)有一多半人能夠說出常見流程,但深入問一些問題:如缺陷如何同版本、測試輪次等結合起來,一些特殊情況如何處理等,很多人就懵了,而這些基本上都是工作中常用的。
      你做的最長的一個項目是什么?在這期間你遇到了什么問題讓你最頭疼?你如何解決它?十個人里大約只有一人能給出還算不錯的答案(能夠識別出問題,提出它帶來 的不利影響是什么,并能夠給出一定的解決方案就算是不錯的答案了)。
      你感興趣的測試工作是什么,你想在哪方面有所發展?十個人里有4個會說是自動化測試,3個會說性能測試,2個會說是管理,一個會說是白盒測試。并希望提供相應培訓。只有極少數人能夠說出具體的思路和技術項。
      如果繼續追問:你說的是性能測試吧?你有過這方面的學習么?一半會說看過一些網站上的技術文章,一半會說看過loadrunner的書。如果繼續追問,是哪本書?是哪類文章?有哪些具體的知識點能講一下么?90%答不上來。
      問:你有看過哪一本測試書籍?哪些技術博客?哪些網站?50%的人會說看過QTP的書(QTP的真正使用率已經快趕上諾基亞的使用率了,國內主流自動化的書竟然還是這個!),并且沒有真正在工作中使用過,然后就沒有別的了。有少一半人最近幾年一本技術書籍也沒有看過。
      如果有管理經驗的應試者,我會問一些測試過程管理相關的問題,如給一個最簡單的題:如果測試時間不夠如何?十個人中只會有兩三個提到排定優先級和測試裁剪,大部分人的回答竟然是加班也一定要搞完。我想說的:
      1.為了你的前途,請多明確一些個人能力思路吧。你五年后,十年后是個什么樣子?有沒有一個明確的想法?有沒有你五年后想達到的某個人的程度?如果這些思路不清楚,請多看看外面的世界,看看一些測試做得非常好的人是如何工作的,他們掌握了什么能力?學習他們,追趕他們并嘗試超越他們。最好認識他們,可以侃侃大山,志同道合抱團前進很好。另外目標別定太抽象,一定要是可以分解,可以檢查的。
      2.多讀一些測試書籍,測試的書并不是只有QTP!看看微軟測試專家史亮推薦的書單,這些都是不錯的好書:http://www.cnblogs.com/liangshi/archive/2011/03/07/1973525.html  有些書能夠幫助你把測試知識框架搭建起來,比照一下你還缺點啥?
      3.多讀一些其它書籍,不限于技術書籍。如果想讀的書有利于工作,推薦一些如何做思辨思維的書。《思考的藝術》《六頂思考帽》《你的燈亮著么》 《學會提問》是我喜歡的4本書。它們會教你怎么獨立思考,養成提問的習慣,而提問的習慣是我們現在的測試人員最缺乏的一件事情。人們往往拿了被測物就開始忙著寫用例,忙著測試。而不是先探索它、研究它。當然IT技術也要掌握,如果你的IT技能能夠趕上開發,你發現你做測試的思路會非常的寬廣:)
      4.把書籍中的東西跟你的工作對比,把好的東西引入工作(這點是檢驗書本質量的好方法,也是促進你思考,促進你能力提高的好方法。
      5.關注大牛們的技術博客。國內寫好測試博客的人不是很多(很多人其實很有水平,但是不喜歡寫blog),但是國外有很多,有人整理了一個list也推薦給大家:http://ssnlove2008.blog.163.com/blog/static/3788942020093284842381/。
      6.搞定你所在行業的領域知識:如常見IT技術,常見業務知識,這些知識掌握的越深,你的價值越高。測試技術是內功,但是你能直接為企業帶來價值的最大之處是你對被測物熟悉程度,也就是你的領域知識!!!
      7.沒有方向?從你的工作入手,比如,你遇到的最大的難題是什么?我怎么解決它?我需要掌握什么樣的技術解決他?我要推動什么樣的組織改變來解決它?別人怎么解決它?有沒有更好的方法?使用后我改進了那些?google一下別人有沒有同樣的問題?嘗試作對比,如果覺得他做得好,嘗試聯系那個人討論一下。看看對方的進展。嘗試把活兒干得特別漂亮。你能解決10個中等問題以后,你的能力會有大幅度提高。
      8.嘗試做筆記。最好是在線的,推薦印象筆記和有道云筆記。
      9.堅持。
      10.保證身體健康,歲月會給你帶來別人的信任感(當然能力要隨著歲數增長)。
      能做到這里面的一半,兩年后你就能在專業上有高分通過我的面試:)當然肯定你也不見得會看得上我們的offer了。
      11.對于沒想好就跳槽,換行業的同學說:你再想想!你的很大價值是與你企業、行業綁定的。如:做了5年保險業務,你的領域知識至少值5w每年,換領域就沒了。你在一家公司證明了你自己,到新公司要重新證明你一遍,有的時候外部環境、機遇等會讓證明過程很痛苦,成本很高。
      另外的吐槽:
      野蠻生長沒有經過系統訓練的同學非常多。這其實有很多因素,分析起來覺得有以下幾點:
      1.大學或者職業教育沒有非常好的課程體系(有些培訓機構還行,但是也需要提高),其實測試技能需要系統訓練和長時間磨練才能有根本的增長,我們的職業教育或者再教育體系其實還是有很大空白的。
      2.說句實話,大家的讀書氛圍不夠濃厚。大家不喜歡看書。而讀書是再教育成本最低,又非常有效的途徑。相比于程序員,測試同學喜歡讀技術書籍的比率明顯的低,這是一個讓人悲傷的事實。真希望這種現象能夠改變。
      3.很多人是不喜歡coding才轉測試,或者是因為IT產業普遍薪水高才來做測試。不是真正熱愛這份工作,不熱愛其實做不好,因為興趣是最好的老師。
      4.很多人認為測試門檻低,young talent 不愿意干,測試吸引人才有點兒困難(我初入行的時候也有這種想法,也是當時被強拉來做測試的,當時想做的是coding和數據DBA相關工作并已經有了一些積累,(我沒說我是啥人才啊))。說實話測試的入門門檻的確有一點點低,但是做好測試的門檻確是相當的高,隨著系統越來越復雜,測試逐漸會比開發還難做,更有挑戰性,我這么說你信么?
      5.專業化社區還沒有形成規模,測試人員沒有能有效交流的平臺。這是跟美國和歐洲的一個挺大的差距。他們的社區做得挺好的,我們也有了一些很好的起步。如一些熱衷測試公益的同學,一些不錯的會議,一些不錯的線下活動,但還需要大大的發揚光大。
      真心希望測試行業的整體水平能夠逐漸提高起來。
      最后看一下測試大牛James Whittaker(Google測試之道 和 探索式軟件測試 的作者)對職業路程發展的一篇文章吧,你會受益很多

    posted @ 2014-10-09 10:27 順其自然EVO 閱讀(206) | 評論 (0)編輯 收藏

    一鍵獲取軟硬件配置及管理員組

    作為公司的IT運維,經常要面對集團各種名頭的稽查,對我們工作量造成相當大的提高。公司的IT政策不允許使用非法軟件、USB口要關閉、電腦使用者不能有管理員權限等等。于是每一個最底層的工作人員一天到晚圍著用戶的電腦跑,查找硬件配置,軟件信息等,為了提高工作效率,于是就寫了以下批處理,減輕自己的工作負擔。
      功能說明:
      1.掃描機器硬件配置
      2.獲取電腦的網絡配置
      3.掃描機器軟件安裝列表
      4.查看Administrators組和Power Users組內的用戶
      5.電腦的USB存儲端口開關情況
      6.電腦的共享信息
      7.掃描結果自動上傳
      掃描的結果以程序畫面顯示(重要內容)及轉出以電腦名稱命名的文本文件(詳細內容)。并將此文本文件自動上傳到共享文件夾中。
      以下是批處理的代碼:
    @echo off
    color 57
    title HardSoft Viewer
    mode con cols=67 lines=42
    setlocal  ENABLEDELAYEDEXPANSION
    echo Prepare For View ...
    del /f "%TEMP%\temp.txt" 2>nul
    dxdiag /t %TEMP%\temp.txt
    del /f "%COMPUTERNAME%.txt" 2>nul
    echo Start Hardware Viewer ...
    echo System Information: >>%COMPUTERNAME%.txt
    :system
    rem This must 30s
    if EXIST "%TEMP%\temp.txt" (
    for /f "tokens=1,2,* delims=:" %%a in ('findstr /c:" Machine name:" /c:" Operating System:" /c:" System Model:" /c:" Processor:" /c:"  Memory:" /c:" Card name:" /c:"Display Memory:" "%TEMP%\temp.txt"') do (
    set /a tee+=1
    if !tee! == 1 echo       Computer Name = %%b>>%COMPUTERNAME%.txt
    if !tee! == 2 echo       OS       Type = %%b>>%COMPUTERNAME%.txt
    if !tee! == 3 echo       System  Model = %%b>>%COMPUTERNAME%.txt
    if !tee! == 4 echo       CPU     Model = %%b>>%COMPUTERNAME%.txt
    if !tee! == 5 echo       RAM      Size = %%b>>%COMPUTERNAME%.txt
    if !tee! == 6 echo.>>%COMPUTERNAME%.txt
    if !tee! == 6 echo DisplayCard : >>%COMPUTERNAME%.txt
    if !tee! == 6 echo       Display  Card = %%b>>%COMPUTERNAME%.txt
    if !tee! == 7 echo       DisplayMemory = %%b>>%COMPUTERNAME%.txt
    )   ) else (
    ping /n 2 127.1>nul
    goto system
    )
    set tee=0
    echo.>>%COMPUTERNAME%.txt
    echo Mother Board:>>%COMPUTERNAME%.txt
    for /f "tokens=1,* delims==" %%a in ('wmic BASEBOARD get Manufacturer^,Product^,Version^,SerialNumber /value') do (
    set /a tee+=1
    if "!tee!" == "3" echo       Manufacturer     = %%b>>%COMPUTERNAME%.txt
    if "!tee!" == "4" echo       MotherBoard Model= %%b>>%COMPUTERNAME%.txt
    )
    set tee=0
    )
    echo.>>%COMPUTERNAME%.txt
    echo Hard Disk: >>%COMPUTERNAME%.txt
    for /f "skip=2 tokens=*" %%a in ('wmic DISKDRIVE get model ^,size /value') do (
    echo.      %%a>>%COMPUTERNAME%.txt
    )
    set tee=0
    echo.>>%COMPUTERNAME%.txt
    echo Network Card:>>%COMPUTERNAME%.txt
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "Description" ^| findstr /v "Microsoft" ^| findstr /v "Tunneling"') do (
    set  name=%%a
    echo      NetCard Model = %%a>>%COMPUTERNAME%.txt
    )
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "Physical Address" ^| findstr /v "00-00-00-00"') do (
    set  name=%%a
    echo      MAC Address = %%a>>%COMPUTERNAME%.txt
    )
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "描述" ^| findstr /v "Microsoft" ^| findstr /v "Tunneling"') do (
    set  name=%%a
    echo      NetCard Model = %%a>>%COMPUTERNAME%.txt
    )
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "物理地址" ^| findstr /v "00-00-00-00"') do (
    set  name=%%a
    echo      MAC Address = %%a>>%COMPUTERNAME%.txt
    )
    ver|find /i "windows xp">nul 2>nul&&goto xp||goto win7
    :xp
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "IP Address"') do (
    set  name=%%a
    echo      IP Address = %%a>>%COMPUTERNAME%.txt
    )
    echo Start Software Viewer For XP...
    echo.>>%COMPUTERNAME%.txt
    echo Software Information:>>%COMPUTERNAME%.txt
    for /f "tokens=7 delims=\" %%i in ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" ^| findstr /v "KB" 2^>nul') do (
    for /f "skip=4 tokens=2*" %%a in ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%%i" /v DisplayName 2^>nul' ) do (
    echo %%b>>%COMPUTERNAME%.txt
    )
    )
    for /f "tokens=2 delims=\" %%x in ('reg query HKU') do (
    for /f "tokens=8 delims=\" %%a in ('reg query "HKU\%%x\Software\Microsoft\Windows\CurrentVersion\Uninstall" 2^>nul') do (
    for /f "skip=4 tokens=2*" %%i in ('reg query "HKU\%%x\Software\Microsoft\Windows\CurrentVersion\Uninstall\%%a"  /v "DisplayName" 2^>nul') do (
    echo %%j>>%COMPUTERNAME%.txt
    )
    )
    )
    echo.>>%COMPUTERNAME%.txt
    if exist %windir%\system32\CCM\CcmExec.exe echo "SMS Client has been installed,please uninstall"
    if exist %windir%\system32\CCM\CcmExec.exe echo "SMS Client has been installed,please uninstall">>%COMPUTERNAME%.txt
    echo ==================================================================
    echo USB Information:
    echo.>>%COMPUTERNAME%.txt
    echo USB Information:>>%COMPUTERNAME%.txt
    for /f "skip=4 tokens=2*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Services\usbstor" /v "start" 2^>nul' ) do (
    if "%%b"=="0x4" echo     USB is Close
    if "%%b"=="0x3" echo     USB is Open,Please Tag It.
    if "%%b"=="0x4" echo     USB is Close>>%COMPUTERNAME%.txt
    if "%%b"=="0x3" echo     USB is Open,Please Tag It.>>%COMPUTERNAME%.txt
    )
    goto last
    :win7
    for /f "tokens=2* delims==:" %%a in ('ipconfig/all^|find /i "IPV4"') do (
    set  name=%%a
    echo      IP Address = %%a>>%COMPUTERNAME%.txt
    )
    echo Start Software Viewer For Win7/8 ...
    rem for 32 win7
    echo.>>%COMPUTERNAME%.txt
    echo Software Information:>>%COMPUTERNAME%.txt
    for /f "tokens=7 delims=\" %%i in ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" ^| findstr /v "KB" 2^>nul ') do (
    for /f "skip=2 tokens=3* delims= " %%a in ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%%i" /v DisplayName 2^>nul') do (
    echo %%a %%b>>%COMPUTERNAME%.txt
    )
    )
    for /f "tokens=8 delims=\" %%i in ('reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" 2^>nul ^| findstr /v "KB" 2^>nul ') do (
    for /f "skip=2 tokens=3* delims= " %%a in ('reg query "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\%%i" /v DisplayName 2^>nul') do (
    echo %%a %%b>>%COMPUTERNAME%.txt
    )
    )
    for /f "tokens=2 delims=\" %%x in ('reg query HKU') do (
    for /f "tokens=8 delims=\" %%a in ('reg query "HKU\%%x\Software\Microsoft\Windows\CurrentVersion\Uninstall" 2^>nul') do (
    for /f "skip=2 tokens=2*" %%i in ('reg query "HKU\%%x\Software\Microsoft\Windows\CurrentVersion\Uninstall\%%a"  /v "DisplayName" 2^>nul') do (
    echo %%j>>%COMPUTERNAME%.txt
    )
    )
    )
    for /f "tokens=2 delims=\" %%x in ('reg query HKU') do (
    for /f "tokens=9 delims=\" %%a in ('reg query "HKU\%%x\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" 2^>nul') do (
    for /f "skip=2 tokens=2*" %%i in ('reg query "HKU\%%x\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\%%a"  /v "DisplayName" 2^>nul') do (
    echo %%j>>%COMPUTERNAME%.txt
    )
    )
    )
    echo ==================================================================
    echo USB Information:
    echo.>>%COMPUTERNAME%.txt
    echo USB Information:>>%COMPUTERNAME%.txt
    for /f "skip=2 tokens=2*" %%a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Services\usbstor" /v "start" 2^>nul' ) do (
    if "%%b"=="0x4" echo     USB is Close
    if "%%b"=="0x3" echo     USB is Open,Please Tag It.
    if "%%b"=="0x4" echo     USB is Close>>%COMPUTERNAME%.txt
    if "%%b"=="0x3" echo     USB is Open,Please Tag It.>>%COMPUTERNAME%.txt
    )
    :last
    echo ==================================================================
    echo Admin Users:
    echo.>>%COMPUTERNAME%.txt
    echo Admin Users:>>%COMPUTERNAME%.txt
    for /f "skip=6 tokens=*" %%i in ('net localgroup Administrators ^| findstr /v "㏑" ^| findstr /v "命" ^| findstr /v "command"') do (
    echo       %%i
    echo       %%i>>%COMPUTERNAME%.txt
    )
    echo Power Users:
    echo.>>%COMPUTERNAME%.txt
    echo Power Users:>>%COMPUTERNAME%.txt
    for /f "skip=6 tokens=*" %%i in ('net localgroup "Power Users" ^| findstr /v "㏑" ^| findstr /v "命" ^| findstr /v "command"') do (
    echo       %%i
    echo       %%i>>%COMPUTERNAME%.txt
    )
    echo ==================================================================
    echo FileShare Information:
    echo.>>%COMPUTERNAME%.txt
    echo FileShare Information:>>%COMPUTERNAME%.txt
    for /f "skip=4 tokens=*" %%i in ('net share 2^>nul ^| findstr /v "㏑" ^| findstr /v "命" ^| findstr /v "command"' ) do (
    echo  %%i
    echo  %%i>>%COMPUTERNAME%.txt
    )
    echo =========================Viewer Over==============================
    net use \\192.168.1.1 password /user:username 1>nul 2>nul
    copy %COMPUTERNAME%.txt \\192.168.1.1\HardFile$\
    net use \\192.168.1.1\IPC$ /del 1>nul 2>nul
    pause
    start %COMPUTERNAME%.txt
     現在來查看下掃描結果XP/WIN8對比
      以及產生的掃描結果
    System Information:
    Computer Name =  C0300022B068
    OS       Type =  Windows 8.1 專業版 64-bit (6.3, Build 9600) (9600.winblue_gdr.131030-1505)
    System  Model =  System Product Name
    CPU     Model =  Pentium(R) Dual-Core  CPU      E5500  @ 2.80GHz (2 CPUs), ~2.8GHz
    RAM      Size =  4096MB RAM
    DisplayCard =
    Display  Card =  Microsoft 基本顯示適配器
    DisplayMemory =  256 MB
    Mother Board:
    Manufacturer     = ASUSTeK Computer INC.
    MotherBoard Model= P5KPL-AM
    Hard Disk:
    Model=ST3500418AS ATA Device
    Size=500038694400
    Network Card:
    NetCard Model =  Realtek PCIe FE Family Controller
    MAC Address =  00-23-54-0A-31-A9
    IP Address =  172.17.44.103(首選)
    Software Information:
    谷歌拼音輸入法 2.7
    7-Zip 9.30 (x64 edition)
    Windows Live MIME IFilter
    Java 8 Update 20 (64-bit)
    Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.4148
    Java SE Development Kit 8 Update 20 (64-bit)
    Microsoft Application Error Reporting
    PDF-Viewer
    Microsoft Visual C++ 2005 Redistributable (x64)
    MSVCRT110_amd64
    VIA 平臺設備管理員
    Mozilla Maintenance Service
    Notepad++
    Windows Live 軟件包
    Windows Live UX Platform
    Windows Live Writer
    Windows Live UX Platform Language Pack
    Junk Mail filter update
    Radmin Viewer 3.5
    Windows Live Photo Common
    Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.4148
    Platform
    Windows Live 軟件包
    Windows Live Writer
    Windows Live Writer
    微軟設備健康助手
    Windows Live Communications Platform
    Java Auto Updater
    Windows Live Mail
    Windows Live Writer Resources
    LibreOffice 4.3.0.4
    Windows Live Installer
    Windows Live Writer
    Windows Live Writer Resources
    Windows Live UX Platform Language Pack
    Windows Live 程式集
    Microsoft Visual C++ 2005 Redistributable
    Photo Common
    MSVCRT
    MSVCRT110
    Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022
    Adobe Reader 8 - Chinese Traditional
    Windows Live PIMT Platform
    Windows Live Mail
    Windows Live Mail
    Windows Live SOXE
    MSVCRT_amd64
    Windows Live SOXE Definitions
    Photo Common
    D3DX10
    Microsoft WSE 3.0 Runtime
    Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218
    Microsoft WSE 2.0 SP3 Runtime
    USB Information:
    USB is Open,Please Tag It.
    Admin Users:
    Administrator
    Luke
    Power Users:
    FileShare Information:
    ADMIN$       C:\Windows                      遠程管理
    C$           C:\                             默認共享
    IPC$                                         遠程 IPC
    D$           D:\                             默認共享
    E$           E:\                             默認共享
    F$           F:\                             默認共享
    HardFile$    D:\HardFile
    cd_rom       D:\cd_rom
    HardSoftViewer
    D:\HardSoftViewer
    HardwareViewer 20140923
    D:\HardwareViewer 20140923
    public       D:\public

    posted @ 2014-10-09 10:24 順其自然EVO 閱讀(352) | 評論 (0)編輯 收藏

    一次關于缺陷狀態的爭論

    在我們的缺陷庫中,有一種缺陷狀態為“待提交”,這是一種最終狀態,并且表明這個缺陷實際上是無效的,不會計入到最終的缺陷報告中。
      一個項目開始了,在幾個版本之后,缺陷庫中存在數個“待提交”狀態的缺陷,原因無外乎幾種:與其它缺陷重復、測試人員的失誤等。
      再來說由此產生的爭論:一個同事認為,“待提交”狀態的缺陷不應當存在于缺陷庫中。從字面上來理解,“待提交”就是提交之前的預狀態,將其修改為有效的缺陷后重新置為“新建”狀態。這樣從缺陷庫來看,不會暴露出測試人員的失誤,可以給上級或其他人一種測試非常高效的印象。
      但我不這么認為:無論是重復的缺陷也好,測試人員的失誤也好,這都是系統測試過程中一種真實的反應和記錄,并且這是不可避免一定會出現的。在測試活動結束之后,可以對所有“待提交”狀態的缺陷做統一的分析,指導后續的測試活動。也就是說“待提交”狀態的缺陷也是一種資源,可以挖掘出有用的信息來。如果人為的使其消失,盡管表面上看來顯示出測試的高效,但是真正了解軟件測試過程的人必然也能發現其中的可疑之處。
      關于爭論的結果,我只能說對方才是這個項目的負責人。

    posted @ 2014-10-09 10:22 順其自然EVO 閱讀(198) | 評論 (0)編輯 收藏

    CentOS安裝搭建BugFree

    BugFree基于PHP和MySQL開發,是免費且開發源代碼的缺陷管理系統。服務器端在LinuxWindows平臺上都可以運行;客戶端無需安裝任何軟件,通過IE,FireFox等瀏覽器就可以自由使用。
      BugFree 2 在BugFree 1.1的基礎上,集成了Test Case和Test Result的管理功能。具體使用流程是:首先創建Test Case(測試用例),運行Test Case產生Test Result(測試結果),運行結果為Failed的Case,可以直接創建Bug。Test Case標題、步驟和Test Result運行環境等信息直接復制到新建的Bug中。
      關閉selinux:
      # vim /etc/selinux/config
      將配置文件中 SELINUX=permissive
      關閉iptables
      # chkconfig --level 35 iptables off
      [root@bugfree ~]# chkconfig --list |grep iptables   查看iptables狀態     0:off 1:off 2:on 3:off 4:on 5:off 6:off
      1. 安裝apache
      yum install httpd
      2. 安裝mysql
      yum install mysql mysql-server
      注:已安裝mysql的跳過此步驟
      3. 安裝PHP
      yum install php php-mysql php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc
      4. 安裝PHP加密算法插件
      yum install libmcrypt
      yum install php-mcrypt
      centos 6.x 默認yum源沒有libmcrypt 相關的包
      從這里下載: http://www.lishiming.net/data/attachment/forum/month_1211/epel-release-6-7.noarch.rpm
      然后再
      yum install -y  libmcrypt-devel   即可解決安裝php加密算法找不到yum源的問題
      注:libmcrypt是加密算法擴展庫,php-mcrypt是Mcrypt對PHP的一個擴展
      5. 安裝bugfree
      bugfree官網已停止對它進行更新,我在百度搜索的一個版本是:bugfree3.0.4
      解壓:unzip bugfree3.zip
      重命名解壓后的文件:mv bugfree3 bugfree
      把bugfree放到apache的DocumentRoot:mv bugfree /var/www/html
      改變bugfree的讀寫權限:chmod -R 777 bugfree
      6. 配置
      1) 配置apache
      vi /etc/httpd/conf/httpd.conf
      修改默認端口號 Listen 80 --> Listen 7999
      啟動httpd服務:service httpd start
     2) 配置mysql
      啟動mysqld服務:service mysqld start
      注:mysqld服務已啟動的跳過此步驟
      登陸mysql:mysql -uroot -p
      創建新用戶:CREATE USER 'bugfree'@'localhost' IDENTIFIED BY '123456';
      新用戶授權:grant all privileges on *.* to bugfree@localhost identified by '123456';
      注:以上授權方式需要把mysql和bugfree安裝在同一臺機器上
      3) 配置bugfree
      瀏覽器訪問http://<servername>:port/bugfree/install
      例如:http://192.168.1.20:7999/bugfree/install
      安裝第一步有個提示/var/www/html/BugFile/  文件不可讀不可寫
      創建BugFile文件夾  mkdir BugFile
      chmod -R 777 BugFile 即可解決
      按照提示配置bugfree關聯的數據庫
      注:要在root權限下操作,即用root登陸或者sudo來操作
      7. 完成安裝,進入BugFree
      初始用戶名: admin 初始密碼:123456
      查看是否已經是開機啟動:chkconfig --list|grep httpd
      [root@localhost ~]# chkconfig --list|grep httpd
      mysql           0:關閉  1:關閉  2:關閉  3:關閉  4:關閉  5:關閉  6:關閉
      0:關機。
      1:單用戶字符界面。
      2:不具備網絡文件系統(NFS)功能的多用戶字符界面。
      3:具有網絡功能的多用戶字符界面。
      4: 保留不用。
      5:具有網絡功能的圖形用戶界面。
      6:重新啟動系統。
      用命令 chkconfig --level 2345 mysqld on (更改相應級別即可)更改httpd隨系統啟動狀態

    posted @ 2014-10-09 10:21 順其自然EVO 閱讀(862) | 評論 (0)編輯 收藏

    VS2010的反匯編功能測試

     F10單步執行前
      F10單步執行后

    posted @ 2014-10-09 10:20 順其自然EVO 閱讀(185) | 評論 (0)編輯 收藏

    如何寫更好的自動化測試用例

    抱歉, 文章的開頭我需要先給這個[自動化測試用例]設一個范圍. 自動化用例的形式有很多, 根據測試對象和測試環境的不同, 有各種script和自動化框架來支持你開發出各式各樣的用例.
      而本文是基于Robot Framework, 一種keyword driven(關鍵字驅動)的自動化測試框架來講的. 如果你是被題目給騙進來的, 那就說明我的第一個目的已經達到了, 哈哈!
      關于更多Robot Framework的信息請google.
      工作中我經常需要去review其他同事的自動化用例(是的,像軟件代碼那樣被review). 我通常從4個方面來審查用例的質量:Readability, Maintainability, Reliability, Performance,
      而這4個方面可以具體到下面這些具體工作:
      Coding Style
      良好的規范可以極大的增強用例的可讀性和可維護性. 當這些用例被轉交給他人時, 也會因為相同的coding style增加對方的接受度.
      當一位"新人"參與用例編寫時我會首先把注意力放到Coding Style上, 因為這也是最容易做到的. 為此我們定義了一些規范:
      a.好的命名規范
      文件名不能包含特殊符號并且遵照特定的格式. 不同作用域的變量采用不同的命名方式, 變量名描述的更有意義, 全局變量要在Variables表中定義..
      還有case的命名、keyword、case setup、teardown.
      b.documentation
      作為用例的補充信息documentation是必不可少的,如果測試用例本身或者背景太過復雜, 我們還可以給suite、test case、keyword分級加注釋.
      c.tags
      給用例打上正確的標簽.標簽的應用非常的廣泛和靈活既可以拿來做用例篩選、版本管理、統計、調度策略,還可以為一些測試策略如[基于風險的測試]提供方案.
      d....
      讓case讀起來像文檔
      在考慮Coding Style時我們可以設置一些固定的規則,大家只要按照這個規則來做,實踐幾次之后Coding Style就會趨于統一. 而考慮將case寫的如同文檔一般則需要更多的主觀能動性.
      為什么需要這樣的mindset? 公司開始推行敏捷之后,測試也在不斷的追求更敏捷的測試.敏捷強調 "快速進入, 不斷迭代", 而文檔在整個開發過程中不可避免的被弱化, 需求設計不斷的更新,
      文檔往往不能被很及時的更新. 那么怎樣可以讓測試人員如何快速的掌握某個功能或者產品的需求和當前狀態呢? 答案是用例.
      a.簡潔明了的documatation
      通過增加[用例注釋]來增強用例的可讀性是一個不錯的辦法.一個suite文件往往包含了一組測試用例,suite level的documation通常可以包含該組測試的背景信息、這組測試的目的、特殊的環境配置說明等.
      要控制documatation的長度,可以通過添加link來擴展更多信息.不要過多的描述測試的詳細內容,用例本身應該對其有很好的描述.也不要因為文檔而文檔,將case名或者重復的信息放在上面.
      case level的documatation我們覺得一般情況下是不需要的, 因為case的結構本身應該足夠清楚的描述.
      b.清晰的用例名
      用例文件名應該能簡單描述文件內所有case的測試目的.我們建立一個目錄來存儲測試點相近的測試用例, 目錄名可以更抽象, 然后我們給這個用例一致的命名規則.
      這樣當在你瀏覽一組用例時,僅僅通過用例名就能大致了解里面的測試內容,也方便你尋找某個case. 應該更多的從用戶角度來思考文件名,對比下面兩個文件名:
      Suite Name1: creation handling after remove unnecessary fatal error.html
      Suite Name2: Avoid DSP restart by removing unnecessary fatal error when timeout happened during call creation.html
      c.條例清晰的case step
      case name
      case step
      keyword
      case的獨立性
      通常一個test suite包含了一組相近的或者有關聯的test case. 而每一個test case應該只測試一種場景,根據case復雜程度的不同場景同樣可大可小,可以是某個功能的測試也可以是端到端的完整測試.(當然也有特殊的寫法比如工作流測試和數據驅動.) case的獨立性又有哪些需要關注的點呢?
      首先一個test suite內的test case在執行時不應該相互影響, 應該將通用的背景部分提取出來放到suite setup中, 允許我隨機的跑某一個case或者亂序的跑這些case. 如果case的步驟有造成環境被破壞的風險,那應該在case teardown中將環境恢復,并且在case setup中做環境監察以及時的終止case. suite level和folder level同樣要注意獨立性的問題,在CRT中通常會將數百數千的case放在一起跑,robot并不會規定case執行的順序所以從某種程度上來說它是隨機的.獨立性還體現在case fail時信息的抓取上,經過一個晚上大批case的執行之后,環境通常已被破壞, 希望通過保留現場來用作case失敗問題定位是不現實的.
      所以每個case都應該準確的收集其開始和結束之間的信息.
      case的可遷移性
      case的可遷移性主要考慮:case對執行環境的依賴,case對外部設備的依賴,case對測試對象的依賴.
      a.避免依賴執行環境,你一直在個人PC上編寫的用例并執行測試,但是不久之后你的用例就會被遷移到組內的測試執行服務器上,之后又被部署到持續集成服務器上,
      中途也有可能被其他同事下載到他的個人PC上來執行測試.所以在編寫用例時我們要避免支持不同平臺的不同庫(windows,linux)和或者不同的腳本命令(CentOS,RedHat,MacOS)
      總之要像Java宣揚的那樣"一處編譯處處運行".
      b.在通信領域為了測試需要或者擴展測試覆蓋,我們會引入一些外部設備如Spirent、Cisco的一些輔助測試設備,各種網絡設備交換機、路由器,還有一些自行開發的模擬設備.
      外部設備會不斷的升級或者更換,在編寫用例時我們就需要考慮如何用一套case更好的兼容這些測試設備. 我有如下幾點建議:
     i.首先將外部設備的操作從測試用例步驟中剝離出去,組織成組級別的庫.
      ii.
      c.對測試對象的依賴,這里我考慮到的是如果測試對象是一個軟件平臺,軟件平臺通常需要適配多種的設備.而設備的硬件配置可能是多種多樣的,CPU、內存、組件的性能和數量都可能不同.
      對測試對象的依賴不僅要考慮在不同設備上的可執行性,重點要考慮測試覆蓋率,由于設備組件的增多你的用例可能無法覆蓋到這些組件,或者捕捉不到某個性能瓶頸,這樣測試結果的可靠性也大打折扣.
      case的可重用性
      自動化用例的開發通常是一項費時的工作,它需要的時間會是手動執行用例的10倍、20倍甚至更多. 我們通過搭建測試框架和封裝資源庫來實現最大范圍的可重用性.
      這里我考慮用例的可重用性包括兩塊:邏輯層的抽象和業務層的重用.
      對一個產品或者功能進行自動化工作時,我們要考慮這些可用性:首先根據測試邏輯的不同對測試用例進行分類,根據邏輯的不同選擇搭建有針對性的case框架,Work Flow, Data Driven等.
      建立公共的庫,將業務的原子操作抽象出來,并且鼓勵其他同事對庫進行補充和調用,避免duplicated庫開發.抽象的API通常需要足夠的原子和靈活才會被大眾所接受. 基于底層API編寫的業務操作也具備可重用性,比方說測試場景(背景資源)的建立、工作流的操作組合、檢查點都可以被復用. 層次分明的抽取時重用性的基礎,提高可重用性可以減少開發時間,也方便日后的維護中的迭代修改.
      case的效率
      不同的case執行時間相距甚遠,短則數秒長則數小時甚至數天,數秒鐘的簡單功能測試用例和穩定性測試耗時數天的用例本身是沒有什么可比性的.但是我當我們放眼某一個或者某一組case時,我們需要重視效率.不論是敏捷還是持續集成都講究快速的反饋,開發人員能在提交代碼后快速的獲得測試結果反饋,測試人員能在最短的時間內執行更大范圍的測試覆蓋,不僅能提高團隊的工作效率也可增強團隊的信心.
      在編寫用例時我們應該注意哪些方面來提高用例的性能?
      對于單一的case我的注意點多放在一些細節上,例如:
      1.執行條件的檢查,如果檢查失敗,則盡快退出執行.
      2.將執行環境搭建或者資源建立和清除 抽取到suite甚至folder level, 抽取時可能需要做一些組合, 但決不允許出現重復的建刪操作.
      3.用例中不允許出現sleep,sleep通常緊接著hard code的時間,不僅效率低還會因為環境的切換使得執行失敗.建議用"wait until ..."來代替.
      4.如有不可避免的sleep,我通常會再三確認其是否清楚它的必要性.
      對于批量的case,我們要如何才能獲得更高的效率呢?
      1.首先我們考慮到可以并行的執行一組case來提高效率,并行方案總有著嚴苛的條件:
      2.為了獲得更快的反饋,我們將軟件質量分為0~10級,對應的把測試用例分為6~10級,從普通的功能測試開始測試復雜度逐級遞增.
      不同的開發階段或者是針對不同的測試目的我們就可以有選擇的調用不同級別的用例.比方說我們調用6級的cases來測試新功能代碼作為冒煙測試的用例集;軟件人員修改了BUG,我可以根據BUG的復雜度選擇7和8級的用例來驗證,系統級測試時我們又會主要測試8和9級的用例.
      分級可以靈活調度用例,并給出更快的反饋,加速迭代過程.
      3.基于風險的測試
      基于風險的測試簡單的說就是根據優先級來選擇需要運行的測試,優先級根據兩個最基本的維度:
      功能點發生錯誤的概率,以及發生錯誤后的嚴重性,根據兩者分值的乘積來排序優先級.
      一般從用例失敗率,bug統計,出錯的代碼段,更新的代碼段來考慮調度.比方說根據BUG修改的代碼段和功能區域來選擇對應的測試.開發人員通常反對這種方式,只有100%的測試覆蓋才能給他們足夠的信心.
      以上是個人的一些積累,由于框架的限制一些建議不一定適用于你的實際工作. 如果你有什么建議歡迎留言. Thx!

    posted @ 2014-10-09 10:16 順其自然EVO 閱讀(756) | 評論 (0)編輯 收藏

    編寫屬于你的第一個Linux內核模塊

     內核編程常常看起來像是黑魔法,而在亞瑟 C 克拉克的眼中,它八成就是了。Linux內核和它的用戶空間是大不相同的:拋開漫不經心,你必須小心翼翼,因為你編程中的一個bug就會影響到整個系統。浮點運算做起來可不容易,堆棧固定而狹小,而你寫的代碼總是異步的,因此你需要想想并發會導致什么。而除了所有這一切之外,Linux內核只是一個很大的、很復雜的C程序,它對每個人開放,任何人都去讀它、學習它并改進它,而你也可以是其中之一。
      學習內核編程的最簡單的方式也許就是寫個內核模塊:一段可以動態加載進內核的代碼。模塊所能做的事是有限的——例如,他們不能在類似進程描述符這樣的公共數據結構中增減字段(LCTT譯注:可能會破壞整個內核及系統的功能)。但是,在其它方面,他們是成熟的內核級的代碼,可以在需要時隨時編譯進內核(這樣就可以摒棄所有的限制了)。完全可以在Linux源代碼樹以外來開發并編譯一個模塊(這并不奇怪,它稱為樹外開發),如果你只是想稍微玩玩,而并不想提交修改以包含到主線內核中去,這樣的方式是很方便的。
      在本教程中,我們將開發一個簡單的內核模塊用以創建一個/dev/reverse設備。寫入該設備的字符串將以相反字序的方式讀回(“Hello World”讀成“World Hello”)。這是一個很受歡迎的程序員面試難題,當你利用自己的能力在內核級別實現這個功能時,可以使你得到一些加分。在開始前,有一句忠告:你的模塊中的一個bug就會導致系統崩潰(雖然可能性不大,但還是有可能的)和數據丟失。在開始前,請確保你已經將重要數據備份,或者,采用一種更好的方式,在虛擬機中進行試驗。
      盡可能不要用root身份
      默認情況下,/dev/reverse只有root可以使用,因此你只能使用sudo來運行你的測試程序。要解決該限制,可以創建一個包含以下內容的/lib/udev/rules.d/99-reverse.rules文件:
      SUBSYSTEM=="misc", KERNEL=="reverse", MODE="0666"
      別忘了重新插入模塊。讓非root用戶訪問設備節點往往不是一個好主意,但是在開發其間卻是十分有用的。這并不是說以root身份運行二進制測試文件也不是個好主意。
      模塊的構造
      由于大多數的Linux內核模塊是用C寫的(除了底層的特定于體系結構的部分),所以推薦你將你的模塊以單一文件形式保存(例如,reverse.c)。我們已經把完整的源代碼放在GitHub上——這里我們將看其中的一些片段。開始時,我們先要包含一些常見的文件頭,并用預定義的宏來描述模塊:
      #include <linux/init.h>
      #include <linux/kernel.h>
      #include <linux/module.h>
      MODULE_LICENSE("GPL");
      MODULE_AUTHOR("Valentine Sinitsyn <valentine.sinitsyn@gmail.com>");
      MODULE_DESCRIPTION("In-kernel phrase reverser");
      這里一切都直接明了,除了MODULE_LICENSE():它不僅僅是一個標記。內核堅定地支持GPL兼容代碼,因此如果你把許可證設置為其它非GPL兼容的(如,“Proprietary”[專利]),某些特定的內核功能將在你的模塊中不可用。
      什么時候不該寫內核模塊
      內核編程很有趣,但是在現實項目中寫(尤其是調試)內核代碼要求特定的技巧。通常來講,在沒有其它方式可以解決你的問題時,你才應該在內核級別解決它。以下情形中,可能你在用戶空間中解決它更好:
      你要開發一個USB驅動 —— 請查看libusb。
      你要開發一個文件系統 —— 試試FUSE。
      你在擴展Netfilter —— 那么libnetfilter_queue對你有所幫助。
      通常,內核里面代碼的性能會更好,但是對于許多項目而言,這點性能丟失并不嚴重。
      由于內核編程總是異步的,沒有一個main()函數來讓Linux順序執行你的模塊。取而代之的是,你要為各種事件提供回調函數,像這個:
      static int __init reverse_init(void)
      {
      printk(KERN_INFO "reverse device has been registered\n");
      return 0;
      }
      static void __exit reverse_exit(void)
      {
      printk(KERN_INFO "reverse device has been unregistered\n");
      }
      module_init(reverse_init);
      module_exit(reverse_exit);
     這里,我們定義的函數被稱為模塊的插入和刪除。只有第一個的插入函數是必要的。目前,它們只是打印消息到內核環緩沖區(可以在用戶空間通過dmesg命令訪問);KERN_INFO是日志級別(注意,沒有逗號)。__init和__exit是屬性 —— 聯結到函數(或者變量)的元數據片。屬性在用戶空間的C代碼中是很罕見的,但是內核中卻很普遍。所有標記為__init的,會在初始化后釋放內存以供重用(還記得那條過去內核的那條“Freeing unused kernel memory…[釋放未使用的內核內存……]”信息嗎?)。__exit表明,當代碼被靜態構建進內核時,該函數可以安全地優化了,不需要清理收尾。最后,module_init()和module_exit()這兩個宏將reverse_init()和reverse_exit()函數設置成為我們模塊的生命周期回調函數。實際的函數名稱并不重要,你可以稱它們為init()和exit(),或者start()和stop(),你想叫什么就叫什么吧。他們都是靜態聲明,你在外部模塊是看不到的。事實上,內核中的任何函數都是不可見的,除非明確地被導出。然而,在內核程序員中,給你的函數加上模塊名前綴是約定俗成的。
      這些都是些基本概念 – 讓我們來做更多有趣的事情吧。模塊可以接收參數,就像這樣:
      # modprobe foo bar=1
      modinfo命令顯示了模塊接受的所有參數,而這些也可以在/sys/module//parameters下作為文件使用。我們的模塊需要一個緩沖區來存儲參數 —— 讓我們把這大小設置為用戶可配置。在MODULE_DESCRIPTION()下添加如下三行:
      static unsigned long buffer_size = 8192;
      module_param(buffer_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
      MODULE_PARM_DESC(buffer_size, "Internal buffer size");
      這兒,我們定義了一個變量來存儲該值,封裝成一個參數,并通過sysfs來讓所有人可讀。這個參數的描述(最后一行)出現在modinfo的輸出中。
      由于用戶可以直接設置buffer_size,我們需要在reverse_init()來清除無效取值。你總該檢查來自內核之外的數據 —— 如果你不這么做,你就是將自己置身于內核異常或安全漏洞之中。
      static int __init reverse_init()
      {
      if (!buffer_size)
      return -1;
      printk(KERN_INFO
      "reverse device has been registered, buffer size is %lu bytes\n",
      buffer_size);
      return 0;
      }
      來自模塊初始化函數的非0返回值意味著模塊執行失敗。
      導航
      但你開發模塊時,Linux內核就是你所需一切的源頭。然而,它相當大,你可能在查找你所要的內容時會有困難。幸運的是,在龐大的代碼庫面前,有許多工具使這個過程變得簡單。首先,是Cscope —— 在終端中運行的一個比較經典的工具。你所要做的,就是在內核源代碼的頂級目錄中運行make cscope && cscope。Cscope和Vim以及Emacs整合得很好,因此你可以在你最喜愛的編輯器中使用它。
      如果基于終端的工具不是你的最愛,那么就訪問http://lxr.free-electrons.com吧。它是一個基于web的內核導航工具,即使它的功能沒有Cscope來得多(例如,你不能方便地找到函數的用法),但它仍然提供了足夠多的快速查詢功能。
      現在是時候來編譯模塊了。你需要你正在運行的內核版本頭文件(linux-headers,或者等同的軟件包)和build-essential(或者類似的包)。接下來,該創建一個標準的Makefile模板:
      obj-m += reverse.o
      all:
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
      clean:
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
      現在,調用make來構建你的第一個模塊。如果你輸入的都正確,在當前目錄內會找到reverse.ko文件。使用sudo insmod reverse.ko插入內核模塊,然后運行如下命令:
      $ dmesg | tail -1
      [ 5905.042081] reverse device has been registered, buffer size is 8192 bytes
      恭喜了!然而,目前這一行還只是假象而已 —— 還沒有設備節點呢。讓我們來搞定它。
      混雜設備
      在Linux中,有一種特殊的字符設備類型,叫做“混雜設備”(或者簡稱為“misc”)。它是專為單一接入點的小型設備驅動而設計的,而這正是我們所需要的。所有混雜設備共享同一個主設備號(10),因此一個驅動(drivers/char/misc.c)就可以查看它們所有設備了,而這些設備用次設備號來區分。從其他意義來說,它們只是普通字符設備。
      要為該設備注冊一個次設備號(以及一個接入點),你需要聲明struct misc_device,填上所有字段(注意語法),然后使用指向該結構的指針作為參數來調用misc_register()。為此,你也需要包含linux/miscdevice.h頭文件:
      static struct miscdevice reverse_misc_device = {
      .minor = MISC_DYNAMIC_MINOR,
      .name = "reverse",
      .fops = &reverse_fops
      };
      static int __init reverse_init()
      {
      ...
      misc_register(&reverse_misc_device);
      printk(KERN_INFO ...
      }
      這兒,我們為名為“reverse”的設備請求一個第一個可用的(動態的)次設備號;省略號表明我們之前已經見過的省略的代碼。別忘了在模塊卸下后注銷掉該設備。
      static void __exit reverse_exit(void)
      {
      misc_deregister(&reverse_misc_device);
      ...
      }
      ‘fops’字段存儲了一個指針,指向一個file_operations結構(在Linux/fs.h中聲明),而這正是我們模塊的接入點。reverse_fops定義如下:
      static struct file_operations reverse_fops = {
      .owner = THIS_MODULE,
      .open = reverse_open,
      ...
      .llseek = noop_llseek
      };
      另外,reverse_fops包含了一系列回調函數(也稱之為方法),當用戶空間代碼打開一個設備,讀寫或者關閉文件描述符時,就會執行。如果你要忽略這些回調,可以指定一個明確的回調函數來替代。這就是為什么我們將llseek設置為noop_llseek(),(顧名思義)它什么都不干。這個默認實現改變了一個文件指針,而且我們現在并不需要我們的設備可以尋址(這是今天留給你們的家庭作業)。
      關閉和打開
      讓我們來實現該方法。我們將給每個打開的文件描述符分配一個新的緩沖區,并在它關閉時釋放。這實際上并不安全:如果一個用戶空間應用程序泄漏了描述符(也許是故意的),它就會霸占RAM,并導致系統不可用。在現實世界中,你總得考慮到這些可能性。但在本教程中,這種方法不要緊。
      我們需要一個結構函數來描述緩沖區。內核提供了許多常規的數據結構:鏈接列表(雙聯的),哈希表,樹等等之類。不過,緩沖區常常從頭設計。我們將調用我們的“struct buffer”:
      struct buffer {
      char *data, *end, *read_ptr;
      unsigned long size;
      };
      data是該緩沖區存儲的一個指向字符串的指針,而end指向字符串結尾后的第一個字節。read_ptr是read()開始讀取數據的地方。緩沖區的size是為了保證完整性而存儲的 —— 目前,我們還沒有使用該區域。你不能假設使用你結構體的用戶會正確地初始化所有這些東西,所以最好在函數中封裝緩沖區的分配和收回。它們通常命名為buffer_alloc()和buffer_free()。
      static struct buffer buffer_alloc(unsigned long size) { struct buffer *buf; buf = kzalloc(sizeof(buf), GFP_KERNEL); if (unlikely(!buf)) goto out; … out: return buf; }
      內核內存使用kmalloc()來分配,并使用kfree()來釋放;kzalloc()的風格是將內存設置為全零。不同于標準的malloc(),它的內核對應部分收到的標志指定了第二個參數中請求的內存類型。這里,GFP_KERNEL是說我們需要一個普通的內核內存(不是在DMA或高內存區中)以及如果需要的話函數可以睡眠(重新調度進程)。sizeof(*buf)是一種常見的方式,它用來獲取可通過指針訪問的結構體的大小。
      你應該隨時檢查kmalloc()的返回值:訪問NULL指針將導致內核異常。同時也需要注意unlikely()宏的使用。它(及其相對宏likely())被廣泛用于內核中,用于表明條件幾乎總是真的(或假的)。它不會影響到控制流程,但是能幫助現代處理器通過分支預測技術來提升性能。
      最后,注意goto語句。它們常常為認為是邪惡的,但是,Linux內核(以及一些其它系統軟件)采用它們來實施集中式的函數退出。這樣的結果是減少嵌套深度,使代碼更具可讀性,而且非常像更高級語言中的try-catch區塊。
      有了buffer_alloc()和buffer_free(),open和close方法就變得很簡單了。
      static int reverse_open(struct inode *inode, struct file *file)
      {
      int err = 0;
      file->private_data = buffer_alloc(buffer_size);
      ...
      return err;
      }
      struct file是一個標準的內核數據結構,用以存儲打開的文件的信息,如當前文件位置(file->f_pos)、標志(file->f_flags),或者打開模式(file->f_mode)等。另外一個字段file->privatedata用于關聯文件到一些專有數據,它的類型是void *,而且它在文件擁有者以外,對內核不透明。我們將一個緩沖區存儲在那里。
      如果緩沖區分配失敗,我們通過返回否定值(-ENOMEM)來為調用的用戶空間代碼標明。一個C庫中調用的open(2)系統調用(如glibc)將會檢測這個并適當地設置errno 。
      學習如何讀和寫
      “read”和“write”方法是真正完成工作的地方。當數據寫入到緩沖區時,我們放棄之前的內容和反向地存儲該字段,不需要任何臨時存儲。read方法僅僅是從內核緩沖區復制數據到用戶空間。但是如果緩沖區還沒有數據,revers_eread()會做什么呢?在用戶空間中,read()調用會在有可用數據前阻塞它。在內核中,你就必須等待。幸運的是,有一項機制用于處理這種情況,就是‘wait queues’。
      想法很簡單。如果當前進程需要等待某個事件,它的描述符(struct task_struct存儲‘current’信息)被放進非可運行(睡眠中)狀態,并添加到一個隊列中。然后schedule()就被調用來選擇另一個進程運行。生成事件的代碼通過使用隊列將等待進程放回TASK_RUNNING狀態來喚醒它們。調度程序將在以后在某個地方選擇它們之一。Linux有多種非可運行狀態,最值得注意的是TASK_INTERRUPTIBLE(一個可以通過信號中斷的睡眠)和TASK_KILLABLE(一個可被殺死的睡眠中的進程)。所有這些都應該正確處理,并等待隊列為你做這些事。
      一個用以存儲讀取等待隊列頭的天然場所就是結構緩沖區,所以從為它添加wait_queue_headt read\queue字段開始。你也應該包含linux/sched.h頭文件。可以使用DECLARE_WAITQUEUE()宏來靜態聲明一個等待隊列。在我們的情況下,需要動態初始化,因此添加下面這行到buffer_alloc():
      init_waitqueue_head(&buf->read_queue);
      我們等待可用數據;或者等待read_ptr != end條件成立。我們也想要讓等待操作可以被中斷(如,通過Ctrl+C)。因此,“read”方法應該像這樣開始:
    static ssize_t reverse_read(struct file *file, char __user * out,
    size_t size, loff_t * off)
    {
    struct buffer *buf = file->private_data;
    ssize_t result;
    while (buf->read_ptr == buf->end) {
    if (file->f_flags & O_NONBLOCK) {
    result = -EAGAIN;
    goto out;
    }
    if (wait_event_interruptible
    (buf->read_queue, buf->read_ptr != buf->end)) {
    result = -ERESTARTSYS;
    goto out;
    }
    }
    ...
      我們讓它循環,直到有可用數據,如果沒有則使用wait_event_interruptible()(它是一個宏,不是函數,這就是為什么要通過值的方式給隊列傳遞)來等待。好吧,如果wait_event_interruptible()被中斷,它返回一個非0值,這個值代表-ERESTARTSYS。這段代碼意味著系統調用應該重新啟動。file->f_flags檢查以非阻塞模式打開的文件數:如果沒有數據,返回-EAGAIN。
      我們不能使用if()來替代while(),因為可能有許多進程正等待數據。當write方法喚醒它們時,調度程序以不可預知的方式選擇一個來運行,因此,在這段代碼有機會執行的時候,緩沖區可能再次空出。現在,我們需要將數據從buf->data 復制到用戶空間。copy_to_user()內核函數就干了此事:
      size = min(size, (size_t) (buf->end - buf->read_ptr));
      if (copy_to_user(out, buf->read_ptr, size)) {
      result = -EFAULT;
      goto out;
      }
      如果用戶空間指針錯誤,那么調用可能會失敗;如果發生了此事,我們就返回-EFAULT。記住,不要相信任何來自內核外的事物!
      buf->read_ptr += size;
      result = size;
      out:
      return result;
      }
      為了使數據在任意塊可讀,需要進行簡單運算。該方法返回讀入的字節數,或者一個錯誤代碼。
      寫方法更簡短。首先,我們檢查緩沖區是否有足夠的空間,然后我們使用copy_from_userspace()函數來獲取數據。再然后read_ptr和結束指針會被重置,并且反轉存儲緩沖區內容:
      buf->end = buf->data + size;
      buf->read_ptr = buf->data;
      if (buf->end > buf->data)
      reverse_phrase(buf->data, buf->end - 1);
      這里, reverse_phrase()干了所有吃力的工作。它依賴于reverse_word()函數,該函數相當簡短并且標記為內聯。這是另外一個常見的優化;但是,你不能過度使用。因為過多的內聯會導致內核映像徒然增大。
      最后,我們需要喚醒read_queue中等待數據的進程,就跟先前講過的那樣。wake_up_interruptible()就是用來干此事的:
      wake_up_interruptible(&buf->read_queue);
      耶!你現在已經有了一個內核模塊,它至少已經編譯成功了。現在,是時候來測試了。
      調試內核代碼
      或許,內核中最常見的調試方法就是打印。如果你愿意,你可以使用普通的printk() (假定使用KERN_DEBUG日志等級)。然而,那兒還有更好的辦法。如果你正在寫一個設備驅動,這個設備驅動有它自己的“struct device”,可以使用pr_debug()或者dev_dbg():它們支持動態調試(dyndbg)特性,并可以根據需要啟用或者禁用(請查閱Documentation/dynamic-debug-howto.txt)。對于單純的開發消息,使用pr_devel(),除非設置了DEBUG,否則什么都不會做。要為我們的模塊啟用DEBUG,請添加以下行到Makefile中:
      CFLAGS_reverse.o := -DDEBUG
      完了之后,使用dmesg來查看pr_debug()或pr_devel()生成的調試信息。 或者,你可以直接發送調試信息到控制臺。要想這么干,你可以設置console_loglevel內核變量為8或者更大的值(echo 8 /proc/sys/kernel/printk),或者在高日志等級,如KERN_ERR,來臨時打印要查詢的調試信息。很自然,在發布代碼前,你應該移除這樣的調試聲明。
      注意內核消息出現在控制臺,不要在Xterm這樣的終端模擬器窗口中去查看;這也是在內核開發時,建議你不在X環境下進行的原因。
      驚喜,驚喜!
      編譯模塊,然后加載進內核:
      $ make
      $ sudo insmod reverse.ko buffer_size=2048
      $ lsmod
      reverse 2419 0
      $ ls -l /dev/reverse
      crw-rw-rw- 1 root root 10, 58 Feb 22 15:53 /dev/reverse
      一切似乎就位。現在,要測試模塊是否正常工作,我們將寫一段小程序來翻轉它的第一個命令行參數。main()(再三檢查錯誤)可能看上去像這樣:
      int fd = open("/dev/reverse", O_RDWR);
      write(fd, argv[1], strlen(argv[1]));
      read(fd, argv[1], strlen(argv[1]));
      printf("Read: %s\n", argv[1]);
      像這樣運行:
      $ ./test 'A quick brown fox jumped over the lazy dog'
      Read: dog lazy the over jumped fox brown quick A
      它工作正常!玩得更逗一點:試試傳遞單個單詞或者單個字母的短語,空的字符串或者是非英語字符串(如果你有這樣的鍵盤布局設置),以及其它任何東西。
      現在,讓我們讓事情變得更好玩一點。我們將創建兩個進程,它們共享一個文件描述符(及其內核緩沖區)。其中一個會持續寫入字符串到設備,而另一個將讀取這些字符串。在下例中,我們使用了fork(2)系統調用,而pthreads也很好用。我也省略打開和關閉設備的代碼,并在此檢查代碼錯誤(又來了):
      char *phrase = "A quick brown fox jumped over the lazy dog";
      if (fork())
      /* Parent is the writer */
      while (1)
      write(fd, phrase, len);
      else
      /* child is the reader */
      while (1) {
      read(fd, buf, len);
      printf("Read: %s\n", buf);
      }
      你希望這個程序會輸出什么呢?下面就是在我的筆記本上得到的東西:
      Read: dog lazy the over jumped fox brown quick A
      Read: A kcicq brown fox jumped over the lazy dog
      Read: A kciuq nworb xor jumped fox brown quick A
      Read: A kciuq nworb xor jumped fox brown quick A
      ...
      這里發生了什么呢?就像舉行了一場比賽。我們認為read和write是原子操作,或者從頭到尾一次執行一個指令。然而,內核確實無序并發的,隨便就重新調度了reverse_phrase()函數內部某個地方運行著的寫入操作的內核部分。如果在寫入操作結束前就調度了read()操作呢?就會產生數據不完整的狀態。這樣的bug非常難以找到。但是,怎樣來處理這個問題呢?
      基本上,我們需要確保在寫方法返回前沒有read方法能被執行。如果你曾經編寫過一個多線程的應用程序,你可能見過同步原語(鎖),如互斥鎖或者信號。Linux也有這些,但有些細微的差別。內核代碼可以運行進程上下文(用戶空間代碼的“代表”工作,就像我們使用的方法)和終端上下文(例如,一個IRQ處理線程)。如果你已經在進程上下文中和并且你已經得到了所需的鎖,你只需要簡單地睡眠和重試直到成功為止。在中斷上下文時你不能處于休眠狀態,因此代碼會在一個循環中運行直到鎖可用。關聯原語被稱為自旋鎖,但在我們的環境中,一個簡單的互斥鎖 —— 在特定時間內只有唯一一個進程能“占有”的對象 —— 就足夠了。處于性能方面的考慮,現實的代碼可能也會使用讀-寫信號。
      鎖總是保護某些數據(在我們的環境中,是一個“struct buffer”實例),而且也常常會把它們嵌入到它們所保護的結構體中。因此,我們添加一個互斥鎖(‘struct mutex lock’)到“struct buffer”中。我們也必須用mutex_init()來初始化互斥鎖;buffer_alloc是用來處理這件事的好地方。使用互斥鎖的代碼也必須包含linux/mutex.h。
      互斥鎖很像交通信號燈 —— 要是司機不看它和不聽它的,它就沒什么用。因此,在對緩沖區做操作并在操作完成時釋放它之前,我們需要更新reverse_read()和reverse_write()來獲取互斥鎖。讓我們來看看read方法 —— write的工作原理相同:
      static ssize_t reverse_read(struct file *file, char __user * out,
      size_t size, loff_t * off)
      {
      struct buffer *buf = file->private_data;
      ssize_t result;
      if (mutex_lock_interruptible(&buf->lock)) {
      result = -ERESTARTSYS;
      goto out;
      }
      我們在函數一開始就獲取鎖。mutex_lock_interruptible()要么得到互斥鎖然后返回,要么讓進程睡眠,直到有可用的互斥鎖。就像前面一樣,_interruptible后綴意味著睡眠可以由信號來中斷。
      while (buf->read_ptr == buf->end) {
      mutex_unlock(&buf->lock);
      /* ... wait_event_interruptible() here ... */
      if (mutex_lock_interruptible(&buf->lock)) {
      result = -ERESTARTSYS;
      goto out;
      }
      }
      下面是我們的“等待數據”循環。當獲取互斥鎖時,或者發生稱之為“死鎖”的情境時,不應該讓進程睡眠。因此,如果沒有數據,我們釋放互斥鎖并調用wait_event_interruptible()。當它返回時,我們重新獲取互斥鎖并像往常一樣繼續:
      if (copy_to_user(out, buf->read_ptr, size)) {
      result = -EFAULT;
      goto out_unlock;
      }
      ...
      out_unlock:
      mutex_unlock(&buf->lock);
      out:
      return result;
      最后,當函數結束,或者在互斥鎖被獲取過程中發生錯誤時,互斥鎖被解鎖。重新編譯模塊(別忘了重新加載),然后再次進行測試。現在你應該沒發現毀壞的數據了。
      接下來是什么?
      現在你已經嘗試了一次內核黑客。我們剛剛為你揭開了這個話題的外衣,里面還有更多東西供你探索。我們的第一個模塊有意識地寫得簡單一點,在從中學到的概念在更復雜的環境中也一樣。并發、方法表、注冊回調函數、使進程睡眠以及喚醒進程,這些都是內核黑客們耳熟能詳的東西,而現在你已經看過了它們的運作。或許某天,你的內核代碼也將被加入到主線Linux源代碼樹中 —— 如果真這樣,請聯系我們!

    posted @ 2014-10-08 09:23 順其自然EVO 閱讀(202) | 評論 (0)編輯 收藏

    優化臨時表使用,SQL語句性能提升100倍

    【問題現象】
      線上mysql數據庫爆出一個慢查詢,DBA觀察發現,查詢時服務器IO飆升,IO占用率達到100%, 執行時間長達7s左右。
      SQL語句如下:
      SELECT DISTINCT g.*, cp.name AS cp_name, c.name AS category_name, t.name AS type_name FROMgm_game g LEFT JOIN gm_cp cp ON cp.id = g.cp_id AND cp.deleted = 0 LEFT JOIN gm_category c ON c.id = g.category_id AND c.deleted = 0 LEFT JOIN gm_type t ON t.id = g.type_id AND t.deleted = 0 WHERE g.deleted = 0 ORDER BY g.modify_time DESC LIMIT 20 ;
      【問題分析】
      使用explain查看執行計劃,結果如下:
      這條sql語句的問題其實還是比較明顯的:
      查詢了大量數據(包括數據條數、以及g.* ),然后使用臨時表order by,但最終又只返回了20條數據。
      DBA觀察到的IO高,是因為sql語句生成了一個巨大的臨時表,內存放不下,于是全部拷貝到磁盤,導致IO飆升。
      【優化方案】
      優化的總體思路是拆分sql,將排序操作和查詢所有信息的操作分開。
      第一條語句:查詢符合條件的數據,只需要查詢g.id即可
      SELECT DISTINCT g.id FROM gm_game g LEFT JOIN gm_cp cp ON cp.id = g.cp_id AND cp.deleted = 0 LEFT JOIN gm_category c ON c.id = g.category_id AND c.deleted = 0 LEFT JOIN gm_type t ON t.id = g.type_id AND t.deleted = 0 WHERE g.deleted = 0 ORDER BY g.modify_time DESC LIMIT 20 ;
      第二條語句:查詢符合條件的詳細數據,將第一條sql的結果使用in操作拼接到第二條的sql
      SELECT DISTINCT g.*, cp.name AS cp_name,c.name AS category_name,t.name AS type_name FROMgm_game g LEFT JOIN gm_cp cp ON cp.id = g.cp_id AND cp.deleted = 0 LEFT JOIN gm_category c ON c.id = g.category_id AND c.deleted = 0 LEFT JOIN gm_type t ON t.id = g.type_id AND t.deleted = 0 WHERE g.deleted = 0 and g.id in(…………………) ORDER BY g.modify_time DESC ;
      【實測效果】
      在SATA機器上測試,優化前大約需要50s,優化后第一條0.3s,第二條0.1s,優化后執行速度是原來的100倍以上,IO從100%降到不到1%
      在SSD機器上測試,優化前大約需要7s,優化后第一條0.3s,第二條0.1s,優化后執行速度是原來的10倍以上,IO從100%降到不到1%
      可以看出,優化前磁盤io是性能瓶頸,SSD的速度要比SATA明顯要快,優化后磁盤不再是瓶頸,SSD和SATA性能沒有差別。
      【理論分析】
      MySQL在執行SQL查詢時可能會用到臨時表,一般情況下,用到臨時表就意味著性能較低。
      臨時表存儲
      MySQL臨時表分為“內存臨時表”和“磁盤臨時表”,其中內存臨時表使用MySQL的MEMORY存儲引擎,磁盤臨時表使用MySQL的MyISAM存儲引擎;
      一般情況下,MySQL會先創建內存臨時表,但內存臨時表超過配置指定的值后,MySQL會將內存臨時表導出到磁盤臨時表;
      Linux平臺上缺省是/tmp目錄,/tmp目錄小的系統要注意啦。
      使用臨時表的場景
      1)ORDER BY子句和GROUP BY子句不同, 例如:ORDERY BY price GROUP BY name;
      2)在JOIN查詢中,ORDER BY或者GROUP BY使用了不是第一個表的列 例如:SELECT * from TableA, TableB ORDER BY TableA.price GROUP by TableB.name
      3)ORDER BY中使用了DISTINCT關鍵字 ORDERY BY DISTINCT(price)
      4)SELECT語句中指定了SQL_SMALL_RESULT關鍵字 SQL_SMALL_RESULT的意思就是告訴MySQL,結果會很小,請直接使用內存臨時表,不需要使用索引排序 SQL_SMALL_RESULT必須和GROUP BY、DISTINCT或DISTINCTROW一起使用 一般情況下,我們沒有必要使用這個選項,讓MySQL服務器選擇即可。
    直接使用磁盤臨時表的場景
      1)表包含TEXT或者BLOB列;
      2)GROUP BY 或者 DISTINCT 子句中包含長度大于512字節的列;
      3)使用UNION或者UNION ALL時,SELECT子句中包含大于512字節的列;
      臨時表相關配置
      tmp_table_size:指定系統創建的內存臨時表最大大小;
      http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_tmp_table_size
      max_heap_table_size: 指定用戶創建的內存表的最大大小;
      http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_max_heap_table_size
      注意:最終的系統創建的內存臨時表大小是取上述兩個配置值的最小值。
      表的設計原則
      使用臨時表一般都意味著性能比較低,特別是使用磁盤臨時表,性能更慢,因此我們在實際應用中應該盡量避免臨時表的使用。 常見的避免臨時表的方法有:
      1)創建索引:在ORDER BY或者GROUP BY的列上創建索引;
      2)分拆很長的列:一般情況下,TEXT、BLOB,大于512字節的字符串,基本上都是為了顯示信息,而不會用于查詢條件, 因此表設計的時候,應該將這些列獨立到另外一張表。
      SQL優化
      如果表的設計已經確定,修改比較困難,那么也可以通過優化SQL語句來減少臨時表的大小,以提升SQL執行效率。
      常見的優化SQL語句方法如下:
      1)拆分SQL語句
      臨時表主要是用于排序和分組,很多業務都是要求排序后再取出詳細的分頁數據,這種情況下可以將排序和取出詳細數據拆分成不同的SQL,以降低排序或分組時臨時表的大小,提升排序和分組的效率,我們的案例就是采用這種方法。
      2)優化業務,去掉排序分組等操作
      有時候業務其實并不需要排序或分組,僅僅是為了好看或者閱讀方便而進行了排序,例如數據導出、數據查詢等操作,這種情況下去掉排序和分組對業務也沒有多大影響。
      如何判斷使用了臨時表?
      使用explain查看執行計劃,Extra列看到Using temporary就意味著使用了臨時表。

    posted @ 2014-10-08 09:17 順其自然EVO 閱讀(224) | 評論 (0)編輯 收藏

    淺談關于java程序員面試的一些事項

    本篇博文針對的是應屆畢業生以及工作兩三年左右的java程序員。
      為什么要跳槽?
      這是一個很廣義的問題,每個人心中都有一份答案。
      例如:
      公司的待遇不好,
      薪資漲幅不符合預期要求,
      厭倦了出差的荒無天日的繁重工作,
      公司的妹子太少,
      領導太傲嬌,
      同事之間關系太逼格,
      某某同學跳槽到某某公司之后漲到了多少多少錢,
      某某同學的朋友的同事的三姑媽家的大兒子的好基友在某某高就,
      等等辭職理由。
      咱們就不多說了,還是談談怎么應付面試吧。
      以下內容是我在面試中總結的一些經驗,希望這些可以給各位帶來幫助和啟迪。
      簡單的說一下筆試,筆試這個環節是很容易通過的,無非就是幾張試卷,一共也就十幾道題。一般由5至10個選擇題+2至5個論述題+1至2個編程題 組成。
      接過筆試題之后,第一步要平靜心態,第二步要瀏覽所有題目,第三步自然就是答題了~
      答題的時候,要先把自己會的快速的答上來,選擇題自然不多說了,論述題根據自己的理解大致說明一下,多少會給你自己加分的。
      編程題其實也不難,出現幾率最大的是寫一個關于某某設計模式的例子,而設計模式的編碼例子,出現最多的是單例模式、工廠模式和代理模式。
      有時候也會有一些算法的編碼,一般是排序算法的編碼實現。
      還有的筆試題,會有一些程序題,就是看程序,然后自己寫出運行結果,這樣的問題考察的是對java基礎知識的掌握,所以,有堅固的基礎是很重要滴!
      OK,筆試結束之后,下一個環節就是面試了,java程序員的一些面試問題主要有哪些呢?
      我個人認為主要有三方面:
      1. 關于java有關的技術問題
      2. 關于項目經驗的問題
      3. 關于個人對團隊的看法以及個人的職業規劃
      咱們就一條一條來看,大家看完之后找相關資料然后一條一條的應對
      一、技術問題
      Struts1原理和Struts2原理以及區別和聯系,在什么項目中用過,有什么體會。
      spring的原理  aop和ioc機制,如何使用,在哪個項目用到過?有什么體會。
      簡要說明一下StrutsMVC和SpringMVC。
      servlet的原理,生命周期。
      socket 原理以及使用方式
      Linux常用命令,shell編程介紹
      java常用算法
      多線程、線程池、線程鎖等等
      二叉樹、java數據結構
      數據庫mysql、Oracle的優缺點以及使用方法和sql語句,問的多的是如果模擬分頁查詢和多表查詢
      Java垃圾回收機制
      敏捷開發的簡要說明,是否了解
      OOA/OOD/OOP 的含義
      java加密與解密
      java網絡通信、http協議要素
      是否熟悉設計模式?簡要說一下自己所了解或者使用過的開發模式有哪些,在哪些場景中使用。

    posted @ 2014-10-08 09:16 順其自然EVO 閱讀(194) | 評論 (0)編輯 收藏

    僅列出標題
    共394頁: First 上一頁 36 37 38 39 40 41 42 43 44 下一頁 Last 
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    統計

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲AV之男人的天堂| 牛牛在线精品免费视频观看| 亚洲色偷偷偷综合网| 亚洲精品国产手机| 国产jizzjizz视频全部免费| 中文字幕在线成人免费看| 亚洲一卡二卡三卡| 亚洲伊人久久大香线蕉影院| 中文字幕亚洲不卡在线亚瑟| 青青青国产在线观看免费 | 亚洲VA中文字幕无码一二三区| 亚洲精品免费在线| 免费观看无遮挡www的视频| 日本黄网站动漫视频免费| 国产在线观看免费观看不卡| 最新免费jlzzjlzz在线播放| a毛片全部免费播放| 免费视频精品一区二区三区 | 2022免费国产精品福利在线| 一级做受视频免费是看美女| 亚洲综合一区无码精品| 亚洲色大成网站www尤物| 日韩亚洲翔田千里在线| 国产免费人成视频尤勿视频| 永久免费A∨片在线观看| 9420免费高清在线视频| 中国在线观看免费国语版| 国产精品免费视频播放器| 精品久久久久久久久免费影院| 拍拍拍又黄又爽无挡视频免费| 国产gav成人免费播放视频| 亚洲日本中文字幕一区二区三区 | 女人18毛片水最多免费观看| 免费国产人做人视频在线观看| 女人毛片a级大学毛片免费| 日本中文一区二区三区亚洲 | 女人隐私秘视频黄www免费| 84pao国产成视频免费播放| 成年美女黄网站18禁免费| 亚洲精品成a人在线观看| 久久亚洲精品成人|