中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Rust中FFI編程知識點有哪些

發布時間:2022-09-15 10:22:58 來源:億速云 閱讀:198 作者:iii 欄目:開發技術

這篇文章主要介紹了Rust中FFI編程知識點有哪些的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Rust中FFI編程知識點有哪些文章都會有所收獲,下面我們一起來看看吧。

    Rust語言對FFI的支持

    Rust 語言主要在關鍵字和標準庫兩個方面對 FFI 提供了支持,具體如下:
    關鍵字 extern
    屬性 #[no_mangle]
    外部塊 ExternBlock 及其屬性 link 和 link_name
    標準庫
    std:os:raw 模塊:例如c_char。
    std:ffi 模塊:傳遞 UTF-8 字符串時,CString和CStr很有用。

    libc-crate庫

    你可以使用 libc::foo 這種形式訪問這個庫中的任何導出內容。
    在Rust里,只能創建子線程,如果想創建子進程,就需要用到libc庫

    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 的所有函數調用,都必須放進 unsafe 塊中。因為它的所有調用都是 unsafe 的;
    2.std 的線程操作封裝,好用,形象。libc 的進程操作,與 C 語言系統編程一樣,完全是另外一套思路和編程風格;
    3.std 的線程操作雖然簡潔,但是也缺少更細顆粒度的控制。而 libc 可以對進程的操作(及后面對子進程的功能擴充,父進程中的信號管理等),做到完全的控制,更加靈活,功能強大;
    4.std 本身無法實現進程 fork 的功能。
    因為我 Rust 的封裝是 zero cost (零成本)的。零成本抽象賦予了 Rust 系統編程的能力。

    libc 與 std::os:????::raw,這里面有的用法是一樣的,沒有任何問題。簡單的和C交互可以用os:raw里面的,而一旦產生了系統調用或者 Unix 環境編程,那么就得引入 libc 庫來操作。

    cbindgen 工具的介紹和使用

    這個工具就是將寫好的Rust代碼配置一下,然后會自動生成接口代碼頭文件等等。其實,FFI封裝、轉換,熟悉了之后,知識點就那些,模式也比較固定,如果接口量很大,那就需要大量重復的 coding。量一大,人手動綁定出錯的機率也大。所以這種輔助工具的意義就顯露出來了。基于輔助工具生成的代碼,如不完美,再適當手動修一修,幾下就能搞定,大大提高生產效率。

    Rust指針

    在Rust中,存在三種類型的指針:

    1.Rust自帶的指針類型:

    引用—安全的指針

    &T:它是對類型T的不可變引用
    &mut T:它是對類型T的可變引用

    2. 原始指針

    眾所周知,Rust語言的指針是一種安全的指針,它會遵循一定的規則,比如ownership規則,會確保不出現懸掛指針。但是當我們需要寫一些底層框架的時候,往往需要繞過這些規則,自由的控制指針,這時候我們就可以使用原始指針。
    *const T:表示指向類型T的不可變原始指針。它是Copy類型。這類似于&T,只是它可以為空值。
    *mut T:一個指向T的可變原始指針,它不支持Copy特征(non-Copy)。
    以下可以定義Rust的原始指針:

    fn main() {
        let mut num = 5;
        let r1 = &num as *const i32;
        let r2 = &mut num as *mut i32;
    }

    3.智能指針

    管理原始指針非常不安全,開發者在使用它們時需要注意很多細節。不恰當地使用它們可能會以非常隱蔽的方式導致諸如內存泄漏、引用掛起,以及大型代碼庫中的雙重釋放等問題。為了解決這些問題,我們可以使用C++中廣泛采用的智能指針。
    智能指針的兩個特性:Drop和Deref
    Drop:這是多次提及的特征,它可以自動釋放相關值超出作用域后占用的資源。Drop特征類似于你在其他語言中遇到的被稱為對象析構函數的東西。它包含一個drop方法,當對象超出作用域時就會被調用。該方法將&mut self作為參數。使用drop釋放值是以LIFO的方式進行的。也就是說,無論最后構建的是什么,都首先會被銷毀。drop方法是你為自己的結構體放置清理代碼的理想場所。例如使用引用計數值或GC時,它尤其方便。當我們實例化任何Drop實現值時(任意堆分配類型),Rust編譯器會在編譯后的代碼中每個作用域結束的位置插入drop方法調用。因此,我們不需要在這些實例上手動調用drop方法。
    Deref:為了提供與普通指針類似的行為,也就是說,為了能夠解引用被指向類型的調用方法,智能指針類型通常會實現Deref特征,這允許用戶對這些類型使用解引用運算符*。雖然Deref只為你提供了只讀權限,但是還有DerefMut,它可以為你提供對底層類型的可變引用。

    智能指針的種類:
    標準庫中的智能指針有如下幾種。
    Box:它提供了最簡單的堆資源分配方式。Box類型擁有其中的值,并且可用于保存結構體中的值,或者從函數返回它們。
    Rc:它用于引用計數。每當獲取新引用時,計數器會執行遞增操作,并在用戶釋放引用時對計數器執行遞減操作。當計數器的值為零時,該值將被移除。
    Arc:它用于原子引用計數。這與之前的類型類似,但具有原子性以保證多線程的安全性。
    Cell:它為我們提供實現了Copy特征的類型的內部可變性。換句話說,我們有可能獲得多個可變引用。
    RefCell:它為我們提供了類型的內部可變性,并且不需要實現Copy特征。它用于運行時的鎖定以確保安全性。

    引用計數指針:
    所有權規則只允許某個給定作用域中存在一個所有者。但是,在某些情況下你需要與多個變量共享類型。例如在GUI庫中,每個子窗體小部件都需要具有對其父容器窗口小部件的引用,以便基于用戶的resize事件來調整子窗口的布局。雖然有時生命周期允許你將父節點存儲為&'a Parent,但是它通常受到’a值生命周期的限制,一旦作用域結束,你的引用將失效。在這種情況下,我們需要更靈活的方法,并且需要使用引用計數類型。程序中的這些智能指針類型會提供值的共享所有權。

    引用計數類型支持某個粒度級別的垃圾回收。在這種方法中,智能指針類型允許用戶對包裝值進行多次引用。在內部,智能指針使用引用計數器(這里是refcount)來統計已發放的并且活動的引用數量,不過它只是一個整數值。當引用包裝的智能指針值的變量超出作用域時,refcount的值就會遞減。一旦該對象的所有引用都消失,refcount的值也會變成0,之后該值會被銷毀。這就是引用計數指針的常見工作模式。

    Rust為我們提供了兩種引用計數指針類型。
    Rc:這主要用于單線程環境。
    Arc:這主要用于多線程環境。

    Rust和C交互時的各種指針變換

    1.pub extern “C” fn sum_of_array(array: *const u32, len: usize) -> u32
    slice::from_raw_parts(array,len)
    C端傳來的數組(指針類型),進到Rust這邊進行強制類型轉換,變成非可變原始指針類型。函數slice::from_raw_parts(array,len)就是對原始指針進行轉換為Rust切片類型,切片就是一個指針+一個長度即可。

    2.CStr::from_ptr(raw_string):CStr就是C端產生數據,Rust端使用,
    只是借用,常用于打印。raw_string是直接從C接過來的可變原始指針。
    使用std::ffi::CStr提供的from_ptr方法包裝 C 的字符串指針,它基于空字符’\0’來計算字符串的長度,并可以通過它將外部 C 字符串轉換為 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()的使用:因為在rust中一切字符都是采用utf8表示的而c不是,
    因此如果要將c的字符串轉換到rust字符串的話,需要檢查是否都為有效utf-8字節。

    4.CString::new(“Hello, world!”).as_ptr():Cstring是Rust端產生數據,C端進行使用。
    as_ptr()就是將RustCString指針類型轉化為C的原始指針類型。

    5.CString::new(“Hello world!”).into_raw()
    使用std::ffi::CString提供的一對方法into_raw和from_raw可以進行原始指針轉換,由于將字符串的所有權轉移給了調用者,所以調用者必須將字符串返回給 Rust,以便正確地釋放內存。
    into_raw()和.as_ptr()的作用類似,都是變成原始指針傳給C端。
    6.CString::from_raw(s)
    一般在釋放內存的時候使用,C端用完需要Rust端來釋放。

    7.Box::into_raw(Box::new(new_stu)):其實這里是智能指針和兩端堆棧申請有關,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()
    }

    這里的參數傳遞一目了然,array一開始是C過來的指針類型,通過slice::from_raw_parts(array,len)之后,變成一個Rust切片類型,后面用iter進行求和。切片類型就是一個指針和一組數據合在一起組成。

    字符串類型

    對于C語言來說,字符串有兩種,一種是共享的只讀字符串 char * ,不能修改。另一種是動態分配的可變字符串 char [],可以修改。
    而在Rust里面,字符串是由字符的 UTF-8 編碼組成的字節序列。表示的類型有很多種。
    字符串則比較復雜,Rust 中的字符串,是一組u8組成的 UTF-8 編碼的字節序列,字符串內部允許NULL字節;但在 C 中,字符串只是指向一個char的指針,用一個NULL字節作為終止。
    我們需要做一些特殊的轉換,在 Rust FFI 中使用std::ffi::CStr,它表示一個NULL字節作為終止的字節數組,可以通過 UTF-8 驗證轉換成 Rust 中的&str。
    CStr:表示以空字符終止的 C 字符串或字節數組的借用,屬于引用類型。一般用于和 C 語言交互,由 C 分配并被 Rust 借用的字符串。
    CString:表示擁有所有權的,中間沒有空字節,以空字符終止的字符串類型。一般用于和 C 語言交互時,由 Rust 分配并傳遞給 C 的字符串。
    下面這段代碼,在這里get_string使用CStr::from_ptr從C的char*獲取一個字符串,并且轉化成了一個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不擁有歸屬權的字符串相反,CString表示由rust分配,Rust擁有所有權,可以進行修改,用以傳給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轉化成char指針傳給c函數
    }

    兩端分配堆棧,另一端填充打印

    關于“Rust中FFI編程知識點有哪些”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Rust中FFI編程知識點有哪些”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    通许县| 穆棱市| 阿鲁科尔沁旗| 延津县| 双柏县| 龙陵县| 陕西省| 上栗县| 彰化县| 泰顺县| 环江| 福清市| 沙湾县| 诏安县| 宁乡县| 江阴市| 夏津县| 耒阳市| 昌都县| 女性| 文水县| 平和县| 罗源县| 津市市| 咸丰县| 芜湖县| 湖北省| 上栗县| 迭部县| 开远市| 德安县| 沈阳市| 溆浦县| 乌拉特后旗| 静安区| 合水县| 吉安市| 宣威市| 牡丹江市| 紫阳县| 太湖县|