Wywołania systemowe wejścia-wyjścia w C | Twórz, otwieraj, zamykaj, czytaj, zapisuj

Wywołania systemowe wejścia-wyjścia w C | Twórz, otwieraj, zamykaj, czytaj, zapisuj

Wywołania systemowe to wywołania wykonywane przez program do jądra systemu w celu zapewnienia usług, do których program nie ma bezpośredniego dostępu. Na przykład zapewnienie dostępu do urządzeń wejściowych i wyjściowych, takich jak monitory i klawiatury. Możemy używać różnych funkcji dostępnych w języku programowania C dla wywołań systemowych wejścia/wyjścia, takich jak tworzenie, otwieranie, odczytywanie, zapisywanie itp.

Zanim przejdziemy do wywołań systemowych we/wy, musimy poznać kilka ważnych terminów.

Ważna terminologia

Co to jest deskryptor pliku?

Deskryptor pliku jest liczbą całkowitą, która jednoznacznie identyfikuje otwarty plik procesu.

Tabela deskryptorów plików: plik tablica deskryptorów to zbiór indeksów tablicy liczb całkowitych, które są deskryptorami plików, w których elementy są wskaźnikami do wpisów tabeli plików. Dla każdego procesu w systemie operacyjnym dostępna jest jedna unikalna tabela deskryptorów plików.

Wpis w tabeli plików: Wpisy w tabeli plików to struktura znajdująca się w pamięci, zastępująca otwarty plik, tworzona podczas przetwarzania żądania otwarcia pliku, a wpisy te utrzymują pozycję pliku.

Wpis do tabeli plików w C

Standardowe deskryptory plików : Kiedy rozpoczyna się dowolny proces, tablica deskryptorów plików tego procesu fd(deskryptor pliku) 0, 1, 2 otwiera się automatycznie (domyślnie) każde z tych 3 fd odwołuje się do wpisu tabeli plików dla pliku o nazwie /dev/tty

/dev/tty : Surogat w pamięci terminala.

Terminal : Połączenie klawiatury i ekranu wideo.

Standardowe deskryptory plików

Czytaj ze stdin => czytaj z fd 0 : Ilekroć zapiszemy dowolny znak z klawiatury, odczytuje on ze standardowego wejścia do fd 0 i zapisuje do pliku o nazwie /dev/tty.
Zapisz na standardowe wyjście => napisz na fd 1 : Ilekroć widzimy jakiekolwiek wyjście na ekranie wideo, pochodzi ono z pliku o nazwie /dev/tty i jest zapisywane na standardowe wyjście na ekranie do fd 1.
Napisz do stderr => napisz do fd 2 : Widzimy jakiś błąd na ekranie wideo, jest to również zapis z tego pliku na stderr na ekranie do fd 2.

Wywołania systemowe wejścia/wyjścia

Zasadniczo istnieje pięć typów wywołań systemowych we/wy:

1. C utwórz

Funkcja create() służy do utworzenia nowego, pustego pliku w C. Za pomocą funkcji create() możemy określić uprawnienia i nazwę pliku, który chcemy utworzyć. Jest to określone wewnątrz plik nagłówkowy, a flagi przekazywane jako argumenty są zdefiniowane w środku plik nagłówkowy.

Składnia create() w C

int   create  (char *  filename  , mode_t   mode  ); 

Parametr

  • Nazwa pliku: nazwa pliku, który chcesz utworzyć
  • tryb: wskazuje uprawnienia nowego pliku.

Wartość zwracana

  • zwróć pierwszy nieużywany deskryptor pliku (zwykle 3 podczas pierwszego tworzenia użycia w procesie, ponieważ 0, 1, 2 fd są zarezerwowane)
  • zwróć -1, gdy wystąpi błąd

Jak C create() działa w systemie operacyjnym

  • Utwórz nowy pusty plik na dysku.
  • Utwórz wpis w tabeli plików.
  • Ustaw pierwszy nieużywany deskryptor pliku, aby wskazywał wpis tabeli plików.
  • Zwróć użyty deskryptor pliku, -1 w przypadku niepowodzenia.

2. C otwarte

Funkcja open() w C służy do otwierania pliku do odczytu, zapisu lub obu. Jest również w stanie utworzyć plik, jeśli nie istnieje. Jest to określone wewnątrz plik nagłówkowy, a flagi przekazywane jako argumenty są zdefiniowane w środku plik nagłówkowy.

Składnia open() w C

int   open   (const char*   Path  , int   flags  ); 

Parametry

  • Ścieżka: Ścieżka do pliku, który chcemy otworzyć.
    • Użyj absolutna ścieżka zaczynając od / kiedy jesteś nie pracując w tym samym katalogu jako plik źródłowy C.
    • Używać względna ścieżka dostępu która jest tylko nazwą pliku z rozszerzeniem, jeśli tak jest pracując w tym samym katalogu jako plik źródłowy C.
  • flagi: Służy do określenia sposobu otwarcia pliku. Możemy użyć następujących flag.

Flagi

Opis

O_RDONLY Otwiera plik w trybie tylko do odczytu.
O_WRONLY Otwiera plik w trybie tylko do zapisu.
O_RDWR Otwiera plik w trybie odczytu i zapisu.
O_UTWÓRZ Utwórz plik, jeśli nie istnieje.
O_WYKŁ Zapobiegaj tworzeniu, jeśli już istnieje.
O_ DOŁĄCZ Otwiera plik i umieszcza kursor na końcu zawartości.
O_ASYNC Włącz sterowanie wejściem i wyjściem za pomocą sygnału.
O_CLOEXEC Włącz tryb zamykania przy wykonywaniu w otwartym pliku.
O_NONBLOKU Wyłącza blokowanie otwieranego pliku.
O_TMPFILE Utwórz nienazwany plik tymczasowy w określonej ścieżce.

Jak C open() działa w systemie operacyjnym

  • Znajdź istniejący plik na dysku.
  • Utwórz wpis w tabeli plików.
  • Ustaw pierwszy nieużywany deskryptor pliku, aby wskazywał wpis tabeli plików.
  • Zwróć użyty deskryptor pliku, -1 w przypadku niepowodzenia.

Przykład C open()

C




// C program to illustrate> // open system call> #include> #include> #include> #include> extern> int> errno> ;> int> main()> {> > // if file does not have in directory> > // then file foo.txt is created.> > int> fd = open(> 'foo.txt'> , O_RDONLY | O_CREAT);> > printf> (> 'fd = %d '> , fd);> > if> (fd == -1) {> > // print which type of error have in a code> > printf> (> 'Error Number % d '> ,> errno> );> > // print program detail 'Success or failure'> > perror> (> 'Program'> );> > }> > return> 0;> }>

Wyjście

fd = 3 

3. C zamknij

Funkcja close() w C informuje system operacyjny, że skończyłeś z deskryptorem pliku i zamyka plik wskazany przez deskryptor pliku. Jest to określone wewnątrz plik nagłówkowy.

Składnia funkcji close() w C

int close(int fd); 

Parametr

  • fd: F ile deskryptor pliku, który chcesz zamknąć.

Wartość zwracana

  • 0 na sukces.
  • -1 na błędzie.

Jak C Close() działa w systemie operacyjnym

  • Zniszcz wpis tabeli plików, do którego odwołuje się element fd tabeli deskryptorów plików
    – O ile żaden inny proces na to nie wskazuje!
  • Ustaw element fd tabeli deskryptorów plików na ZERO

Przykład 1: zamknij() w C

C




// C program to illustrate close system Call> #include> #include> #include> int> main()> {> > int> fd1 = open(> 'foo.txt'> , O_RDONLY);> > if> (fd1 <0) {> > perror> (> 'c1'> );> > exit> (1);> > }> > printf> (> 'opened the fd = % d '> , fd1);> > // Using close system Call> > if> (close(fd1) <0) {> > perror> (> 'c1'> );> > exit> (1);> > }> > printf> (> 'closed the fd. '> );> }>

Wyjście

opened the fd = 3 closed the fd. 

Przykład 2:

C




// C program to illustrate close system Call> #include> #include> int> main()> {> > // assume that foo.txt is already created> > int> fd1 = open(> 'foo.txt'> , O_RDONLY, 0);> > close(fd1);> > > // assume that baz.tzt is already created> > int> fd2 = open(> 'baz.txt'> , O_RDONLY, 0);> > > printf> (> 'fd2 = % d '> , fd2);> > exit> (0);> }>

Wyjście

fd2 = 3 

Tutaj w tym kodzie najpierw zwracana jest metoda open(). 3 ponieważ kiedy tworzony jest proces główny, wówczas fd 0, 1, 2 są już zajęte stdin , standardowe wyjście, I stderr . Zatem pierwszym nieużywanym deskryptorem pliku jest 3 w tabeli deskryptorów plików. Następnie w funkcji Close() wywołanie systemowe jest wolne 3 deskryptory plików, a następnie ustaw 3 deskryptory plików jako zero . Kiedy więc wywołaliśmy drugą metodę open(), wówczas pierwsza nieużywana funkcja fd również zostanie wywołana 3 . Zatem wyjściem tego programu jest 3 .

4. C czytaj

Z pliku wskazanego przez deskryptor pliku fd funkcja read() odczytuje określoną ilość bajtów cnt danych wejściowych do obszaru pamięci wskazanego przez buf . Pomyślne read() aktualizuje czas dostępu do pliku. Funkcja read() jest również zdefiniowana w pliku nagłówkowym.

Składnia read() w C

size_t   read   (int   fd  , void*   buf  , size_t   cnt  ); 

Parametry

  • fd: deskryptor pliku, z którego mają zostać odczytane dane.
  • buf: bufor, z którego można odczytać dane
  • cnt: długość bufora

Wartość zwracana

  • return Liczba bajtów odczytanych w przypadku powodzenia
  • zwróć 0 po osiągnięciu końca pliku
  • zwróć -1 w przypadku błędu
  • zwróć -1 w przypadku przerwania sygnału

Ważne punkty

  • buf musi wskazywać prawidłową lokalizację pamięci o długości nie mniejszej niż określony rozmiar z powodu przepełnienia.
  • fd powinien być poprawnym deskryptorem pliku zwróconym przez open() w celu wykonania operacji odczytu, ponieważ jeśli fd ma wartość NULL, wówczas odczyt powinien wygenerować błąd.
  • cnt to żądana liczba odczytanych bajtów, a zwracana wartość to rzeczywista liczba odczytanych bajtów. Ponadto czasami funkcja systemowa read powinna odczytać mniej bajtów niż cnt.

Przykład read() w C

C




// C program to illustrate> // read system Call> #include> #include> #include> int> main()> {> > int> fd, sz;> > char> * c = (> char> *)> calloc> (100,> sizeof> (> char> ));> > fd = open(> 'foo.txt'> , O_RDONLY);> > if> (fd <0) {> > perror> (> 'r1'> );> > exit> (1);> > }> > sz = read(fd, c, 10);> > printf> (> 'called read(% d, c, 10). returned that'> > ' %d bytes were read. '> ,> > fd, sz);> > c[sz] => ' '> ;> > printf> (> 'Those bytes are as follows: % s '> , c);> > return> 0;> }>

Wyjście

called read(3, c, 10). returned that 10 bytes were read. Those bytes are as follows: 0 0 0 foo. 

Załóżmy, że foobar.txt składa się z 6 znaków ASCII foobar. Jaki jest zatem wynik poniższego programu?

C




// C program to illustrate> // read system Call> #include> #include> #include> #include> int> main()> {> > char> c;> > int> fd1 = open(> 'sample.txt'> , O_RDONLY, 0);> > int> fd2 = open(> 'sample.txt'> , O_RDONLY, 0);> > read(fd1, &c, 1);> > read(fd2, &c, 1);> > printf> (> 'c = %c '> , c);> > exit> (0);> }>

Wyjście

c = f 

Deskryptory fd1 I fd2 każdy ma swój własny otwarty wpis w tabeli plików, więc każdy deskryptor ma własną pozycję w pliku foobar.txt . Zatem odczyt z fd2 czyta pierwszy bajt foobar.txt , a wyjście jest do = f , nie do = o .

5. Napisz C

Zapisuje cnt bajtów z buf do pliku lub gniazda powiązanego z fd. cnt nie powinno być większe niż INT_MAX (zdefiniowane w pliku nagłówkowym limity.h). Jeśli cnt wynosi zero, write() po prostu zwraca 0, nie podejmując żadnej innej akcji.

Funkcja write() jest również zdefiniowana wewnątrz plik nagłówkowy.

Składnia write() w C

size_t   write   (int   fd  , void*   buf  , size_t   cnt  ); 

Parametry

  • fd: deskryptor pliku
  • buf: bufor, z którego będą zapisywane dane.
  • cnt: długość bufora.

Wartość zwracana

  • zwraca liczbę bajtów zapisanych w przypadku powodzenia.
  • zwróć 0 po osiągnięciu końca pliku.
  • zwróć -1 w przypadku błędu.
  • zwraca -1 w przypadku przerwania sygnału.

Ważne uwagi dotyczące zapisu w języku C

  • Aby móc wykonać operacje zapisu, plik musi zostać otwarty
  • buf musi być co najmniej tak długi, jak określono w cnt, ponieważ jeśli rozmiar buf jest mniejszy niż cnt, wówczas buf doprowadzi do warunku przepełnienia.
  • cnt to żądana liczba bajtów do zapisania, natomiast wartość zwracana to rzeczywista liczba zapisanych bajtów. Dzieje się tak, gdy fd ma mniejszą liczbę bajtów do zapisania niż cnt.
  • Jeśli funkcja write() zostanie przerwana przez sygnał, efekt będzie jeden z poniższych:
    • Jeśli funkcja write() nie zapisała jeszcze żadnych danych, zwraca -1 i ustawia errno na EINTR.
    • Jeśli funkcja write() pomyślnie zapisała jakieś dane, zwraca liczbę bajtów, które zapisała przed przerwaniem.

Przykład write() w C

C




// C program to illustrate> // write system Call> #include> #include> main()> {> int> sz;> int> fd = open(> 'foo.txt'> , O_WRONLY | O_CREAT | O_TRUNC, 0644);> if> (fd <0)> {> > perror> (> 'r1'> );> > exit> (1);> }> sz = write(fd,> 'hello geeks '> ,> strlen> (> 'hello geeks '> ));> printf> (> 'called write(% d, 'hello geeks ', %d).'> > ' It returned %d '> , fd,> strlen> (> 'hello geeks '> ), sz);> close(fd);> }>

Wyjście

called write(3, 'hello geeks
', 12). it returned 11 

Tutaj, gdy zobaczysz w pliku foo.txt po uruchomieniu kodu, otrzymasz cześć maniakom . Jeśli plik foo.txt zawiera już jakąś treść, wywołania systemowe write a nadpiszą tę zawartość, a cała poprzednia zawartość zostanie usunięte i tylko cześć maniakom zawartość będzie znajdować się w pliku.

Przykład: Wydrukuj hello world z programu bez użycia funkcji printf.

C




// C program to illustrate> // I/O system Calls> #include> #include> #include> #include> int> main(> void> )> {> > int> fd[2];> > char> buf1[12] => 'hello world'> ;> > char> buf2[12];> > // assume foobar.txt is already created> > fd[0] = open(> 'foobar.txt'> , O_RDWR);> > fd[1] = open(> 'foobar.txt'> , O_RDWR);> > write(fd[0], buf1,> strlen> (buf1));> > write(1, buf2, read(fd[1], buf2, 12));> > close(fd[0]);> > close(fd[1]);> > return> 0;> }>

Wyjście

hello world 

W tym kodzie ciąg znaków tablicy buf1 Witaj świecie jest najpierw zapisywany na stdin fd[0], a następnie ten ciąg znaków jest zapisywany na stdin do tablicy buf2. Następnie zapisz do tablicy buf2 na standardowe wyjście i wydrukuj Witaj świecie .