您好,登錄后才能下訂單哦!
這篇文章主要介紹了Rust中FFI編程知識(shí)點(diǎn)有哪些的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Rust中FFI編程知識(shí)點(diǎn)有哪些文章都會(huì)有所收獲,下面我們一起來看看吧。
Rust 語(yǔ)言主要在關(guān)鍵字和標(biāo)準(zhǔn)庫(kù)兩個(gè)方面對(duì) FFI 提供了支持,具體如下:
關(guān)鍵字 extern
屬性 #[no_mangle]
外部塊 ExternBlock 及其屬性 link 和 link_name
標(biāo)準(zhǔn)庫(kù)
std:os:raw 模塊:例如c_char。
std:ffi 模塊:傳遞 UTF-8 字符串時(shí),CString和CStr很有用。
你可以使用 libc::foo 這種形式訪問這個(gè)庫(kù)中的任何導(dǎo)出內(nèi)容。
在Rust里,只能創(chuàng)建子線程,如果想創(chuàng)建子進(jìn)程,就需要用到libc庫(kù)
fn main() { unsafe { let pid = libc::fork(); if pid > 0 {println!("Hello, I am parent thread: {}", libc::getpid());} else if pid == 0 {println!("Hello, I am child thread: {}", libc::getpid());println!("My parent thread: {}", libc::getppid()); } else {println!("Fork creation failed!");}}}
1.libc 的所有函數(shù)調(diào)用,都必須放進(jìn) unsafe 塊中。因?yàn)樗乃姓{(diào)用都是 unsafe 的;
2.std 的線程操作封裝,好用,形象。libc 的進(jìn)程操作,與 C 語(yǔ)言系統(tǒng)編程一樣,完全是另外一套思路和編程風(fēng)格;
3.std 的線程操作雖然簡(jiǎn)潔,但是也缺少更細(xì)顆粒度的控制。而 libc 可以對(duì)進(jìn)程的操作(及后面對(duì)子進(jìn)程的功能擴(kuò)充,父進(jìn)程中的信號(hào)管理等),做到完全的控制,更加靈活,功能強(qiáng)大;
4.std 本身無法實(shí)現(xiàn)進(jìn)程 fork 的功能。
因?yàn)槲?Rust 的封裝是 zero cost (零成本)的。零成本抽象賦予了 Rust 系統(tǒng)編程的能力。
libc 與 std::os:????::raw,這里面有的用法是一樣的,沒有任何問題。簡(jiǎn)單的和C交互可以用os:raw里面的,而一旦產(chǎn)生了系統(tǒng)調(diào)用或者 Unix 環(huán)境編程,那么就得引入 libc 庫(kù)來操作。
這個(gè)工具就是將寫好的Rust代碼配置一下,然后會(huì)自動(dòng)生成接口代碼頭文件等等。其實(shí),F(xiàn)FI封裝、轉(zhuǎn)換,熟悉了之后,知識(shí)點(diǎn)就那些,模式也比較固定,如果接口量很大,那就需要大量重復(fù)的 coding。量一大,人手動(dòng)綁定出錯(cuò)的機(jī)率也大。所以這種輔助工具的意義就顯露出來了?;谳o助工具生成的代碼,如不完美,再適當(dāng)手動(dòng)修一修,幾下就能搞定,大大提高生產(chǎn)效率。
在Rust中,存在三種類型的指針:
引用—安全的指針
&T:它是對(duì)類型T的不可變引用
&mut T:它是對(duì)類型T的可變引用
眾所周知,Rust語(yǔ)言的指針是一種安全的指針,它會(huì)遵循一定的規(guī)則,比如ownership規(guī)則,會(huì)確保不出現(xiàn)懸掛指針。但是當(dāng)我們需要寫一些底層框架的時(shí)候,往往需要繞過這些規(guī)則,自由的控制指針,這時(shí)候我們就可以使用原始指針。
*const T:表示指向類型T的不可變?cè)贾羔?。它是Copy類型。這類似于&T,只是它可以為空值。
*mut T:一個(gè)指向T的可變?cè)贾羔槪恢С諧opy特征(non-Copy)。
以下可以定義Rust的原始指針:
fn main() { let mut num = 5; let r1 = &num as *const i32; let r2 = &mut num as *mut i32; }
管理原始指針非常不安全,開發(fā)者在使用它們時(shí)需要注意很多細(xì)節(jié)。不恰當(dāng)?shù)厥褂盟鼈兛赡軙?huì)以非常隱蔽的方式導(dǎo)致諸如內(nèi)存泄漏、引用掛起,以及大型代碼庫(kù)中的雙重釋放等問題。為了解決這些問題,我們可以使用C++中廣泛采用的智能指針。
智能指針的兩個(gè)特性:Drop和Deref
Drop:這是多次提及的特征,它可以自動(dòng)釋放相關(guān)值超出作用域后占用的資源。Drop特征類似于你在其他語(yǔ)言中遇到的被稱為對(duì)象析構(gòu)函數(shù)的東西。它包含一個(gè)drop方法,當(dāng)對(duì)象超出作用域時(shí)就會(huì)被調(diào)用。該方法將&mut self作為參數(shù)。使用drop釋放值是以LIFO的方式進(jìn)行的。也就是說,無論最后構(gòu)建的是什么,都首先會(huì)被銷毀。drop方法是你為自己的結(jié)構(gòu)體放置清理代碼的理想場(chǎng)所。例如使用引用計(jì)數(shù)值或GC時(shí),它尤其方便。當(dāng)我們實(shí)例化任何Drop實(shí)現(xiàn)值時(shí)(任意堆分配類型),Rust編譯器會(huì)在編譯后的代碼中每個(gè)作用域結(jié)束的位置插入drop方法調(diào)用。因此,我們不需要在這些實(shí)例上手動(dòng)調(diào)用drop方法。
Deref:為了提供與普通指針類似的行為,也就是說,為了能夠解引用被指向類型的調(diào)用方法,智能指針類型通常會(huì)實(shí)現(xiàn)Deref特征,這允許用戶對(duì)這些類型使用解引用運(yùn)算符*。雖然Deref只為你提供了只讀權(quán)限,但是還有DerefMut,它可以為你提供對(duì)底層類型的可變引用。
智能指針的種類:
標(biāo)準(zhǔn)庫(kù)中的智能指針有如下幾種。
Box:它提供了最簡(jiǎn)單的堆資源分配方式。Box類型擁有其中的值,并且可用于保存結(jié)構(gòu)體中的值,或者從函數(shù)返回它們。
Rc:它用于引用計(jì)數(shù)。每當(dāng)獲取新引用時(shí),計(jì)數(shù)器會(huì)執(zhí)行遞增操作,并在用戶釋放引用時(shí)對(duì)計(jì)數(shù)器執(zhí)行遞減操作。當(dāng)計(jì)數(shù)器的值為零時(shí),該值將被移除。
Arc:它用于原子引用計(jì)數(shù)。這與之前的類型類似,但具有原子性以保證多線程的安全性。
Cell:它為我們提供實(shí)現(xiàn)了Copy特征的類型的內(nèi)部可變性。換句話說,我們有可能獲得多個(gè)可變引用。
RefCell:它為我們提供了類型的內(nèi)部可變性,并且不需要實(shí)現(xiàn)Copy特征。它用于運(yùn)行時(shí)的鎖定以確保安全性。
引用計(jì)數(shù)指針:
所有權(quán)規(guī)則只允許某個(gè)給定作用域中存在一個(gè)所有者。但是,在某些情況下你需要與多個(gè)變量共享類型。例如在GUI庫(kù)中,每個(gè)子窗體小部件都需要具有對(duì)其父容器窗口小部件的引用,以便基于用戶的resize事件來調(diào)整子窗口的布局。雖然有時(shí)生命周期允許你將父節(jié)點(diǎn)存儲(chǔ)為&'a Parent,但是它通常受到’a值生命周期的限制,一旦作用域結(jié)束,你的引用將失效。在這種情況下,我們需要更靈活的方法,并且需要使用引用計(jì)數(shù)類型。程序中的這些智能指針類型會(huì)提供值的共享所有權(quán)。
引用計(jì)數(shù)類型支持某個(gè)粒度級(jí)別的垃圾回收。在這種方法中,智能指針類型允許用戶對(duì)包裝值進(jìn)行多次引用。在內(nèi)部,智能指針使用引用計(jì)數(shù)器(這里是refcount)來統(tǒng)計(jì)已發(fā)放的并且活動(dòng)的引用數(shù)量,不過它只是一個(gè)整數(shù)值。當(dāng)引用包裝的智能指針值的變量超出作用域時(shí),refcount的值就會(huì)遞減。一旦該對(duì)象的所有引用都消失,refcount的值也會(huì)變成0,之后該值會(huì)被銷毀。這就是引用計(jì)數(shù)指針的常見工作模式。
Rust為我們提供了兩種引用計(jì)數(shù)指針類型。
Rc:這主要用于單線程環(huán)境。
Arc:這主要用于多線程環(huán)境。
1.pub extern “C” fn sum_of_array(array: *const u32, len: usize) -> u32
slice::from_raw_parts(array,len)
C端傳來的數(shù)組(指針類型),進(jìn)到Rust這邊進(jìn)行強(qiáng)制類型轉(zhuǎn)換,變成非可變?cè)贾羔橆愋汀:瘮?shù)slice::from_raw_parts(array,len)就是對(duì)原始指針進(jìn)行轉(zhuǎn)換為Rust切片類型,切片就是一個(gè)指針+一個(gè)長(zhǎng)度即可。
2.CStr::from_ptr(raw_string):CStr就是C端產(chǎn)生數(shù)據(jù),Rust端使用,
只是借用,常用于打印。raw_string是直接從C接過來的可變?cè)贾羔槨?br/>使用std::ffi::CStr提供的from_ptr方法包裝 C 的字符串指針,它基于空字符’\0’來計(jì)算字符串的長(zhǎng)度,并可以通過它將外部 C 字符串轉(zhuǎn)換為 Rust 的 &str和String
use std::ffi::CStr; use libc::c_char; extern { fn char_func() -> *mut c_char; } fn get_string() -> String { unsafe { let raw_string: *mut c_char = char_func(); let cstr = CStr::from_ptr(raw_string); cstr.to_string_lossy().into_owned() } }
3.CStr::from_ptr(s).to_string_lossy().into_owned():注意to_string_lossy()的使用:因?yàn)樵趓ust中一切字符都是采用utf8表示的而c不是,
因此如果要將c的字符串轉(zhuǎn)換到rust字符串的話,需要檢查是否都為有效utf-8字節(jié)。
4.CString::new(“Hello, world!”).as_ptr():Cstring是Rust端產(chǎn)生數(shù)據(jù),C端進(jìn)行使用。
as_ptr()就是將RustCString指針類型轉(zhuǎn)化為C的原始指針類型。
5.CString::new(“Hello world!”).into_raw()
使用std::ffi::CString提供的一對(duì)方法into_raw和from_raw可以進(jìn)行原始指針轉(zhuǎn)換,由于將字符串的所有權(quán)轉(zhuǎn)移給了調(diào)用者,所以調(diào)用者必須將字符串返回給 Rust,以便正確地釋放內(nèi)存。
into_raw()和.as_ptr()的作用類似,都是變成原始指針傳給C端。
6.CString::from_raw(s)
一般在釋放內(nèi)存的時(shí)候使用,C端用完需要Rust端來釋放。
7.Box::into_raw(Box::new(new_stu)):其實(shí)這里是智能指針和兩端堆棧申請(qǐng)有關(guān),into_raw()就是將Rust智能指針變成原始指針。
8.Box::from_raw(p_stu):from_raw():就是將C端傳來的p_stu變成Rust智能指針。
C代碼:
uint32_t sum = sum_of_array(numbers, length);
Rust代碼:
pub extern "C" fn sum_of_array(array: *const u32, len: usize) -> u32 { let array = unsafe { assert!(!array.is_null()); slice::from_raw_parts(array, len) }; array.iter().sum() }
這里的參數(shù)傳遞一目了然,array一開始是C過來的指針類型,通過slice::from_raw_parts(array,len)之后,變成一個(gè)Rust切片類型,后面用iter進(jìn)行求和。切片類型就是一個(gè)指針和一組數(shù)據(jù)合在一起組成。
對(duì)于C語(yǔ)言來說,字符串有兩種,一種是共享的只讀字符串 char * ,不能修改。另一種是動(dòng)態(tài)分配的可變字符串 char [],可以修改。
而在Rust里面,字符串是由字符的 UTF-8 編碼組成的字節(jié)序列。表示的類型有很多種。
字符串則比較復(fù)雜,Rust 中的字符串,是一組u8組成的 UTF-8 編碼的字節(jié)序列,字符串內(nèi)部允許NULL字節(jié);但在 C 中,字符串只是指向一個(gè)char的指針,用一個(gè)NULL字節(jié)作為終止。
我們需要做一些特殊的轉(zhuǎn)換,在 Rust FFI 中使用std::ffi::CStr,它表示一個(gè)NULL字節(jié)作為終止的字節(jié)數(shù)組,可以通過 UTF-8 驗(yàn)證轉(zhuǎn)換成 Rust 中的&str。
CStr:表示以空字符終止的 C 字符串或字節(jié)數(shù)組的借用,屬于引用類型。一般用于和 C 語(yǔ)言交互,由 C 分配并被 Rust 借用的字符串。
CString:表示擁有所有權(quán)的,中間沒有空字節(jié),以空字符終止的字符串類型。一般用于和 C 語(yǔ)言交互時(shí),由 Rust 分配并傳遞給 C 的字符串。
下面這段代碼,在這里get_string使用CStr::from_ptr從C的char*獲取一個(gè)字符串,并且轉(zhuǎn)化成了一個(gè)String。
fn get_string() -> String { unsafe { let raw_string: *mut c_char = char_func(); let cstr = CStr::from_ptr(raw_string); cstr.to_string_lossy().into_owned() } }
和CStr表示從C中來,rust不擁有歸屬權(quán)的字符串相反,CString表示由rust分配,Rust擁有所有權(quán),可以進(jìn)行修改,用以傳給C程序的字符串。
use std::ffi::CString; use std::os::raw::c_char; extern { fn my_printer(s: *const c_char); } let c_to_print = CString::new("Hello, world!").unwrap(); unsafe { my_printer(c_to_print.as_ptr()); // 使用 as_ptr 將CString轉(zhuǎn)化成char指針傳給c函數(shù) }
兩端分配堆棧,另一端填充打印
關(guān)于“Rust中FFI編程知識(shí)點(diǎn)有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Rust中FFI編程知識(shí)點(diǎn)有哪些”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。