Shader專題:卡通着色(一)控制顏色的藝術

什麼是 Shader?

關於什麼是 Shader ,各種百科各種教程都有說過,但是今天我們就從一個另一個角度去試着理解什麼是 Shader?

我們先看下 Shade 的英文意思,如下:
v.給…遮擋(光線);把…塗暗

其中 把…塗暗 更貼近我們想要的意思。
所以:Shader 這個單詞從字面上理解,就是把什麼東西塗暗。

再強調一次:Shader 從單詞字面上理解,就是把什麼東西塗暗。
再強調一次:把什麼東西塗暗的就是 Shader,就是着色器。

Shader 把什麼塗暗了?

當然是遊戲世界的各個物體,總所周知:有光明就有黑暗,有光照物體就有明暗對比,同時也會有陰影,而 Shader 之所以叫 Shader 是因為起初的時候,Shader 就是用來給物體增加明暗對比的,有了明暗對比,物體在遊戲世界中就會更加立體,從而畫面會更加真實。

所以 Shader 的作用就是給物體添加明暗對比。

Shader 為什麼叫 Shader

當然以上純屬個人推測。現在 Shader 不止可以給物體添加明暗對比,而且還可以做很多濾鏡效果,也可以做很多性能優化(比如減少包大小、減少圖片內存等)的事情。

也許,一開始給 Shader 起名叫 Shader 的時候,Shader 功能非常有限,僅僅只是給物體添加明暗對比(也就是光照計算),後來由於硬件和軟件的發展, 很多離線渲染(電影 CG)的算法都逐步應用在實時渲染(主要是 遊戲 和3D 仿真等),Shader 能做的事情就越來越多,發展到今天,Shader 主要的功能並不只有光照計算。這樣導致,在概念理解上給很多初學者增加了很多阻礙。

教練有一次聽過一位搞圖形學的朋友說:“我們搞實時渲染的都是那些搞視頻(離線渲染)玩剩的”。

Shader 是着色器

什麼是 Shader,中文叫做着色器,也就是給物體上色的意思,也就是說寫 Shader 就是給物體上色的藝術。而這個上色不只是簡單的色彩填充,而是涵蓋了非常多的技巧(幾何計算、顏色計算、貼圖等)

所以中文的着色器,是一個非常精準的翻譯。

群內的笑笑說了一個比較不錯的說法:Shader 主要是光線數據作用在不同數據的物體上產生不同效果。

Shader 學習的順序

不管是 Shader 還是其它某個科目,都有一些最常用、最簡單的知識點。

而這些知識點很容易學以致用,也就是說,這種知識點,我們學習完了就能馬上落地。

所以,教練要做的就是,把 Shader 中的知識點按照是否常用和是否簡單這兩個維度進行排列篩選,然後把它們一個個整理成案例,這樣童鞋們的學習體驗就會大幅上升。

主題式研究第三個階段

  • 第一個階段:確定主題(關鍵字)
  • 第二個階段:搜索資料、搜索信息(搜集情報)
  • 第三個階段:構建知識體系(畫腦圖、寫大綱)

到此,Shader 這個主題,我們目前已經到了第三個階段,也就是構建知識體系的階段。

當然,這一整篇,都再講,我們要怎麼怎麼做,接下來幹嗎,並沒有學習 Shader 的任何一個知識點。

那麼今天就學習一點 Shader 知識意思一下。

顏色的控制

現有一張貼圖,如下:

用來控制顏色的 shader 代碼如下:

float4 frag (v2f i) : SV_Target
{
    // 圖片上每個像素的顏色值
    float4 color = tex2D(_MainTex, i.uv);
                
    // 返回顏色,表示將改像素的顏色值輸出到屏幕上
    return color;
}

我們只看方法中的代碼,先不要在意一些細節。

雖然,我們沒有 Shader 的語法學習經驗,但是憑我們的 C# 經驗,可以將上述代碼推測個大概來。

首先 float4 是一個類型,可以存儲 4 個 float 數值。而顏色一般都是由 r(red 紅色)、g(green,綠色)、b(blue,藍色)、a(alpha,透明度) 四個值控制。所以 float4 可以存儲一個顏色。

現在,我們把圖片中每個像素顏色重的紅色值設置為 0,圖片結果則如下所示:

代碼如下所示:

float4 frag (v2f i) : SV_Target
{
    // 圖片上每個像素的顏色值
    float4 color = tex2D(_MainTex, i.uv);
                
    color.r = 0;

    // 返回顏色,表示將改像素的顏色值輸出到屏幕上
    return color;
}

我們看到,圖片變成了藍綠色。

小結

Shader 是一門控制顏色的藝術,Shader 的核心也是如此。
在此篇,我們學習了 Shader 的兩個重要知識點:

  1. float4 結構體
  2. 顏色的 rgb 控制

這兩個知識點非常簡單,也非常基礎,但是是非常常用的兩個知識點。

這片文章的內容就這些。

知識地圖

相關下載:

轉載請註明地址:liangxiegame.com

更多內容
QFramework 地址:https://github.com/liangxiegame/QFramework
QQ 交流群:623597263
涼鞋的主頁:https://liangxiegame.com/zhuanlan
關注公眾號:liangxiegame 獲取第一時間更新通知及更多的免費內容。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

東京奧運聖火傳遞福島起點 被查出輻射量異常

摘錄自2019年12月4日自由時報報導

日本東京奧運聖火明年3月底從福島縣的足球國練中心「J-VILLAGE」出發,但日本環境省今(4日)透露,「J-VILLAGE」相鄰的停車場有部分區域空間輻射量較高,當局已要求東京電力公司再次去污。

J-VILLAGE位於福島縣濱通南部,2011年福島核災發生後,由於此處距離福島核一廠僅有20公里,被政府借用為核災事故處理的對應據點,該中心今年4月下旬才全面恢復營運,因具有災區重建的重大象徵意義,所以被選為聖火傳遞的起點。

根據《共同社》報導,環保團體綠色和平組織10月對J-VILLAGE周圍展開調查,發現異常的輻射量,隨後將結果送交環境省。

環境省表示,空間輻射量較高的是與J-VILLAGE相鄰的楢葉町營停車場部分區域,已要求東電對該地區未經鋪設的地面再次去污。東電3日去除了周圍約0.03立方米的土和草,調查放射性物質的種類等。

根據東電的調查,在去污地區1公尺高的位置測得每小時1.79微西弗的輻射量,超過日本政府訂定的0.23微西弗的去污標準。地表輻射量為70.2微西弗。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

澳洲野火持續燃燒 摧毀約四成台灣面積

整理:劉妙慈(環境資訊中心實習編輯)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

奧迪2018年量產首款純電動SUV汽車

奧迪準備2018年在布魯塞爾量產首款純電動SUV汽車。布魯塞爾的奧迪工廠會同時製造汽車和電池,它還要向大眾其它汽車提供電池。

在2015年舉行的法蘭克福汽車展上,奧迪展示了e-tron概念車,它預示著奧迪即將推出量產版本的SUV,汽車取名為“Q6 e-tron”。

Q6 e-tron配有3個電動機,一個位於前軸,兩個放在後面。大型電池組安裝在前後軸之間,位於乘客座位的下方,這樣可以降低重心,提供更好的平衡性。

Q6 e-tron安裝的電池來自韓國LG化學和三星SDI,充電一次可以行駛約500千米。資料是以歐洲測試週期作為標準的,如果用美國環境保護署(EPA)的標準測試里程會短一些。因此,Q6 e-tron充電一次的行駛距離估計為390千米,這個資料更為合理一些。

目前布魯塞爾的奧迪工廠主要負責生產A1,奧迪會將A1生產線轉移到西班牙工廠,讓布魯塞爾負責生產Q6 e-tron。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

FastDFS圖片服務器單機安裝步驟(修訂版)

前面已經講 ,通過此文章可以了解FastDFS組件中單機安裝流程。

單機版架構圖

以下為單機FastDFS安裝步驟

一、環境準備

CentOS 7.X

使用的版本: libfastcommon-1.0.41.tar.gz

使用的版本: fastdfs-6.01.tar.gz

使用的版本:fastdfs-nginx-module-1.21.tar.gz

使用的版本: nginx-1.16.1.tar.gz

二、安裝過程

1、安裝 libfastcommon-1.0.41.tar.gz

tar -zxvf libfastcommon-1.0.41.tar.gz
cd libfastcommon-1.0.41/
./make.sh
./make.sh install

2、安裝 FastDFS

tar -zxvf  fastdfs-6.01.tar.gz
cd fastdfs-6.01/
./make.sh
./make.sh install

準備配置文件

cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf
cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
cd /opt/apps/fastdfs-6.01/conf
cp http.conf mime.types /etc/fdfs/

Tracker Server 配置

vim /etc/fdfs/tracker.conf
修改配置如下:
#tracker server端口號
port=22122
#存儲日誌和數據的根目錄
base_path=/opt/fastdfs/tracker
#HTTP服務端口
http.server_port=80
開放防火牆端口

1、打開跟蹤端口

vim /etc/sysconfig/iptables

2、添加以下端口行:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 22122 -j ACCEPT

3、重啟防火牆

service iptables restart
啟動Tracker
/etc/init.d/fdfs_trackerd start

Storage Server 配置

vim /etc/fdfs/storage.conf
修改配置如下:
#storage server端口號
port=23000
#數據和日誌文件存儲根目錄
base_path=/opt/fastdfs/storage
#第一個存儲目錄
store_path0=/opt/fastdfs/storepath0
#tracker服務器IP和端口
tracker_server=192.168.0.1:22122
#http訪問文件的端口(默認8888,看情況修改,和nginx中保持一致)
http.server_port=8888
開放防火牆端口

1、打開跟蹤端口

vim /etc/sysconfig/iptables

2、添加以下端口行:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 23000 -j ACCEPT

3、重啟防火牆

service iptables restart
啟動Storage
/etc/init.d/fdfs_storaged start
查看集群狀態
 fdfs_monitor /etc/fdfs/storage.conf list

查看狀態是否正常

Storage 1:
id = 6.0.36.243
ip_addr = 6.0.36.243 (anantes-651-1-49-net.w2-0.abo.wanadoo.fr) ACTIVE

Client配置

vim /etc/fdfs/client.conf
修改配置如下:
#
base_path=/opt/apps/fastdfs/client
#tracker服務器IP和端口
tracker_server=192.168.0.1:22122 
上傳一個圖片測試是否能上傳成功
 fdfs_upload_file /etc/fdfs/client.conf test.jpg

test.jpg 是測試本地上傳的圖片,路徑請填寫正確

3、安裝Nginx和 fastdfs-nginx-module

#解壓fastdfs-nginx-module
tar -zxvf fastdfs-nginx-module-1.21.tar.gz
cd fastdfs-nginx-module-1.21/
cp ./src/mod_fastdfs.conf /etc/fdfs
#解壓nginx
tar -zxvf nginx-1.16.1.tar.gz
cd nginx-1.16.1/
#安裝nginx_http_image_filter_module
yum -y install gd-devel
yum -y install zlib zlib-devel openssl openssl--devel pcre pcre-devel
#添加模塊
./configure --add-module=../fastdfs-nginx-module-1.21/src --prefix=/usr/local/nginx --with-http_image_filter_module 
#編譯nginx
make
#安裝nginx
make install

查看是否安裝成功

/usr/local/nginx/sbin/nginx -V

查看插件是否安裝成功

[root@FastDFS nginx-1.16.1]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 
configure arguments: --add-module=../fastdfs-nginx-module-1.21/src --prefix=/usr/local/nginx --with-http_image_filter_module
[root@FastDFS nginx-1.16.1]# 

修改Nginx訪問

vim /etc/fdfs/mod_fastdfs.conf

修改配置如下:

#
connect_timeout=10
#tracker服務器IP和端口
tracker_server=192.168.0.1:22122
#是否啟用group組名
url_have_group_name=true
#
store_path0=/opt/fastdfs/storepath0

修改Nginx配置:

vim /usr/local/nginx/conf/nginx.conf

修改配置如下:

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;

    #access_log  logs/host.access.log  main;

    location / {
        root   html;
        index  index.html index.htm;
    }
    #圖片帶壓縮訪問
    location ~ /group1/M00/(.*)\.(jpg|gif|png)!format=([0-9]+)_([0-9]+) {
        alias  /home/fastdfs/storage/data/;
        ngx_fastdfs_module;
        set $w $3;
        set $h $4;

        rewrite group1/M00(.+)\.(jpg|gif|png)!format=([0-9]+)_([0-9]+)$ group1/M00$1.$2 break;

        image_filter resize $w $h;
        image_filter_buffer 5M;
    }
    #主圖訪問
    location ~ /group([0-9])/M00/(.+)\.?(.+) {
        alias /home/fastdfs/storage/data/;
        ngx_fastdfs_module;
    }
...
}

啟動Nginx

#啟動
/usr/local/nginx/sbin/nginx
#停止
/usr/local/nginx/sbin/nginx -s stop
#重啟
/usr/local/nginx/sbin/nginx -s reload

通過以上配置完成FastDFS的搭建。

測試圖片訪問

圖片訪問示例:

主圖訪問

http://218.2.204.124:30308/group1/M00/00/03/BgAk813IvTCAIxxxAAD44NFKFPc908.png

壓縮圖片 (主圖后加 !format=寬度_高度)訪問

http://218.2.204.124:30308/group1/M00/00/03/BgAk813IvTCAIxxxAAD44NFKFPc908.png!format=400_10

未解決的問題

壓縮圖片使用主圖后?format=寬度_高度

本文由博客一文多發平台 發布!

再次感謝!!! 您已看完全文,歡迎關注微信公眾號猿碼 ,你的支持是我持續更新文章的動力!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

C表達式中的彙編指令

C 表達式中的彙編指令

asm 為 gcc 中的關鍵字,asm 表達式為在 C代碼中嵌套彙編指令,該表達式只是單純的替換出彙編代碼,並不對彙編代碼的含義進行解析。

asm 表達式有兩種形式,第二種 asm-qualifiers 包含了 goto 語句。
第一種形式為常見的用法,AssemblerTemplate 和 OutputOperands 必須存在, 其中 Clobbers 存在需要 InputOperands 也出現。

asm asm-qualifiers ( AssemblerTemplate 
                 : OutputOperands 
                 [ : InputOperands
                 [ : Clobbers ] ])

asm asm-qualifiers ( AssemblerTemplate 
                      : 
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

Qualifiers 的類型

  • volatile, 避免編譯器的優化
  • inline, 內斂限定符,最小的體積
  • goto, 包含跳轉指令

參數

  • AssemblerTemplate
    – 彙編指令模板是包含彙編器指令的文字字符串,編輯器替換引用輸入,編譯器不會解析該指令的含義。
  • OutputOperands
    – 由 AssemblerTemplate 中的指令修改的C變量的逗號分隔列表,允許使用空列表。
  • InputOperands
    – 由 AssemblerTemplate 中的指令讀取的C變量的逗號分隔列表,允許使用空列表。
  • Clobbers
    – 用逗號分隔的寄存器列表或由 AssemblerTemplate 修改的值,不能出現在 OutputOperands 和 InputOperands 中被提及,允許使用空列表。
  • GotoLabels
    – 當使用asm的goto形式時,此部分包含 AssemblerTemplate 中的代碼可能跳轉到的所有C標籤的列表。

AssemblerTemplate

彙編指令由一個字符串給出,多條彙編指令結合在一起使用的時候,中間以 \r\t 隔開,如

asm("inc %0\n\tinc %0" : "=r"(res) : "0"(res));

/APP
# 11 "asm.c" 1
        inc %rax
        inc %rax
# 0 "" 2
/NO_APPs

需要轉義的字符:%, =, {, }, |

故在ATT彙編中,對寄存器進行操作的需要雙 %%, 如 inc %%rax.

OutputOperands

操作數之間用逗號分隔。 每個操作數具有以下格式:

[ [asmSymbolicName] ] constraint (cvariablename)
  • asmSymbolicName
    – 為操作數指定名稱,格式為 %[name]
    c // res = num asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num));
    – 如果未指定名稱使用数字, 從 output 域開始,第一個參數為 %0, 一次類推, 這裏的 res 為 %0, num 為 %1
    c // res = num asm("movq %1, %0" : "=r"(res) : "m"(num));
  • constraint
    – 一個字符串常量,用於指定對操作數的存儲的 , 需要以 “=” 或 “+” 開頭
  • cvariablename
    – 指定一個C左值表達式來保存輸出,通常是一個變量名。 括號是語法的必需部分

第一個參數為增加可讀性使用的,現在我們有代碼如下

int64_t res;
int64_t num = 1;

asm("movq %[num], %[res]" : [res] "=r"(res) : [num] "m"(num));
asm("movq %1, %0" : "=r"(res) : "m"(num));
asm("movq %1, %0" : "=m"(res) : "m"(num));
asm("movq %1, %0" : "=r"(res) : "r"(num));

// 對應的彙編代碼, 只保留asm表達式中的代碼
# 13 "asm.c" 1
        movq -16(%rbp), %rax  // asm-1
 # 0 "" 2
/NO_APP

/APP
 # 15 "asm.c" 1
        movq -16(%rbp), %rax  // asm-2
 # 0 "" 2
/NO_APP

/APP
 # 17 "asm.c" 1
        movq -16(%rbp), -8(%rbp)  // asm-3
 # 0 "" 2
/NO_APP

/APP
 # 19 "asm.c" 1
        movq %rax, %rax  // asm-4
 # 0 "" 2
/NO_APP
  1. 使用名稱替換和数字替換效果一樣,見 asm-1 和 asm-2
  2. 約束的用法,這裏使用比較簡單通用的的兩種情況,r 為通過寄存器尋址操作,m 通過內存尋址操作,所以看到當約束了 r 就對應寄存器的操作。
  3. 結果保存在 res 也就是 cvariablename 中

InputOperands

輸入操作數使C變量和表達式中的值可用於彙編代碼。

[ [asmSymbolicName] ] constraint (cexpression)
  • asmSymbolicName 和輸出列表的用法完全一致
  • constraint 約束不能使用 =+. 可以使用 “0”, 這表明在輸出約束列表中(從零開始)的條目,指定的輸入必須與輸出約束位於同一位置。
int64_t res = 3;
int64_t num = 1;
asm("addq %1, %0" : "=g"(res) : "0"(num));

// 輸入輸出位置相同
        movq    $3, -8(%rbp)
        movq    $1, -16(%rbp)
        movq    -16(%rbp), %rax
/APP
# 32 "asm.c" 1
        addq %rax, %rax
# 0 "" 2
/NO_APP
  • cexpression 可以不為左值,作為彙編表達式的輸入值即可

Clobbers

破壞列表,主要用於指示編譯器生成的彙編指令。

從asm表達式中看到輸出操作數中列出條目的更改編譯器是可以確定的,但內聯彙編代碼可能不僅對輸出進行了修改。 例如,計算可能需要其他寄存器,或者處理器可能會由於特定彙編程序指令而破壞寄存器的值。 為了將這些更改通知編譯器,在Clobber列表中列出這些會產生副作用的條目。 破壞列表條目可以是寄存器名稱,也可以是特殊的破壞列表項(在下面列出)。 每個內容列表條目都是一個字符串常量,用雙引號引起來並用逗號分隔。

  • 寄存器

      ```c
      asm volatile("movc3 %0, %1, %2"
              : /* No outputs. */
              : "r"(from), "r"(to), "g"(count)
              : "%rbx", "%rcx", "%rdx", "memory");
    
      /APP
      # 25 "asm.c" 1
              movc3 %rax, %r8, -72(%rbp)
      # 0 "" 2
      /NO_APP
      ```
    
      可以看到使用到了 rax 寄存器,然後修改程序在 Clobbers 增加 %rax, 結果如下
    
      ```c
      asm volatile("movc3 %0, %1, %2"
              : /* No outputs. */
              : "r"(from), "r"(to), "g"(count)
              : "%rax", "%rbx", "%rcx", "%rdx", "memory");
    
      /APP
      # 25 "asm.c" 1
              movc3 %r8, %r9, -72(%rbp)
      # 0 "" 2
      /NO_APP
      ```
      編譯器在產生的彙編代碼中就未使用 %rax 寄存器了。
  • 特殊破壞列表項
    – “cc”, 表示彙編代碼修改了標誌寄存器
    – “memory”, 為了確保內存中包含正確的值,編譯器可能需要在執行asm之前將特定的寄存器值刷新到內存中

編譯器為了破壞列表項的值受到破壞,當這些條目是寄存器時,不對其進行使用;為特殊參數時,重新刷新得到最新的值。

約束

  • 一些基礎的約束
約束名 說明
whitespace 空白字符被忽略
m 允許使用內存操作數,以及機器通常支持的任何類型的地址
o 允許使用內存操作數,但前提是地址是可偏移的
V 允許使用內存操作數,不可偏移的內存地址,與 “o’互斥
r 允許在通用寄存器中使用的寄存器操作數,其中可以指定寄存器,如 a(%rax), b(%rbx)
i 允許使用立即整數操作數
n 允許使用具有已知數值的立即整數操作數, ‘I’, ‘J’, ‘K’, … ‘P’ 更應該使用 n
F 允許使用浮點立即數
g 允許使用任何寄存器,內存或立即數整數操作數,但非通用寄存器除外
X 允許任何操作數, ‘0’, ‘1’, ‘2’, … ‘9’
p 允許使用有效內存地址的操作數
  • 標識符約束
標識符 說明
= 表示此操作數是由該指令寫入的:先前的值將被丟棄並由新數據替換
+ 表示該操作數由指令讀取和寫入
& 表示(在特定替代方法中)此操作數是早期指令操作數,它是在使用輸入操作數完成指令之前寫入的,故輸入操作數部分不能分配與輸出操作數相同的寄存器
% 表示該操作數與後續操作數的可交換指令

內核示例

  1. x86 的內存屏障指令。
// 避免編譯器的優化,聲明此處內存可能發生破壞
#define barrier() asm volatile("" ::: "memory")
// 在32位的CPU下,lock 指令為鎖總線,加上一條內存操作指令就達到了內存屏障的作用,64位的cpu已經有新增的 *fence 指令可以使用
// mb() 執行一個內存屏障作用的指令,為指定CPU操作;破壞列表聲明 cc memory 指示避免編譯器進行優化
#ifdef CONFIG_X86_32
#define mb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "mfence", \
                                X86_FEATURE_XMM2) ::: "memory", "cc")
#define rmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "lfence", \
                                X86_FEATURE_XMM2) ::: "memory", "cc")
#define wmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "sfence", \
                                X86_FEATURE_XMM2) ::: "memory", "cc")
#else
#define mb()    asm volatile("mfence":::"memory")
#define rmb()   asm volatile("lfence":::"memory")
#define wmb()   asm volatile("sfence" ::: "memory")
#endif
  1. x86 下獲取 current 的值
DECLARE_PER_CPU(struct task_struct *, current_task);

#define this_cpu_read_stable(var)   percpu_stable_op("mov", var)

static __always_inline struct task_struct *get_current(void)
{
        return this_cpu_read_stable(current_task);
}

#define percpu_stable_op(op, var)           \
({                          \
        typeof(var) pfo_ret__;              \
        switch (sizeof(var)) {              \
        case 8:                     \
                asm(op "q "__percpu_arg(P1)",%0"    \
                : "=r" (pfo_ret__)          \
                : "p" (&(var)));            \
                break;                  \
        }                       \
        pfo_ret__;                  \
})

current_task 為一個 struct task_struct 類型的指針,追蹤宏調用,在x86-64 下命中了 case 8: 的彙編代碼, 展開的代碼為

asm("mov" "q ""%%""gs" ":" "%" "P1"",%0" : "=r" (pfo_ret__) : "p" (&(current_task)));
// 變換一下為
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(current_task)));

這行代碼的含義為將 約束輸入部分必須為有效的地址(p約束), 將CPU id(通過段寄存器gs和偏移通過GDT得到,這裏後文分析了)通過寄存器(r約束)賦值給 pfo_ret__.

參考

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

Spring Boot 2.X(十六):應用監控之 Spring Boot Actuator 使用及配置

Actuator 簡介

Actuator 是 Spring Boot 提供的對應用系統的自省和監控功能。通過 Actuator,可以使用數據化的指標去度量應用的運行情況,比如查看服務器的磁盤、內存、CPU等信息,系統的線程、gc、運行狀態等等。

Actuator 通常通過使用 HTTP 和 JMX 來管理和監控應用,大多數情況使用 HTTP 的方式。

Actuator 端點說明

端點 描述
auditevents 獲取當前應用暴露的審計事件信息
beans 獲取應用中所有的 Spring Beans 的完整關係列表
caches 獲取公開可以用的緩存
conditions 獲取自動配置條件信息,記錄哪些自動配置條件通過和沒通過的原因
configprops 獲取所有配置屬性,包括默認配置,显示一個所有 @ConfigurationProperties 的整理列版本
env 獲取所有環境變量
flyway 獲取已應用的所有Flyway數據庫遷移信息,需要一個或多個 Flyway Bean
liquibase 獲取已應用的所有Liquibase數據庫遷移。需要一個或多個 Liquibase Bean
health 獲取應用程序健康指標(運行狀況信息)
httptrace 獲取HTTP跟蹤信息(默認情況下,最近100個HTTP請求-響應交換)。需要 HttpTraceRepository Bean
info 獲取應用程序信息
integrationgraph 显示 Spring Integration 圖。需要依賴 spring-integration-core
loggers 显示和修改應用程序中日誌的配置
logfile 返回日誌文件的內容(如果已設置logging.file.name或logging.file.path屬性)
metrics 獲取系統度量指標信息
mappings 显示所有@RequestMapping路徑的整理列表
scheduledtasks 显示應用程序中的計劃任務
sessions 允許從Spring Session支持的會話存儲中檢索和刪除用戶會話。需要使用Spring Session的基於Servlet的Web應用程序
shutdown 關閉應用,要求endpoints.shutdown.enabled設置為true,默認為 false
threaddump 獲取系統線程轉儲信息
heapdump 返回hprof堆轉儲文件
jolokia 通過HTTP公開JMX bean(當Jolokia在類路徑上時,不適用於WebFlux)。需要依賴 jolokia-core
prometheus 以Prometheus服務器可以抓取的格式公開指標。需要依賴 micrometer-registry-prometheus

Actuator 使用及配置

快速使用

項目依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
</dependencies>

配置文件

management.endpoints.enabled-by-default=true
#啟動所有端點
management.endpoints.web.exposure.include=*
#自定義管理端點路徑
#management.endpoints.web.base-path=/manage

Spring Boot 2.X 中,Actuator 默認只開放 health 和 info 兩個端點。

添加management.endpoints.web.exposure.include=*配置后啟動應用,訪問 http://127.0.0.1:8080/actuator 我們可以看到所有的 Actuator 端點列表。

如果將management.endpoints.enabled-by-default設置為false,則禁用所有端點,如需啟用則如下:

management.endpoints.enabled-by-default=false
management.endpoint.info.enabled=true

禁用的端點將從應用程序上下文中完全刪除。如果只想更改公開端點,使用include和exclude屬性。使用如下:

management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,beans

management.endpoints.web.base-path=/manage 配置表示將 /actuator 路徑重定義為 /manage。

常用端點詳解

health

主要用來檢測應用的運行狀況,是使用最多的一個監控點。監控軟件通常使用該接口實時監測應用運行狀況,在系統出現故障時把報警信息推送給相關人員,如磁盤空間使用情況、數據庫和緩存等的一些健康指標。
默認情況下 health 端點是開放的,訪問 http://127.0.0.1:8080/actuator/health 即可看到應用運行狀態。

{"status":"UP"}

如果需要看到詳細信息,則需要做添加配置:

management.endpoint.health.show-details=always

訪問返回信息如下:

{"status":"UP","details":{"diskSpace":{"status":"UP","details":{"total":180002725888,"free":8687988736,"threshold":10485760}}}}

info

查看應用信息是否在 application.properties 中配置。如我們在項目中配置是:

info.app.name=Spring Boot Actuator Demo
info.app.version=v1.0.0
info.app.description=Spring Boot Actuator Demo

啟動項目,訪問 http://127.0.0.1:8080/actuator/info 返回信息如下:

{"app":{"name":"Spring Boot Actuator Demo","version":"v1.0.0","description":"Spring Boot Actuator Demo"}}

env

通過 env 可以獲取到所有關於當前 Spring Boot 應用程序的運行環境信息,如:操作系統信息(systemProperties)、環境變量信息、JDK 版本及 ClassPath 信息、當前啟用的配置文件(activeProfiles)、propertySources、應用程序配置信息(applicationConfig)等。

可以通過 http://127.0.0.1:8080/actuator/env/{name} ,name表示想要查看的信息,可以獨立显示。

beans

訪問 http://127.0.0.1:8080/actuator/beans 返回部分信息如下:

{
    "contexts": {
        "Spring Boot Actuator Demo": {
            "beans": {
                "endpointCachingOperationInvokerAdvisor": {
                    "aliases": [
                    ],
                    "scope": "singleton",
                    "type": "org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor",
                    "resource": "class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.class]",
                    "dependencies": [
                        "environment"
                    ]
                },
                "defaultServletHandlerMapping": {
                    "aliases": [
                    ],
                    "scope": "singleton",
                    "type": "org.springframework.web.servlet.HandlerMapping",
                    "resource": "class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]",
                    "dependencies": [
                    ]
                },
                ...
            }
        }
    }
}

從返回的信息中我們可以看出主要展示了 bean 的別名、類型、是否單例、類的地址、依賴等信息。

conditions

通過 conditions 可以在應用運行時查看代碼了某個配置在什麼條件下生效,或者某個自動配置為什麼沒有生效。

訪問 http://127.0.0.1:8080/actuator/conditions 返回部分信息如下:

{
    "contexts": {
        "Spring Boot Actuator Demo": {
            "positiveMatches": {
                "SpringBootAdminClientAutoConfiguration": [
                    {
                        "condition": "OnWebApplicationCondition",
                        "message": "@ConditionalOnWebApplication (required) found 'session' scope"
                    },
                    {
                        "condition": "SpringBootAdminClientEnabledCondition",
                        "message": "matched"
                    }
                ],
                "SpringBootAdminClientAutoConfiguration#metadataContributor": [
                    {
                        "condition": "OnBeanCondition",
                        "message": "@ConditionalOnMissingBean (types: de.codecentric.boot.admin.client.registration.metadata.CompositeMetadataContributor; SearchStrategy: all) did not find any beans"
                    }
                ],
                ...
            }
        }
    }
}

loggers

獲取系統的日誌信息。

訪問 http://127.0.0.1:8080/actuator/loggers 返回部分信息如下:

{
    "levels": [
        "OFF",
        "ERROR",
        "WARN",
        "INFO",
        "DEBUG",
        "TRACE"
    ],
    "loggers": {
        "ROOT": {
            "configuredLevel": "INFO",
            "effectiveLevel": "INFO"
        },
        "cn": {
            "configuredLevel": null,
            "effectiveLevel": "INFO"
        },
        "cn.zwqh": {
            "configuredLevel": null,
            "effectiveLevel": "INFO"
        },
        "cn.zwqh.springboot": {
            "configuredLevel": null,
            "effectiveLevel": "INFO"
        },
        ...
    }
}

mappings

查看所有 URL 映射,即所有 @RequestMapping 路徑的整理列表。

訪問 http://127.0.0.1:8080/actuator/mappings 返回部分信息如下:

{
    "contexts": {
        "Spring Boot Actuator Demo": {
            "mappings": {
                "dispatcherServlets": {
                    "dispatcherServlet": [
                        {
                            "handler": "ResourceHttpRequestHandler [class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/], class path resource []]",
                            "predicate": "/**/favicon.ico",
                            "details": null
                        },
                        ...
                    ]
                }
            }
        }
    }
}

heapdump

訪問:http://127.0.0.1:8080/actuator/heapdump會自動生成一個 GZip 壓縮的 Jvm 的堆文件 heapdump,我們可以使用 JDK 自帶的 Jvm 監控工具 VisualVM 打開此文件查看。如圖:

VisualVM下載:https://visualvm.github.io/download.html

threaddump

獲取系統線程的轉儲信息,主要展示了線程名、線程ID、線程的狀態、是否等待鎖資源等信息。在工作中,我們可以通過查看線程的情況來排查相關問題。

訪問 http://127.0.0.1:8080/actuator/threaddump 返回部分信息如下:

{
    "threads": [
        {
            "threadName": "DestroyJavaVM",
            "threadId": 40,
            "blockedTime": -1,
            "blockedCount": 0,
            "waitedTime": -1,
            "waitedCount": 0,
            "lockName": null,
            "lockOwnerId": -1,
            "lockOwnerName": null,
            "inNative": false,
            "suspended": false,
            "threadState": "RUNNABLE",
            "stackTrace": [
            ],
            "lockedMonitors": [
            ],
            "lockedSynchronizers": [
            ],
            "lockInfo": null
        },
        ...
    ]
}

shutdown

開啟可以接口關閉 Spring Boot 應用,要使用這個功能需要做如下配置:

management.endpoint.shutdown.enabled=true

可以通過 post(僅支持 post) 請求訪問 http://127.0.0.1:8080/actuator/shutdown 關閉應用。

metrics

訪問 http://127.0.0.1:8080/actuator/metrics 可以獲取系統度量指標信息項如下:

{
    "names": [
        "jvm.memory.max",
        "jvm.threads.states",
        "jvm.gc.pause",
        "http.server.requests",
        "process.files.max",
        "jvm.gc.memory.promoted",
        "system.load.average.1m",
        "jvm.memory.used",
        "jvm.gc.max.data.size",
        "jvm.memory.committed",
        "system.cpu.count",
        "logback.events",
        "tomcat.global.sent",
        "jvm.buffer.memory.used",
        "tomcat.sessions.created",
        "jvm.threads.daemon",
        "system.cpu.usage",
        "jvm.gc.memory.allocated",
        "tomcat.global.request.max",
        "tomcat.global.request",
        "tomcat.sessions.expired",
        "jvm.threads.live",
        "jvm.threads.peak",
        "tomcat.global.received",
        "process.uptime",
        "tomcat.sessions.rejected",
        "process.cpu.usage",
        "tomcat.threads.config.max",
        "jvm.classes.loaded",
        "jvm.classes.unloaded",
        "tomcat.global.error",
        "tomcat.sessions.active.current",
        "tomcat.sessions.alive.max",
        "jvm.gc.live.data.size",
        "tomcat.threads.current",
        "process.files.open",
        "jvm.buffer.count",
        "jvm.buffer.total.capacity",
        "tomcat.sessions.active.max",
        "tomcat.threads.busy",
        "process.start.time"
    ]
}

對應訪問 names 中的指標,可以查看具體的指標信息。如訪問 http://127.0.0.1:8080/actuator/metrics/jvm.memory.used 返回信息如下:

{
    "name": "jvm.memory.used",
    "description": "The amount of used memory",
    "baseUnit": "bytes",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 1.16828136E8
        }
    ],
    "availableTags": [
        {
            "tag": "area",
            "values": [
                "heap",
                "nonheap"
            ]
        },
        {
            "tag": "id",
            "values": [
                "Compressed Class Space",
                "PS Survivor Space",
                "PS Old Gen",
                "Metaspace",
                "PS Eden Space",
                "Code Cache"
            ]
        }
    ]
}

示例代碼

參考文檔

https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/html/production-ready-features.html

非特殊說明,本文版權歸 所有,轉載請註明出處.

原文標題:Spring Boot 2.X(十六):應用監控之 Spring Boot Actuator 使用及配置

原文地址:

如果文章對您有幫助,請掃碼關注下我的公眾號,文章持續更新中…

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

深入理解計算機系統 — 信息的表示和處理

1. 信息的存儲

    大多數計算機使用 8 位的塊,或者字節,作為最小的尋址內存單位,而非訪問內存中單獨的位,機器級程序將內存視為一個非常大的字節數組,稱為 虛擬內存 ,內存的每個字節都用一個唯一的数字標識,稱為它的 地址 。以 C 語言的指針為例,指針使用時指向某一個存儲塊的首字節的 虛擬地址 ,C 編譯器將指針和其類型信息結合起來,這樣即可以根據指針的類型,生成不同的機器級代碼來訪問存儲在指針所指向位置處的值。每個程序對象可以簡單視為一個字節塊,而程序本身就是一個字節序列。

1.1 十六進製表示法

    一個字節由 8 位組成。用二進製表示即 00000000 ~ 11111111 。十進製表示為 0 ~ 255 。由於兩者表示要麼過於冗餘,要麼轉換不遍,因此通常使用十六進制來表示一個字節。這幾種進制的轉換在此就不多說了。

1.2 字數據大小

    每台計算機都會有一個字長(此處字長非字節長度),指明 指針數據的標稱大小(nominal size),因為虛擬地址是以這樣的一個字來進行編碼的,所以字長決定的最重要的一個系統參數即是虛擬地址空間的最大大小。 對於一個字長為 w 位的機器而言,虛擬地址的範圍為 0 ~ (2 ^w )- 1 ,程序最多訪問 2 ^ w 個字節。以 32 位機器為例,32位字長限制虛擬地址空間為 (2 ^32) -1 ,程序最多訪問 2 ^ 32 個字節,大約為 4 x 10^9 字節,即4 GB ( 根據 2 ^ 10 (1024) 約等於 10 ^ 3 (1000) ,可以得到 2 ^ 32 =  4 * 2^30 = 4 * 10 ^ 9 ) 。64位機器的限制虛擬地址空間為 16 EB。大約為 1.84 x 10 ^9 。

1.3 尋址和字節順序

    對於跨越多個字節的對象,我們必須建立兩個規則:這個對象的地址是什麼以及在內存中如何排列這些字節。在幾乎所有的機器上,多字節對象都被存儲為連續的字節序列,對象的地址為這個字節序列中最小的字節地址。以 int 類型為例,假定int 大小為32 位,有變量 int x = 0x01234567 。若 x 的地址為 0x100 ,則 x 的 4 個字節將被存儲在 0x100 , 0x101 , 0x102, 0x103 的位置,此時 4個字節的值分別為 0x01, 0x23, 0x45, 0x67,那麼在內存中的排列順序有如下兩種情況,

  • 大端法:最高有效字節放在最前面的方式稱為大端法,即將一個数字的最高位字節放在最小的字節地址。
  • 小端法:最低有效字節放在最前面的方式稱為小端法,即將一個数字的最低位字節放在最小的字節地址。

  以上面的 x 為例,x 的最高位字節是 0x01 ,將其放在最小的字節地址即 0x100。x 的最低位字節為 0x67 ,將其放在最小的字節地址 0x100 。即大小端對應高低位字節。對於我們來說,機器的字節順序是完全不可見的,我們大部分情況下也無需關心其字節順序,但是在不同類型的機器之間通過網絡傳遞二進制數據的時候,如小端法機器傳送數據給大端法機器時,接受方接收到的字節序會變成反序,為了避免這種問題的產生,發送方和接收方都需要遵循一個網絡規則,發送方將二進制數據轉換成網絡標準,接收方再將這個網絡標準的字節序轉換成自己的字節序。此外,我們在閱讀機器級代碼的時候,可能會出現如下的情況:

  暫時忽略這條指令的意義,可以看到左邊6個字節分別為 01 05 43 0b 20 00 ,而右邊的指令中的地址為 0x200b43,可以看到從左邊的第三個字節開始,43 0b 20 是右邊指令地址的倒序,因此在閱讀這種機器級代碼的時候,也需要注意字節序的問題。此外還存在一種情況。如下圖所示。

    我們可以看到, show_bytes 這個函數可以打印出 start 指針指向的地址開始的 len 個字節內容,且不受字節序的影響,那麼它是如何做到的呢?在 show_int 函數中,可以看到它將 參數 x 的地址強制類型轉換為了 byte_pointer , 即 unsigned char * 。通過強制類型轉換的 start 指針指向的仍是 x 的最低字節地址,但是其類型改變了,通過其類型編譯器會認為該指針指向的對象大小為 1 個字節,此時將該指針進行 ++ 操作可以得到順延下一個字節的內容,從而得到對應的整個對象的字節序列中每個字節的內容而不受字節序影響。

1.4 字符串

    在C語言中,字符串被編碼為一個以 null (其值為0 )字符結尾的字符數組。每個字符都有某個標準編碼來表示,最常見的則是 ASCII 字符碼。假如我們調用 show_bytes(“12345”, 6),那麼會輸出 31 32 33 34 35 00 。可以看到最後打印出了一個終止符,所以通常 C 字符串的長度為實際字符串長度 + 1。 在C 標準庫中的 strlen 函數可以傳入一個字符串得出其長度,這裏的長度即是實際長度,不包含終止符。

2. 整數表示

    在本章節中,介紹了編碼整數的兩種不同的方式,一種只能表示非負數,另一種則能夠表示負數,正數和零。接下來逐一進行介紹。

2.1 整型數據類型

    C語言中,整數有多種數據類型,如下圖所示,此外可以通過加上 unsigned 符號來限定該數據類型為非負數。這些數據類型有的是根據機器的字長(32位和64位)決定其實際最大值和最小值的範圍。我們可以看到,圖中最小值和最大值的取值範圍是不對稱的,負數的取值範圍比正數大一,當我們考慮如何表現負數時,會看到為什麼會這樣。

    關於無符號整數的編碼,其實與普通的十進制正數轉換成二進制沒有什麼區別,假設字長 w = 32 位,轉換后大於 32 位的数字將被捨去。這裏主要介紹一下關於有符號数字的編碼,通常計算機使用的編碼錶示方式為 補碼 ,在這個表示方式中,將字的最高有效位(即符號位)表示為負權,權重為 – 2^(w-1) ,當 w 位的值為 1 時表示為負數,反之為正數。以 -1 為例,-1 的補碼為1111 1111  …. …. 1111 ,即 -2^31 + 2^30 + … + 2^0 = -1 ,通常我們看到一個負數想要直接將其使用補碼錶示還是有些不方便的,因此我們可以先使用原碼錶示,所謂原碼和普通的十進制數轉二進制數沒有區別,只不過最高位用來表示符號位,然後再求其反碼,即符號位不變,其餘位取反加 1,就可以得到這個負數的補碼了,還是以 -1 舉例, -1 的原碼為 1000 0000 …. 0001 ,其反碼的值為 1111 1111 …. 1111 ,與 -1 的補碼值是相同的。而正數的補碼為其本身,不需要做這種轉換。

    那麼為什麼要使用補碼這種表示方式呢,首先,二進制補碼可以使正負數相加時仍然採用正常加法的邏輯,不需要做特殊的處理,此外,如果不採用補碼錶示,採用原碼的表示方法,那麼會出現幾個問題,正負零的存在,以及提高了減法的計算複雜度,而補碼可以十分簡單的計算正負數相加,只需求出兩者的補碼對其進行加法,更多關於補碼的解釋可以參考  。

PS: 為什麼正負數補碼相加會得到正確的結果,這裏個人的見解是:由於補碼最高位為負權,而正數與負數補碼相加相當於正數去抵消這個負權。比如 -16 的補碼為 1111 …. 1111 0000,加上正數 1,由於正數的補碼為本身,所以等價於 -16 + 1  == (-2^31 + 2^30 + … + 2^4 ) +  2^ 0 ,相當於多了一個 2^0 的正權去抵消其最高位的負權。

2.2 有符號數和無符號數之間的轉換

    C語言允許各種不同的数字類型之間進行強制類型轉換, 如 int x= -1 ; unsigned y = (unsigned) x ; 此時會將 x 的值強制類型轉換成 unsigned 類型然後賦值給 y ,那麼此時 y 的值是多少呢?可以通過打印兩者的十六進制值來看有什麼區別。下面為 test.c 的代碼:

int main()

{

int x = -1;

unsigned y = (unsigned) x;

printf(“%x \n”, x);

printf(“%x \n”, x);

return 0;

}

此處為編譯后可執行文件的輸出結果:

ffffffff
ffffffff

可以看到, x 和 y 的十六進制值是相同的,這也說明了,強制類型轉換並不會改變數據底層的位表示,只是改變了解釋位模式的方式。我們可以利用 printf 的指示符進一步驗證這個結果,使用 %d (有符號十進制), %u (無符號十進制), 來打印 x 和 y 的值。以下是代碼:

int main()

{

int x = 1;

unsigned y = (unsigned) x;

printf(“x format d = %d , format u = %u \n”, x, x);

printf(“y format d = %d , format u = %u \n”, y, y);

return 0;

}

這是編譯后可執行文件的對應輸出:

x format d = -1 , format u = 4294967295
y format d = -1 , format u = 4294967295

 我們可以看到,我們使用指示符控制了解釋這些位的方式,得到的結果是一致的。

2.3 整數運算

 關於整數的運算,主要就是加減乘除四種運算,補碼的加減乘除都比較簡單明了,這裏主要說一下除法的舍入問題,首先,我們先確認下 C 語言中的舍入方式,在 C 語言中,浮點數被賦值給整數時,小數位總是被捨去,如

float f = 1.5;

int x = f ;

printf(“%d \n “, x);

輸出的結果為:

1

當 f 為負數時結果又是如何呢 ?

float f = -1.5 ;

int x = f;

printf(“%d \n”, x);

輸出的結果為:

-1

因此我們可以認為,C語言的舍入方式為向零舍入。接下來看一下除法的舍入問題。此處先以除以 2 的冪的無符號除法為例,

上圖表示 12340 / 2^k 的時候二進制與對應的十進制的表示,此時的舍入是完全沒有問題的。接下來看下除以 2 的冪的有符號除法。

    當k = 4 的時候,-12340 / 2^ 4 == -771.25,此時的正確舍入值應該為 -771,但是其卻舍入成了 -772。這是因為,如果我們單純使用右移來進行除法的時候,其舍入方式為向下舍入,即總是往更小值的方向舍入,在沒有小數位的情況下是正確的,但是如果有小數位的時候,如 -771.25 舍入為 -772, 771.25 舍入為 771。而C語言的舍入方式為向零舍入,即總是往靠近零的值舍入,如 771.25 舍入為 771, -771.25 舍入為 -771。那麼如何實現這種舍入方式呢。當被除數為負數時,我們可以通過加上一個偏置值來糾正這種不正確的舍入方式。

    我們可以觀察一下上圖的有符號除法例子,可以發現,當右移的 k 位單獨拿出來,不為 0 的時候,會導致舍入結果不正確,這是因為,k 位的值不為 0 的時候,表示該結果有小數,所以可以通過 (x + (1 << k) – 1) >> k 得到正確的結果, (1 << k) – 1 可以獲得 k 個 1,x 加上 k 個 1 可以使捨去的 k 位不為 0 時產生進位,x >> k 的結果加一,從而使舍入正確。

關於整數的表示和運算,個人覺得有幾個需要關注的點,一是溢出問題,由於使用有限的位來表示整數,所以當数字過大的時候可能會產生溢出,溢出的位會被捨去,但是有符號數的溢出可能會使符號位被置反,如 0111 1111 …. 1111 + 1 = 1000 0000 …. 0000,0111 1111 …. 1111 為 INT_MAX , INT_MAX + 1 會得到 INT_MIN。此外,無符號數與有符號數進行比較的時候,會使有符號數強制轉換為無符號數,如果有以下循環代碼:

for(size_t i = 10; i >= 0 ; i–);

由於 i 為無符號數,當 i == 0 的時候,判斷還會繼續循環下去, 0 – 1  = -1 , -1 的補碼錶示為 1111 1111 …. 1111 , 剛好是無符號數的最大值,會導致死循環。因此也需要注意一切與無符號類型數據的運算,以及強制類型轉換可能出現的問題。

3. 浮點數

    終於來到了這一章的重點內容之一(其實感覺這本書哪裡都挺重要的),這裏主要介紹浮點數是如何表示的,並且介紹浮點數舍入的問題(和上面講到的舍入不大一樣),浮點數的表示及其運算標準稱為 IEEE754 標準,初看可能會讓你覺得有些晦澀難懂,但是理解之後會覺得設計的十分巧妙。

3.1 定點表示法

    首先讓我們先看下十進制的浮點數是如何表示的,浮點數的定義與小數點息息相關,定義在小數點左邊的数字的權是 10 的正冪,右邊的数字為 10 的負冪,如 12.34 表示 1 * 10^ 1 + 2 * 10^0 + 3 * 10 ^-1 + 4 * 10 ^ -2 = 12又34/100,同理可以得到二進制的浮點數表示,即定義在小數點左邊的数字的權是 2 的正冪,右邊的数字為 2 的負冪,如 101.11 = 1 * 2^2 + 0 * 2^1 + 1 * 2^0 + 1 * 2^-1 + 1 * 2^-2 。這種浮點數的表示方法是有缺陷的,無法精準的表示特定的数字,以 1/5 為例,可以用 十進制数字 0.2 表示,但是我們無法用二進制數字錶示它,只能近似的表示它,通過增加二進製表示的長度可以提升表示的精度。如下圖所示。

3.2 IEEE754標準

    在前面談到的定點表示法不能有效的表示一個比較大的数字,例如 5 x 2^100 是用 101 後面跟隨 100 個零的位模式,我們希望能夠通過給定 x 和 y 的值來表示如 x * 2 ^y 的数字。IEEE754 標準使用 V = ( – 1)^S * M * 2^E 的形式來表示一個數。

  • 符號(Sign): S 決定這個數是負數(S = 1 )還是正數 (S = 0), 對於數值為 0 的符號位做特殊解釋。
  • 尾數(Significand): M 是一個二進制小數,範圍為 1 ~ 2 – e , 或者是 0 ~ 1 – e 。
  • 階碼(Exponent): E 的作用是對浮點數進行加權,這個權重是 2 的 E 次冪(E 可能為負數)。

通過將浮點數的位劃分為三個字段,分別對這些值進行編碼:

  • 一個單獨的符號位 S 。
  • k 位的階碼字段 ,exp = e(0) e(1) e(2) … e(k-1) ,exp 用來編碼階碼 E。
  • n 位的小数字段 ,   frac = f(n-1) … f(1) f(0) ,frac 用來編碼尾數 M。

下圖是該標準下封裝到字中的兩種最常見的格式。

此外,根據階碼值(exp),被編碼的值可以分為下圖幾種情況(階碼值全為 0 ,階碼值全為 1 , 階碼值不全為 0 也不全為 1):

接下來對這幾種格式進行一一介紹~:

  • 規格化浮點數 : 這是最普遍的情況,當 exp 的值不全為 0 也不全為 1 時,就屬於這種情況,這種情況下,階碼值 E = e – bias ,其中 e 為無符號數,即 exp 的值,而 bias 是一個 2^(k-1) – 1 的偏置值(單精度為 127,雙精度為 1023),而小数字段 frac 被解釋為描述小數值 f ,其中 0 <= f < 1,其二進製表示為 0.f(n-1)…f(1)f(0) 的数字,也就是二進制小數點在最高有效位的左邊的形式。尾數定義為 M = 1 + f 。 有時候這種方式也叫做 隱含 1 開頭的表示(implied leading  1),因為這種定義我們可以把 M 看成一個二進製表示為 1.f(n-1) … f(1)f(0) 的数字。既然我們總是能調整階碼 E ,使得尾數 M 在範圍 1 <= M < 2 之中(假設沒有溢出),那麼這樣可以節約一個位,因為第一位總是為 1 。
  • 非規格化浮點數 : 當 exp 的值全為 0 的時候,所表示的浮點數為非規格化類型,E = 1 – bias ,而尾數的值為 M = f 。不含開頭的 1 。非規格化有兩種用途,首先它提供了表示  0 的方法,因為規格化數使得 M >=  1,所以不能表示 0 ,另外非規格化數另一個功能則是表示那些非常接近於 0.0 的數,他們提供了一種屬性,稱為逐漸溢出,其中,可能的數值均勻分佈接近於 0.0 。
  • 特殊值 : 最後一類數值是指當階碼全為 1 的時候出現的。當小數域全為 0 時,表示為無窮大/小,當我們將兩個非常大的數相乘時,或者除以零時,無窮能夠表示溢出的結果。當小數域為非 0 時,結果為 NaN(Not a Number),一些運算的結果不能為實數或者無窮時,會返回 NaN,比如 根號 -1 ,或者 無窮減無窮。此外,在某些應用中也可以用來表示未初始化的數值。

    首先,通過一個字長為 8 位的例子,來看一下IEEE754標準實際上使用時是如何表示的 :

    上圖為展示了假定 w = 8 的字長,k = 4 的階碼位以及 n = 3 的小數位。偏移量為 2 ^ ( k -1 ) -1 = (2 ^ 3) – 1 = 7。圖中分別展示了非規格化數,規格化數以及特殊值是如何編碼的,以及如何結合在一起表示 V = (2^E) * M。我們可以看到,從最大非規格化數到最小規格化數,其值的轉變十分平滑,從 7/512 到 8/512 。這得益於非規格化數的 E 定義為 1 – bias ,最大的非規格化數的階碼值 E 與最小的規格化數的階碼值 E 是相等的,兩者唯一的區別在於 M 值,規格化數尾數 M = 1 + f ,而非規格化的尾數 M = f ,因為非規格化值是用於表示 [0, 1] 區間的小數的,當 f 達到最大值時, f 接近於 1 ,此時最大的非規格化數再進一位,小數 M 只能表示為 1 ,因為此時限制於 f 的位數,沒有比 f 大又比 1 小的小數值 ,進位後轉換成了規格化數,此時 f = 0 , 在階碼值 E 相等的情況下,讓規格化的 M = 1 + f 恰好可以使兩者進行平滑的轉換。

    假如我們使非規格化數的 E = 0 – bias = -7 ,那麼會導致最大非規格化數和最小規格化數的粒度過大,兩者的值分別為 7/1024 和 8/512 。這種定義可以彌補非規格化數的尾數沒有隱含的 1 。通過上述的例子,我們可以發現 ,假如我們把上述的例子按無符號整數表示的話,會發現它的值是有序上升的,這不是偶然的,IEEE 格式如此設計就是為了浮點數能夠使用整數排序函數進行排序。

    通過練習將整數值轉換為浮點數值形式對理解浮點數很有用,以 12345(十進制) 為例,其二進製表示為 1100 0000 1110 01 . 0  ,通過將小數點左移 13 位得到 1.1000000111001 * 2^13 ,我們丟棄開頭的 1 (這裏的 1 就是規格化數隱含的 1),構造小数字段,當 f 不足 23 位的時候,往後填充 0 ,即 M = 1 + f = 1 + 1000 0001 1100 1000 0000 000 ,當 f 大於 23 位的時候,f 多出的位會被捨棄(這裏可以看出浮點數的兩個性質,以 int 類型和 float 類型舉例,當 int 值 大於 2^24 的時候,int 轉換成 float 兩者很有可能值會不相等,因為多出的部分被捨棄了,二是 float 可以表示的數值遠遠大於 int 類型,V =  (-1 ^ S)  * M * 2^E  ,E 最高可以等於 127 ,float 的最大值為 (2^127) * (1 + f),而 int 最大值為 (2^31) -1。

3.3 舍入

    浮點數的舍入方式有四種,分別是向上舍入,向下舍入,向零舍入,向偶數舍入。下圖是幾種舍入方式的例子 :

偶數舍入是浮點數默認的舍入方式,可以看到,向偶數舍入時,當小數值為中間值時,會使最低有效数字總為偶數,如 2.5 和 1.5 都舍入為 2 。為什麼使用向偶數舍入呢,假設我們採用向上舍入,用這種方法舍入一組數值,會在計算這些值的平均值中引入統計偏差。我們採用這種方式舍入得到的平均值總是比這些數本身的平均值要略高一些,反之向下舍入亦然,向偶數舍入則可以使在 50% 的時間內向上舍入,50% 的時間內向下舍入。

4. 小結

    • 計算機將信息編碼為位(bit),通常組織成字節序列,有不同的編碼方式來表示整數,實數和字符串。不同的計算機模型在編碼数字和多字節數據中的字節順序時使用不同的約定。
    • 絕大部分機器使用補碼來編碼整數。對於浮點數使用 IEEE754 標準來編碼。
    • 在進行對無符號和有符號整數進行強制類型轉換時,底層的位模式是不變的。(浮點數與整數轉換則會進行 改變,如 float f = 1.25; int x = f; 此時打印兩者的十六進制值,可以分別輸出為 f = 92463258 ,x = 1 )
    • 由於編碼的長度有限,當超出表示範圍時,有限長度會引起數值溢出,如 x * x 可能會得到負數。當浮點數非常接近於 0.0 時,轉換成 0 時也會產生下溢。
    • 使用補碼運算 ~x + 1 = -x (不適用於 INT_MIN) 。可以通過 (2^k) – 1 生成一個 k 位的掩碼。
    • 浮點數不具備結合率,因為可能發生溢出或者舍入,從而失去精度。如(le20 * le20) * le-20 = 正無窮,而 le20 * (le20 * le-20) = le20 。此外也不具備分配性,如 le20 * (le20 – le20) = 0.0 ,而 le20 * le20 – le20 * le20 = NaN。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

基於cookie的用戶登錄狀態管理

cookie是什麼

先來花5分鐘看完這篇文章:

看完上文,相信大家對cookie已經有了一個整體的概念,我再強調一下,cookie是一個客戶端概念,它是存儲在瀏覽器本地的一小段文本(通常由服務器來生成這段文本)。

cookie的作用

如上文所說,cookie有許多作用,如會話狀態管理,個性化設置,瀏覽器行為跟蹤,客戶端數據的存儲等等。本篇文章就來講講基於cookie的用戶登錄狀態管理。

插一句哈,一般提到cookie,還會有一個叫session的傢伙和它一起出現,下篇文章我會講到它,以及兩者的區別。

cookie的產生過程

如上圖所示,客戶端攜帶賬號和密碼向服務器發起請求,服務器在校驗通過後,通過HTTP Respose Header中的Set-Cookie頭部,將一小段文本寫入客戶端瀏覽器,在以後的每個客戶端HTTP Request Header的Cookie頭部中會自動攜帶這段文本。

基於cookie的用戶登錄狀態管理

下面我基於golang和gin框架(中間件使用比較舒服)來簡單的實現一個基於cookie的用戶登錄狀態管理demo

package main

import (
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.GET("/login", Login)
    
    // 需要登陸保護的
    auth := r.Group("")
    auth.Use(AuthRequired())
    {
        auth.GET("/me", UserInfo)
        auth.GET("/logout", Logout)
    }

    r.Run("localhost:9000")
}

// 登陸
func Login(c *gin.Context) {
    // 為了演示方便,我直接通過url明文傳遞賬號密碼,實際生產中應該用HTTP POST在body中傳遞
    userID := c.Query("user_id")
    password := c.Query("password")

    // 用戶身份校驗(查詢數據庫)
    if userID == "007" && password == "007" {
        // 生成cookie
        expiration := time.Now()
        expiration = expiration.AddDate(0, 0, 1)
        // 實際生產中我們可以加密userID
        cookie := http.Cookie{Name: "userID", Value: userID, Expires: expiration}
        http.SetCookie(c.Writer, &cookie)

        c.JSON(http.StatusOK, gin.H{"msg": "Hello " + userID})
        return
    }
    c.JSON(http.StatusBadRequest, gin.H{"msg": "賬號或密碼錯誤"})
}

// 檢測是否登陸的中間件
func AuthRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        cookie, _ := c.Request.Cookie("userID")
        if cookie == nil {
            c.JSON(http.StatusUnauthorized, gin.H{"msg": "請先登陸"})
            c.Abort()
        }
        // 實際生產中應校驗cookie是否合法
        c.Next()
    }
}

// 查看用戶個人信息
func UserInfo(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"msg": "007的個人頁面"})
}

// 退出登陸
func Logout(c *gin.Context) {
    // 設置cookie過期
    expiration := time.Now()
    expiration = expiration.AddDate(0, 0, -1)
    cookie := http.Cookie{Name: "userID", Value: "", Expires: expiration}
    http.SetCookie(c.Writer, &cookie)

    c.JSON(http.StatusOK, gin.H{"msg": "退出成功"})
}

我們來看具體的演示流程和效果:

如下圖所示,當我們退出后再去嘗試訪問個人頁面時,會出現401沒有權限的錯誤。

上述例子的缺點

先來說說上面的demo存在的問題吧,我們的退出登錄函數本質是設置了一個過期了的cookie來覆蓋以前發送給用戶的正常cookie。

但是,這兒存在着一個重大的安全問題。如果用戶將之前未過期的正常cookie記錄下來(即本例子中的userID=007),即使調用了我們的logout接口,只要用戶自己手動輸入之前未過期的正常cookie,也是可以通過服務器的驗證。

而且,最重要的是,我們無法讓其失效,因為cookie的過期刪除機制是由瀏覽器來控制的,但是當用戶記錄了cookie中的哪段文本后,在cookie到期后,瀏覽器只能刪除存在於瀏覽器中的cookie,對用戶自己記錄下來的cookie確無能為力,也就是說這段cookie永遠有效。(後面我們會講一種叫json web token的技術,可以做到讓我們簽發的憑證自帶過期機制,而不依賴瀏覽器)

當然,有同學會說,我們可以在服務器存儲一份有效的cookie列表,在用戶退出登錄后,從有效列表中刪除對應的cookie,這種在服務端維護用戶狀態的機制本質是session的思想,我們後面會講基於session的用戶登錄狀態管理。

再來說說cookie別的缺點:

當然這個我們通過設置cookie的屬性為HttpOnly,來禁止JavaScript讀取cookie值,可以起到一定的防護作用。

當然,cookie也是有優點的,我們把用戶的登錄狀態保存在客戶端,這樣就不需要每一次去訪問數據庫來檢測用戶是否登錄,減少了系統的IO開銷。

最後

本文希望通過一個不是很完美的demo來講述基於cookie的用戶登錄狀態管理,下期我們來講講session。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

程序員需要了解的硬核知識之操作系統入門

對於程序員來說,最莫大的榮耀莫過於自己的軟件被大多數人使用了吧。

歷史文章請戳

本篇文章作為操作系統的入門文章,可能入門都算不上吧,畢竟操作系統太龐大和複雜了。本篇文章主要帶你了解一下我們常用的操作系統環境。

操作系統環境

程序中包含着運行環境這一內容,可以說 運行環境 = 操作系統 + 硬件 ,操作系統又可以被稱為軟件,它是由一系列的指令組成的。我們不介紹操作系統,我們主要來介紹一下硬件的識別。

我們肯定都玩兒過遊戲,你玩兒遊戲前需要干什麼?是不是需要先看一下自己的筆記本或者電腦是不是能肝的起遊戲?下面是一個遊戲的配置(懷念一下 wow)

圖中的主要配置如下

  • 操作系統版本:說的就是應用程序運行在何種系統環境,現在市面上主要有三種操作系統環境,Windows 、Linux 和 Unix ,一般我們玩兒的大型遊戲幾乎都是在 Windows 上運行,可以說 Windows 是遊戲的天堂。Windows 操作系統也會有區分,分為32位操作系統和64位操作系統,互不兼容。
  • 處理器:處理器指的就是 CPU,你的電腦的計算能力,通俗來講就是每秒鐘能處理的指令數,如果你的電腦覺得卡帶不起來的話,很可能就是 CPU 的計算能力不足導致的。想要加深理解,請閱讀博主的另一篇文章:

  • 顯卡:顯卡承擔圖形的輸出任務,因此又被稱為圖形處理器(Graphic Processing Unit,GPU),顯卡也非常重要,比如我之前玩兒的劍靈開五檔(其實就是圖像變得更清晰)會卡,其實就是顯卡显示不出來的原因。
  • 內存:內存即主存,就是你的應用程序在運行時能夠動態分析指令的這部分存儲空間,它的大小也能決定你電腦的運行速度,想要加深理解,請閱讀博主的另一篇文章

  • 存儲空間:存儲空間指的就是應用程序安裝所佔用的磁盤空間,由圖中可知,此遊戲的最低存儲空間必須要大於 5GB,其實我們都會遺留很大一部分用來安裝遊戲。

從程序的運行環境這一角度來考量的話,CPU 的種類是特別重要的參數,為了使程序能夠正常運行,必須滿足 CPU 所需的最低配置。

CPU 只能解釋其自身固有的語言。不同的 CPU 能解釋的機器語言的種類也是不同的。機器語言的程序稱為 本地代碼(native code),程序員用 C 等高級語言編寫的程序,僅僅是文本文件。文本文件(排除文字編碼的問題)在任何環境下都能显示和編輯。我們稱之為源代碼。通過對源代碼進行編譯,就可以得到本地代碼。下圖反映了這個過程。

Windows 操作系統克服了CPU以外的硬件差異

計算機的硬件並不僅僅是由 CPU 組成的,還包括用於存儲程序指令的數據和內存,以及通過 I/O 連接的鍵盤、显示器、硬盤、打印機等外圍設備。

在 WIndows 軟件中,鍵盤輸入、显示器輸出等並不是直接向硬件發送指令。而是通過向 Windows 發送指令實現的。因此,程序員就不用注意內存和 I/O 地址的不同構成了。Windows 操作的是硬件而不是軟件,軟件通過操作 Windows 系統可以達到控制硬件的目的。

不同操作系統的 API 差異性

接下來我們看一下操作系統的種類。同樣機型的計算機,可安裝的操作系統類型也會有多種選擇。例如:AT 兼容機除了可以安裝 Windows 之外,還可以採用 Unix 系列的 Linux 以及 FreeBSD (也是一種Unix操作系統)等多個操作系統。當然,應用軟件則必須根據不同的操作系統類型來專門開發。CPU 的類型不同,所對應機器的語言也不同,同樣的道理,操作系統的類型不同,應用程序向操作系統傳遞指令的途徑也不同

應用程序向系統傳遞指令的途徑稱為 API(Application Programming Interface)。Windows 以及 Linux 操作系統的 API,提供了任何應用程序都可以利用的函數組合。因為不同操作系統的 API 是有差異的。所以,如何要將同樣的應用程序移植到另外的操作系統,就必須要覆蓋應用所用到的 API 部分。

鍵盤輸入、鼠標輸入、显示器輸出、文件輸入和輸出等同外圍設備進行交互的功能,都是通過 API 提供的。

這也就是為什麼 Windows 應用程序不能直接移植到 Linux 操作系統上的原因,API 差異太大了。

在同類型的操作系統下,不論硬件如何,API 幾乎相同。但是,由於不同種類 CPU 的機器語言不同,因此本地代碼也不盡相同。

FreeBSD Port 幫你輕鬆使用源代碼

不知道你有沒有這個想法:“既然 CPU 不同會導致本地代碼不同,那為何不將源代碼直接發送給程序呢?”這確實是一種解決辦法,Unix 系列的 FreeBSD 操作系統就使用了這種方式。

Unix 系列操作系統 FreeBSD 中,存在一種名為 Ports 的機制。該機制能夠結合當前運行環境的硬件環境來編譯應用的源代碼,進而得到可以運行的本地代碼。如果目標應用的源代碼在硬件上找不到,Ports 就會自動使用 FTP 連接到相應站點下載代碼。

全球有很多站點都提供適用於 FreeBSD 的應用源代碼。通過使用 Ports 可以利用的程序源代碼,大約有 16000 種。根據不同的領域進行分類,可以隨時使用。

FreeBSD 上應用的源代碼,大部分是用 C 語言來標註的,C 編譯器可以結合 FreeBSD 的運行環境來生成合適的本地代碼。

FTP( File Transfer Protocol) 是連接到互聯網上的計算機之間的傳送文件的協議。

可以使用虛擬機獲取其他環境

即使不通過應用程序的移植,在同一個操作系統上仍然可以使用其他的操作系統,那就是使用 虛擬機軟件。虛擬機(Virtual Machine)指通過軟件的具有完整硬件系統功能的、運行在一個完全隔離環境中的完整計算機系統。在實體計算機中能夠完成的工作在虛擬機中都能夠實現。

提供相同運行環境的 Java 虛擬機

總算是提到大 Java 了, Java 大法好,除了虛擬機的方法之外,還有一種方法能夠提供不依賴於特定硬件和操作系統的程序運行環境,那就是 Java。

大家說的 Java 其實有兩層意思,一種是作為編程語言的 Java;一種是作為程序運行環境的 Java。Java 與其他語言相同,都是通過源代碼編譯后運行的。不過,編譯後生成的不是特定 CPU 使用的本地代碼,而是名為字節代碼 的程序。直接代碼的運行環境就稱為 Java 虛擬機(Java Virtual Machine)。Java 虛擬機是一邊把 Java 字節代碼逐一轉換為本地代碼一邊在運行着。

程序運行時,將編譯后的字節代碼轉換為本地代碼,這樣的操作看上去有些迂迴,但由此可以實現相同的字節碼可以在不同的操作系統環境下運行。

想象一下,你開發完成的應用部署到 Linux 環境下,是不是什麼都不用管?

Windows 有專門的 Windows 虛擬機,Macintosh 有 Macintosh 專門的虛擬機。從操作系統來看,Java虛擬機就是一個應用,從運行環境上來看,Java 虛擬機就是運行環境。

BIOS 和引導

最後對一些比較基礎的部分做一些補充說明。程序的運行環境,存在着名為 BIOS(Basic Input/Output System)的系統。BIOS 存儲在 ROM 中,是預先內置在計算機主機內部的程序。BIOS 除了鍵盤、磁盤和顯卡等基本控制外,還有引導程序的功能。引導程序是存儲在啟動驅動器啟示區域的小程序。操作系統的啟動驅動器一般硬盤。不過有時也可能是 CD-ROM 或軟盤。

電腦開機后,BIOS 會確認硬件是否正常運行,沒有異常的話會直接啟動引導程序。引導程序的功能是把在硬盤等記錄的 OS 加載到內存中運行。雖然啟動應用是 OS 的功能,但 OS 不能啟動自己,是通過引導程序來啟動的。

文章參考:

《程序是怎樣跑起來的》第七章

關注公眾號後台回復 191106 即可獲得《程序是怎樣跑起來的》电子書

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?