close na file deskryptorze 2 razy

0

Witam
Background:
Jeżeli otworzymy file deskryptor przed forkiem to wtedy po forku po stronie kernela w file table entry (FTE) referencja do file deskryptora zostanie zinkrementowana czyli będzie równa 2, co oznacza tyle, że offset na pliku będzie współdzielony pomiędzy procesami. Jeżeli dasz write("xx") w child procesie a w macierzystym write("yy") (przy założeniu że macierzysty czeka na childa) to w sumie będziesz miał w pliku zapisane "xxyy". Jakby były różne file deskryptory to byśmy mieli "yy".
Jestem jednak zaskoczony że wywołanie 2 razy pod rząd w przypadku wyżej w obrębie jednego procesu close() za drugim razem fejluje. Ale jak już wywołasz close() w jednym procesie a potem w drugim to za tym drugim razem nie fejluje. Widocznie po stronie kernela implementacja jest bardziej skomplikowana - nie tylko licznik referencji jest inkrementowany dla file deskryptora ale dodatkowo jeszcze pid procesu pewnie jest dodawany do numeru referencji i stąd takie zachowanie. Jak ktoś się zna na kernelu i mógłby to potwierdzić (2xclose w obrębie 1 procesu - error VS close() w obu procesach - brak errora) to byłbym wdzieczny.

#include "tests.h"

void setOffsetToStart(const int& fd)
{
    //start at the beginning of the file
    if (lseek(fd, SEEK_SET, 0) == -1)
    {
        perror("lseek error");
    }
}

void read(const int& fd, const int& size)
{
    //read data
    std::string buffer(size, '0');
    int nrOfData = read(fd, &buffer[0], size);
    if (nrOfData == -1)
    {
        perror("Data read error");
    }

    cout << "read " << nrOfData << " bytes:" << buffer << endl;
}

void write(const int& fd, const char* data, const int& size)
{
    //read data
    std::string buffer(size, '0');
    int nrOfData = write(fd, (void *)data, size);
    if(nrOfData == -1)
    {
        perror("Data write error");
    }

    cout << "write " << nrOfData << " bytes:" << data << endl;
}

void FileOpenTest()
{
#ifdef LINUX_USED
    cout << "FileOpenTest()" << endl;
    const char* fileToOpen = "/media/sf_share/mmapTestFile.txt";
    int fd = open(fileToOpen, O_RDWR);
    if (fd == -1)
    {
        perror("File %s not opened succesfully");
        exit(EXIT_FAILURE);
    }

    read(fd, 4);
    setOffsetToStart(fd);
    write(fd, "dupadupa", 4);
    setOffsetToStart(fd);
    read(fd, 4);

    pid_t pid = fork();
    if(pid == 0)
    {
        setOffsetToStart(fd);
        write(fd, "ChildProcess", 12);
        cout << "Child fd = " << fd << endl; 
        if(close(fd) == -1)
        {
            perror("close file error");
        }
        cout << "Child fd = " << fd << endl; 
        if(close(fd) == -1)
        {
            perror("close file error"); //to otrzymuje
        }
        exit(0);
    }
    else //macierzysty proces
    {
        //wait for child exit
        int status = 0;
        pid = wait(&status);
        cout << "Parent fd = " << fd << endl; 
        read(fd, 12);
    }

    if(close(fd) == -1) //when process terminates OS do this for you, however it is good practise to close fd
    {
        perror("close file error"); //tego nie otrzymuje
    }
#endif
}

Output programu:

FileOpenTest()
read 4 bytes:Chil
write 4 bytes:dupadupa
read 4 bytes:dupa
write 12 bytes:ChildProcess
Child fd = 3
Child fd = 3
close file error: Bad file descriptor
Parent fd = 3
read 12 bytes:a b c d e f
0

W zasadzie opisałeś zachowanie zgodne z intuicją i istotą deskryptora pliku. Zauważ, że deskryptory są czymś lokalnym w stosunku do procesu, tzn. nawet jeśli robisz fork(), to każdy z procesów ma własny deskryptor. Stąd można niezależnie je zamykać w każdym z nich. Natomiast wywołanie pod rząd dwa razy close na tym samym deskryptorze w obrębie jednego procesu poskutkuje informacją za drugim razem, że FD jest nieprawidłowy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1