C - 1021 次迭代时出现段错误,无法在 1020 次迭代时打开 i2c

C - segfault at 1021 iterations and unable to open i2c at 1020 iterations

您好,我在使用以下脚本将数据连续记录到 .csv 文件时遇到问题

    int ddm(void)
{   
//  96 Temp MSB,    97 Temp LSB,    98 Vcc MSB,     99 Vcc LSB
//  100 TX_BIA MSB, 101 TX_BIA LSB,
//  102 TX MSB,     103 TX LSB,     104 RX MSB,     105 RX LSB
    
    FILE *focat;
    float temperature, vcc, tx_bias, optical_tx, optical_rx, RAW_tx, RAW_rx;
    char temp[10], vccc[10], txbi[10], optx[10], oprx[10], rwtx[30], rwrx[30];
    int i;
    //Open (or create) the csv file and write the heading row
    focat=fopen("fcatdata.csv", "w");
    
        if(focat == NULL)
        {
                printf("error openining file\n");
                exit(1);
        }
    fprintf(focat,"Temp, Vcc, Tx_Bias, Tx, Rx, RAWTx, RAWRx\n");
    fclose(focat);
    focat=fopen("fcatdata.csv", "a+");
    i=0;
    //start infinite loop
    for(;;) 
    {
    if(!read_eeprom(0x51));
    else exit(EXIT_FAILURE);
    i=i+1;

    //Taking MSB and LSB data and converting
    temperature =  (A51[96]+(float) A51[97]/256);
    vcc =                   (float)(A51[98]<<8  | A51[99])  * 0.0001;
    tx_bias =               (float)(A51[100]<<8 | A51[101]) * 0.002;
    optical_tx = 10 * log10((float)(A51[102]<<8 | A51[103]) * 0.0001);
    optical_rx = 10 * log10((float)(A51[104]<<8 | A51[105]) * 0.0001);
    
    RAW_tx =               ((float)(A51[102]<<8 | A51[103]) * 0.0001);
    RAW_rx =               ((float)(A51[104]<<8 | A51[105]) * 0.0001);
    
    //Display Diagnostics Monitoring Data in Terminal
    printf ("SFP Temperature = %4.4fC\n", temperature);
    printf ("Vcc, Internal supply = %4.4fV\n", vcc);
    printf ("TX bias current = %4.4fmA\n", tx_bias);
    printf ("Tx, Optical Power = %4.4f dBm", optical_tx);
    printf (", %6.6f mW\n", RAW_tx);
    printf ("Rx, Optical Power = %4.4f dBm", optical_rx);
    printf (", %6.6f mW\n", RAW_rx);
    printf ("iteration %d \n", i);
   
    //Change the integers into strings for appending to file
    sprintf(temp, "%4.4f", temperature);
    sprintf(vccc, "%4.4f", vcc);
    sprintf(txbi, "%4.4f", tx_bias);
    sprintf(optx, "%4.4f", optical_tx);
    sprintf(oprx, "%4.4f", optical_rx);
    sprintf(rwtx, "%6.6f", RAW_tx);
    sprintf(rwrx, "%6.6f", RAW_rx);
    
    //Appends DDM Data into a new row of a csv file
    //focat=fopen("fcatdata.csv", "a");
    fprintf(focat, "%s,%s,%s,%s,%s,%s,%s\n",temp,vccc,txbi,optx,oprx,rwtx,rwrx);
    //fclose(focat);

    }
        
    fclose(focat);
    return 0;
    }

当我将代码设置为在进入循环之前打开 .csv 文件时,我在第 1020 次迭代时收到以下错误:

SFP Temperature = 31.9258C

Vcc, Internal supply = 3.1374V

TX bias current = 8.0540mA

Tx, Optical Power = -1.8006 dBm, 0.660600 mW

Rx, Optical Power = -40.0000 dBm, 0.000100 mW

Unable to open I2C device: Too many open files

当我更改代码底部的注释时,它的内容如下:

//Appends DDM Data into a new row of a csv file
focat=fopen("fcatdata.csv", "a");
fprintf(focat, "%s,%s,%s,%s,%s,%s,%s\n",temp,vccc,txbi,optx,oprx,rwtx,rwrx);
fclose(focat);

然后还注释掉循环之前打开的文件,随后在第 1021 次循环迭代中出现以下错误:

SFP Temperature = 31.8906C

Vcc, Internal supply = 3.1372V

TX bias current = 8.0620mA

Tx, Optical Power = -1.8006 dBm, 0.660600 mW

Rx, Optical Power = -40.0000 dBm, 0.000100 mW

Segmentation fault

我认为这在某种程度上与 ulimit - n 显示 1024 的结果有关,但我需要能够连续 运行 这个脚本一周,因此更改 ulimit 不是真正的问题的解决方案。

我通过制作一个脚本来测试这个理论,该脚本无限循环并将​​整数 i 附加到一个 csv 文件并且远远超过 1021 行数据。这已经困扰我一个星期了。感谢您的帮助。

欢迎批评格式等问题,我不经常post在这里(或任何地方)


int read_eeprom(unsigned char address)
{
    int xio,i,fd1;
    xio = wiringPiI2CSetup (address);
    if (xio < 0) 
    {
        fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                 strerror (errno));
        return 1;
    }
    for(i=0; i <128; i++) 
        {

        fd1 = wiringPiI2CReadReg8 (xio,i);
        if  (address == 0x50) 
            {
                A50[i] = fd1;
            }
        else 
            {
                A51[i] = fd1;
            }
        if (fd1 <0) 
            {
                fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                         address, strerror (errno));
                return 1;
            }
        }
    return 0;
}

编辑1: 阐明了文件打开和关闭的两种情况

编辑 2: 添加了有关 read_eeprom

内容的信息

编辑3:通过在read_eeprom

末尾添加close(fp);解决

编辑 4: 通过在 read_eeprom 末尾添加 close(xio); 正确地解决了 - 感谢@约翰

在此过程中您只需调用wiringPiI2CSetup()一次。一种方法可以通过为 xio 使用静态变量来实现,这样它将在调用之间保留值:

int read_eeprom(unsigned char address)
{
    int i, value;
    static int xio = -1;

    if( xio == -1 ) {
       xio = wiringPiI2CSetup (address);
       if (xio < 0) {
           fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                    strerror (errno));
           return 1;
       }
    }

    for(i=0; i <128; i++) {
        value = wiringPiI2CReadReg8 (xio,i);
        if( value > 0 ) {
            if (address == 0x50)
                A50[i] = value;
            else 
                A51[i] = value;
        }
        else {
            fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                     address, strerror (errno));
            return 1;
        }
    }
    return 0;
}

另一种方法是每次进入例程时调用 wiringPiI2CSetup(),然后在每次调用之间关闭它:

int read_eeprom(unsigned char address)
{
    int xio, i, value;
    xio = wiringPiI2CSetup (address);
    if (xio < 0) {
       fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                strerror (errno));
       return 1;
    }

    for(i=0; i <128; i++) {
        value = wiringPiI2CReadReg8 (xio,i);
        if( value > 0 ) {
            if (address == 0x50)
                A50[i] = value;
            else 
                A51[i] = value;
        }
        else {
            fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                     address, strerror (errno));
            close(xio);
            return 1;
        }
    }
    close(xio);
    return 0;
}

通过在read_eeprom末尾添加close(fd1);解决,如下:

int read_eeprom(unsigned char address)
{
    int xio,i,fd1;
    xio = wiringPiI2CSetup (address);
    if (xio < 0) 
    {
        fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                 strerror (errno));
        return 1;
    }
    //loop through addresses and extract data from eeprom
    for(i=0; i <128; i++) 
        {
        fd1 = wiringPiI2CReadReg8 (xio,i);
        if  (address == 0x50) 
            {
                A50[i] = fd1;
            }
        else 
            {
                A51[i] = fd1;
            }
        if (fd1 <0) 
            {
                fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                         address, strerror (errno));
                return 1;
            }
        }
    //close fd1 to prevent segfault and "too many files open" errors
    close(fd1);
    return 0;
}

感谢@Mat 识别 FD 泄漏