亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

通過 cgo 調用fts_open時出現分段沖突錯誤

通過 cgo 調用fts_open時出現分段沖突錯誤

Go
SMILET 2022-09-19 20:54:56
我正在測試cgo,每個簡單的你好世界,如代碼,工作得很好。但我對下面的C代碼有問題。C 代碼是遍歷目錄樹并對文件大小求和的代碼。如果我用go命令構建,那么構建是可以的,沒有錯誤。但是在運行時,發生了“分段沖突”錯誤bash$./walkdir fatal error: unexpected signal during runtime execution[signal SIGSEGV: segmentation violation code=0x1 addr=0x1 pc=0x7f631e077c1a]. . . .-------------------------------------------------------------package main/*#include <stdint.h>#include <fts.h>#include <sys/stat.h>uintmax_t get_total_size(char *path){    uintmax_t total_size = 0;    FTS *fts = fts_open(&path, FTS_PHYSICAL, NULL);    FTSENT *fent;    while ((fent = fts_read(fts)) != NULL)        if (fent->fts_info == FTS_F)            total_size += fent->fts_statp->st_size;    fts_close(fts);    return total_size;}*/import "C"import "fmt"func main() {    fmt.Println(C.get_total_size(C.CString("/usr")))}
查看完整描述

2 回答

?
侃侃無極

TA貢獻2051條經驗 獲得超10個贊

fts_open 定義如下:

fts_open()
該函數獲取指向一個字符指針數組的指針,這些指針命名一個或多個路徑,這些路徑構成了要遍歷的邏輯文件層次結構。數組必須由指針終止。fts_open()null

C沒有對數組的直接支持;它只有指針。在你的情況下,你傳遞一個有效的指針,但它不在一個數組中,該數組有一個指針作為緊隨其后的元素,所以繼續掃描過去的內存 - 尋找一個指針, 并最終嘗試在某個地址讀取內存,這是禁止這樣做的(通常是因為該地址的頁面沒有被分配)。fts_openNULLfts_open&pathNULL

修復它的一種方法是創建該數組并在C端初始化它。
看起來你正在使用一個相當最新的C標準,所以讓我們只使用直接文字來初始化數組:

package main


/*

#include <stddef.h> // for NULL

#include <stdint.h>

#include <stdlib.h> // for C.free

#include <fts.h>

#include <sys/stat.h>


uintmax_t get_total_size(char *path)

{

    uintmax_t total_size = 0;

    char * path_argv[2] = {path, NULL};

    FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);

    FTSENT *fent;

    while ((fent = fts_read(fts)) != NULL)

        if (fent->fts_info == FTS_F)

            total_size += fent->fts_statp->st_size;

    fts_close(fts);

    return total_size;

}

*/

import "C"


import (

    "fmt"

    "unsafe"

)


func main() {

    cpath := C.CString("/usr")

    defer C.free(unsafe.Pointer(cpath))

    fmt.Println(C.get_total_size(cpath))

}

請注意,您的程序有一個錯誤和一個可能的問題:

  • 一個錯誤是,調用通過從鏈接的 C 庫執行調用來分配內存塊,而您沒有釋放該內存塊。C.CStringmalloc(3)

  • 該符號定義在“標準定義.h”中;編譯時可能會或可能不會收到錯誤。NULL

我已經在我的示例中修復了這兩個問題。

對我們的示例的進一步改進可能是利用函數在單次運行中掃描多個路徑的能力;如果我們要實現它,那么為Go端的第一個參數分配數組會更有意義:fts_*fts_open

package main


/*

#include <stddef.h>

#include <stdint.h>

#include <stdlib.h>

#include <fts.h>

#include <sys/stat.h>


uintmax_t get_total_size(char * const *path_argv)

{

    uintmax_t total_size = 0;

    FTS *fts = fts_open(path_argv, FTS_PHYSICAL, NULL);

    FTSENT *fent;

    while ((fent = fts_read(fts)) != NULL)

        if (fent->fts_info == FTS_F)

            total_size += fent->fts_statp->st_size;

    fts_close(fts);

    return total_size;

}

*/

import "C"

import (

    "fmt"

    "unsafe"

)


func main() {

    fmt.Println(getTotalSize("/usr", "/etc"))

}


func getTotalSize(paths ...string) uint64 {

    argv := make([]*C.char, len(paths)+1)

    for i, path := range paths {

        argv[i] = C.CString(path)

        defer C.free(unsafe.Pointer(argv[i]))

    }


    return uint64(C.get_total_size(&argv[0]))

}

請注意,這里我們沒有顯式地將最后一個參數清零,因為與C相反,Go用零初始化每個分配的內存塊,因此一旦分配,其所有內存都已歸零。argvargv


查看完整回答
反對 回復 2022-09-19
?
楊魅力

TA貢獻1811條經驗 獲得超6個贊

您收到錯誤,因為“fts_open”需要一個指向數組的字符指針,該數組以 NULL 結尾,如 char *argv[] = { 路徑,NULL };.。(https://linux.die.net/man/3/fts_open)


package main


/*

#include <stdint.h>

#include <fts.h>

#include <sys/stat.h>


uintmax_t get_total_size(char *path)

{

    uintmax_t total_size = 0;

    char *argv[] = { path, NULL };

    FTS *fts = fts_open(argv, FTS_PHYSICAL, NULL);

    if (fts == NULL)

        return 0;

    FTSENT *fent;

    while ((fent = fts_read(fts)) != NULL)

        if (fent->fts_info == FTS_F)

            total_size += fent->fts_statp->st_size;

    fts_close(fts);

    return total_size;

}

*/

import "C"

import "fmt"


func main() {

    fmt.Println(C.get_total_size(C.CString("/usr")))

}

因此添加數組指針將修復代碼。


使用 GCC 編譯時,相同的代碼也有效,但fts_open返回 NULL。我猜gcc和cgo之間的優化有一些區別(不是很確定)


我嘗試了一些測試結果,并能夠發現,當使用GCC編譯時,字符**指針被空終止,但在cgo的情況下,它沒有以空值終止,所以你得到“SIGSEGV”,因為你的代碼正在讀取無效的內存引用。


#include <stdio.h>

#include <string.h>


void try(char **p)

{

   while (*p != NULL)

   {

      printf("%zu\n", strlen(*p));

      ++p;

   }

}


void get_total_size(char *path)

{

   try(&path);

}

int main()

{

   get_total_size("/usr");

}

c 代碼(有效)


package main

/*

#include <stdio.h>

#include <string.h>


void try(char **p)

{

   while (*p != NULL)

   {

      printf("%zu\n", strlen(*p));

      ++p;

   }

}


void get_total_size(char *path)

{

   try(&path);

}

*/

import "C"


func main() {

    C.get_total_size(C.CString("/usr"))

}

相同的去代碼,你會面臨錯誤


查看完整回答
反對 回復 2022-09-19
  • 2 回答
  • 0 關注
  • 116 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號