In this section we will describes functions for performing low-level input/output operations on file descriptors.
In lower level file handling we will study the basic opening, closing, reading, and writing a file through a file descriptor which acts as a pointer or cursor to a file.It is called lower level file handling because it will execute or do operations on kernel level not even can open a binary file only to read or write but can open a device also. This is the basically main difference between open and fopen call.
Difference between Lower Level File Handling and Upper Level File Handling:
In lower level file handling the call is directly made to the kernel of the OS. i.e. for example, there is a open, read, write system call to open a file or a device, so in this that call is directly made to the kernel level and hence faster and more efficient but in Upper Level File Handling there is call in the user space of the OS and actually the upper level system calls uses lower level system calls to write and read. For example, there is a fopen, fwrite and fread system calls that will call open, write and read system calls to perform actions.
But in these system calls operations are limited to user space. i.e. in fopen system call we could not open a device, here we cal open a binary file or a normal text file to write and read in it similarly with file descriptor.
Uses of lower level file handling system calls –
- For reading binary or text files .
- To open a device with a descriptor to transfer data to a external device through serially.
- To pass descriptors to a child process. (The child can create its own stream to use a descriptor that it inherits, but cannot inherit a stream directly.)
1. open system call –
The open() call is used to open a file to perform read and write operations on it and it returns a file descriptor, a small, non-negative integer for use in subsequent system calls i.e. read and write.
The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process.
Syntax :
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags,...);
path name – contains the location or name of file on which the read and write operations are to be performed.
Flags –
O_RDONLY open for reading only
O_WRONLY open for writing only
O_RDWR open for reading and writing
O_NONBLOCK do not block on open
O_APPEND append on each write
O_CREAT create file if it does not exist
O_TRUNC truncate size to 0
O_EXCL error if create and file exists
O_SHLOCK atomically obtain a shared lock
O_EXLOCK atomically obtain an exclusive lock
Return Value : It will return a file descriptor value through which we can write or read into that file. This will be illustrated by an example later.
IMPORTANT POINT TO NOTE :The file descriptor numbers 0,1,2 are reserved i.e. 0 for take input 1 for display and 2 for errors.It means that if we want to display anything on a terminal then we can simply use,write(1,”HELLO”,5);It will display HELLO on terminal. i.e. it behaves like an printf function. Here 1 is the value of file descriptor.And we want to use take an input from a user without using a scanf function the simply use read (0,buff,sizeof(buff));It will take input from user and stire that result in a buff variable.
2.write system call :
Syntax :-
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
Data Type: ssize_t
This data type is used to represent the sizes of blocks that can be read or written in a single operation. It is similar to size_t, but must be a signed type.
Here 1st argument is the file descriptor that is come from the open call after opening a file or a device and 2nd argument is the buffer name and 3rd argument is the size of that buffer or size that we want to write into a file.
On success, the number of bytes written are returned (zero indicates nothing was written). On error, -1 is returned, and errno is set appropriately.
3.read system call :
Syntax :
#include <unistd.h>
ssize_t read (int filedes, void *buffer, size_t size)
Data Type: ssize_t
This data type is used to represent the sizes of blocks that can be read or written in a single operation. It is similar to size_t, but must be a signed type.
Here 1st argument is the file descriptor that is come from the open call after opening a file or a device and 2nd argument is the buffer name and 3rd argument is the size of that buffer or size that we want to write into a file.
The read function reads up to size bytes from the file with descriptor filedes, storing the results in the buffer. (This is not necessarily a character string, and no terminating null character is added.)
The return value is the number of bytes actually read. This might be less than size; for example, if there aren’t that many bytes left in the file or if there aren’t that many bytes immediately available. The exact behavior depends on what kind of file it is. On error, -1 is returned.
4. Close system call :
Syntax :
int close (int filedescriptor)
The function close closes the file descriptor filedescriptor. Closing a file has the following consequences:
- The file descriptor is deallocated.
- Any record locks owned by the process on the file are unlocked.
When all file descriptors associated with a pipe or FIFO have been closed, any unread data is discarded.
Setting the File Position of a Descriptor
To read the current file position value from a descriptor, use lseek.
Syntax : off_t lseek (int filedes, off_t offset, int whence)
The lseek function is used to change the file position of the file with descriptor filedes.
The whence argument specifies how the offset should be interpreted, in the same way as for the fseek function, and it must be one of the symbolic constants SEEK_SET, SEEK_CUR, or SEEK_END.
SEEK_SET
Specifies that whence is a count of characters from the beginning of the file.
SEEK_CUR
Specifies that whence is a count of characters from the current file position. This count may be positive or negative.
SEEK_END
Specifies that whence is a count of characters from the end of the file. A negative count specifies a position within the current extent of the file; a positive count specifies a position past the current end. If you set the position past the current end, and actually write data, you will extend the file with zeros up to that position.
The return value from lseek is normally the resulting file position, measured in bytes from the beginning of the file. You can use this feature together with SEEK_CUR to read the current file position.
EXAMPLES : –
1.open a file with ‘open’ system call
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> int main() { int fd; char buff[50]=”Testing of File”; char temp[50]; fd=open(“test”,O_CREAT | O_RDWR); if(fd==-1) printf(“Error in creating a file\n”); else printf(“File opened successfully\n”); write(fd,buff,sizeof(buff)); read(fd,temp,sizeof(temp)); printf(“File after reading is :%s”,temp); close(fd); }
2.open a device with ‘open’ system call
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> int main() { int fd; fd=open(“/dev/ttyUSB0”, O_RDWR); if(fd==-1) printf(“Error in opening ttyUSB device\n”); else printf(“ttyUSB device is opened successfully\n”); close(fd); }