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.
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.
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 kiedy wynaleziono pierwszy komputer | 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
lista połączona w Javie
Powiedziała Madhuri
// 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 .