作者:apocelipes
連結:https://www.cnblogs.com/apocelipes/p/10899484.html
通常,當我們開發Linux程式時有兩種方案:
- 在Linux上直接編寫程式并進行運作測試和調試
- 在Windows或Mac OS X上借助工具進行遠端開發
雖然我自己是在Linux環境上直接進行開發的,但也有許多的人是在Windows環境上從事開發工作的,如果離開自己熟悉的系統到陌生的環境上也許會影響到工作效率。
是以今天我們就來看下如何在Windows上使用Visual Studio 2019進行Linux遠端開發以及如何避免常見的陷阱。
Visual Studio的跨平台開發功能簡介
從Visual Studio 2017開始微軟推出了VS的跨平台開發功能,你可以在VS中編輯代碼,随後進行跨平台編譯和遠端調試,将原先我們需要手動完成的工作進行了自動化,大幅減輕了我們的負擔。其中支援的平台包括Android和Linux,也就是我們今天要重點介紹的主角。
也許你會好奇,VS究竟是怎樣進行遠端開發的,雖然你不用了解這些知識也可以進行開發,但我還是希望能用兩分鐘做個簡短的解釋。
VS進行遠端開發分為兩步:
- 建立遠端環境的連接配接,随後讓vs将遠端環境中的系統頭檔案同步到本地(也可以指定其他地方的頭檔案,後面會講解),c++的代碼補全隻需要頭檔案即可。
- 當代碼寫好後,選擇合适的遠端環境,vs将目标檔案和代碼複制到遠端環境的指定位置,接着根據你的配置進行編譯。
- 随後vs将會在console的gdb或gdbserver中運作你的程式,在此期間你可以充分享受vs debugger帶來的高效和便利。
經過上述步驟之後你就可以在vs裡調試自己編寫的跨平台程式了。
使用 VS 2019進行Linux遠端開發
簡介到此結束了,下面我們來看看在VS 2019進行Linux開發的圖文教程。在我們開始之前,首先要做點準備工作:
- 安裝好VS 2019,且勾選了C++ for Linux功能;
- 準備一個可用的Linux遠端環境,例如配置了靜态IP的Linux虛拟機,并且已經安裝好了GCC工具鍊以及openssh。
做好準備後我們就該進入正題了。
建立項目
安裝好C++ for Linux功能後我們會在建立新項目的面闆中看到Linux的選項,如圖:

這裡我們選擇了使用傳統的vs項目解決方案建構的空白控制台程式,後續的文章中你還可以看到如何建立cmake項目,這裡暫且不提。
下面沒什麼要說的,選擇項目的存儲位置,注意是本地的位置,遠端機器的位置在後面會進行配置:
點選建立,我們的遠端開發項目就建立成功了。
配置遠端項目
VS不能編輯空項目的配置,是以我們先在項目中建立一個main.cpp,然後點選頂部菜單:項目->屬性,你就能看到項目的配置界面了:
遠端計算機是在調試中的遠端連接配接管理器中添加的。這裡一般不需要改動,除非你需要改變項目的類型或編譯結果的存放位置。如果有多個遠端環境時,也可以在這裡進行選擇。
調試部分提供了gdb和gdbserver,前者是讓VS在Linux上啟動一個console,然後在其中運作gdb并傳回輸出,如果你的Linux上的終端配置了彩色輸出,那麼和遺憾vs并不認識他們,會顯示成原始的字元串;
使用gdbserver時會在遠端啟用gdbserver 本地VS解析回傳的資料不會出現雜音。
這裡我們選擇了gdbserver,如果你發現無法打斷點,那麼參考微軟的建議,換回gdb方案:
接着是配置的重點,首先是配置需要同步的遠端環境的頭檔案,有了這些檔案vs才能對你的代碼進行自動補全和提示:
預設複制的路徑通常已經包含了Linux上大部分的頭檔案,通常我們也不需要做更改。頭檔案的同步發生在第一次建構項目成功後或添加遠端連接配接後手動同步。
接着是C/C++編譯器的選擇,也就是對gcc和g++編譯參數的配置,講解這些參數超出了我們的讨論範圍,我們這裡隻需要選擇合适的C++标準版本:
這裡我們選擇了c++17。其他設定與在Windows上進行開發時一樣,vs可以自動轉換成g++的參數,這裡就不再贅述。
添加遠端環境
有了遠端環境我們才能同步頭檔案或者進行調試運作。
在第一次編譯或調試你的項目時vs會自動讓你連接配接遠端環境,當然,我們推薦在調試->選項->跨平台->連接配接管理器中進行設定:
填入你的遠端ip/域名,端口ssh預設為22,安全起見你需要修改成其他端口,這裡友善示範使用了預設配置,密碼同上,你應該考慮使用更安全的ssh私鑰登入。
登入成功後這個連接配接就添加完成了,我們看到管理器下面還有一個遠端标頭管理器的設定項,這就是用來同步頭檔案的:
點選更新按鈕就會開始同步頭檔案,這些檔案會被緩存在本地,因為要從遠端一次性複制大量檔案,是以可能會花費較長的時間。
這樣遠端環境就添加好了,可以開始寫代碼了。
本地編寫和遠端調試
至此你已經可以在VS中編寫面向Linux平台的代碼了,自動補全可以正常工作:
可以看到Linux中的頭檔案和結構體都已經可以識别了。如果你發現無法自動補全(通常發生在剛添加遠端連接配接或是項目設定發生了變化後),先試試關閉vs重新打開,如果沒用請嘗試重新整理intellisense或重新同步頭檔案。
在編輯結束後我們就能點選調試按鈕運作我們的程式了:
注意,建構的體系架構必須是和遠端環境一緻的,比如遠端環境是x64,這裡可以選擇x64或x86,但是不能選擇arm,否則會報錯。
這是測試代碼,它将輸出目前Linux系統核心的版本:
#include
#include
#include
int main(){
auto start = chrono::high_resolution_clock::now();
utsname names;
if (uname(&names) != 0) {
std::perror("cannot get unames");
}
std::cout << "Linux kernel version: " << names.release << std::endl;
}
點選調試->Linux 控制台,會顯示一個可以互動的console,你可以在其中輸入内容或是看到程式的輸出:
程式運作成功。
避免踩坑
遠端編譯順利完成後,我們就可以接着利用vs debugger設定斷點,在斷點處檢視變量,甚至對運作中的Linux進行動态性能分析了。
不過在此之前,還有一些坑需要提前踩掉。
中文亂碼
編碼問題帶來的麻煩永遠會被放在第一位,畢竟當人們看到預想的輸出實際上是一堆亂碼時總會不可避免得緊張起來。
衆所周知,編碼問題一直是老大難,特别是Windows上中文環境通常是GB18030或GBK,而Linux上統一為utf8時。
下面看個實際例子,通常我們的程式裡隻包含ASCII字元的話不容易産生問題,是以我們加上一點中文字元:
#include
#include
#include
#include
int main(){
utsname names;
if (uname(&names) != 0) {
std::perror("cannot get unames");
}
std::cout << "Linux kernel version: " << names.release << std::endl;
std::cout << "輸入内容:";
std::string input;
std::cin >> input;
std::cout << "你輸入了:" << input << std::endl;
}
對于上面的測試程式,我們添加了一點中文輸出資訊,現在打開控制台進行調試:
可以看到中文輸出變成了亂碼,我們輸入一些資訊進去,這是運作結果:
可以看到,程式内寫入的中文發生了亂碼,而我們的輸入沒有。原因很簡單,輸入時實在linux的控制台環境下,編碼預設是utf8的,是以我們的輸入被正确編碼,而源檔案中的内容是GB18030的,是以在Linux控制台(預設以utf8解碼資料并顯示)中會發生亂碼。
錯誤的原因知道了解決起來也就很簡單了,把源檔案的編碼改成utf8就行,我們選擇最簡單的方法,在進階儲存選項中修改編碼(這個菜單選項預設被隐藏,網上有很多介紹如何顯示它的方法的資料):
設定好後儲存檔案,現在檔案的編碼已經被改為了utf8了。
現在運作修改後的程式:
運作結果也是正常的:
使用數學函數和第三方庫
在Linux上使用标準庫提供的數學函數也是一個老生常談的問題,根據你使用cpp還是C會有如下幾個情況:
- 使用cpp時,libstdc++依賴于libm,是以使用g++編譯你的程式時會自動連結數學函數庫;
- 使用c時,如果是sqrt(4)這樣的形式,較新的gcc提供了替換措施,不需要顯示連結libm;
- 接上一條,如果你的參數是個變量,那麼編譯器可能會選擇需要你連結libm。
通常在Windows上我們無需操心這點,但在Linux上使用c語言時就很難忽略這個問題了。
是以保險起見,如果你正在編寫一個使用了數學函數的c程式,那麼總是指定連接配接libm是沒錯的。(具體可以參考http://www.linuxforums.org/forum/programming-scripting/125526-c-gcc-math-h-lm.html)
另外當你使用例如boost這類第三方庫時,也需要注意。在Windows上我們通常指定好附加包含目錄和附加庫目錄即可正常編譯,但是Linux上必須明确指定連結庫的名字,是以我們在項目屬性中進行設定。
在Linux上我們可以使用pkg-config來減輕上述的重複勞動,而在vs中我們不能直接利用這一工具,當你的項目使用了大量第三方庫時就會成為不小的麻煩,如果想要解決這一問題,可以參考後續文章裡我會介紹的vs+cmake建構項目。
下面我們給例子加上一點boost chrono的功能測試,在Linux上需要指定-lboost_chrono,這是設定:
下面是完整的代碼:
#include
#include
#include
#include
#include
int main(){
namespace chrono = boost::chrono;
auto start = chrono::high_resolution_clock::now();
utsname names;
if (uname(&names) != 0) {
std::perror("cannot get unames");
}
std::cout << "Linux kernel version: " << names.release << std::endl;
std::cout << "輸入内容:";
std::string input;
std::cin >> input;
std::cout << "你輸入了:" << input << std::endl;
auto counter = chrono::duration_cast<:milliseconds>(chrono::high_resolution_clock::now() - start);std::cout << "程式運作了:" << counter.count() << "ms
";
}
點選運作按鈕,程式就能正常調試了,否則會報錯:
●編号514,輸入編号直達本文
●輸入m擷取文章目錄
C語言與C++程式設計
分享C/C++技術文章