Linux file IO operation

File operation

Before operating Linux files, let's take a brief look at the Linux file system

Linux file type

File types in Linux are divided into the following types:

Symbol

file type

-

Ordinary file

d

Directory file, d is the abbreviation of directory

l

Soft link file, also known as symbolic link file, s is short for soft or symbolic

b

Block file is one kind of device file (and another), and b is the abbreviation of block

c

Character file is also a kind of device file (this is the second kind), and c is the character file

s

Socket file, which is used for network communication between processes

p

Pipeline file, which is used for communication between processes

How to judge the file type?

View file types

$ ls -l
total 8
drwxrwxr-x 2 ubuntu ubuntu 4096 Oct 25 15:30 dir
prw-rw-r-- 1 ubuntu ubuntu    0 Oct 25 15:30 FIFOTEST
-rw-rw-r-- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c
lrwxrwxrwx 1 ubuntu ubuntu    6 Oct 25 15:28 main.c-temp -> main.c
srwxrwxr-x 1 ubuntu ubuntu    0 Oct 25 15:24 test.sock

ls -l the first letter represents the file type

Linux file permissions

File permission is the access control permission of the file. Those users and groups can access the file and what operations they can perform

View file permissions

View file permissions

File types are followed by file permissions

Briefly introduce the following file permissions, as shown in the following figure:

Because Linux is a multi-user login operating system, file permissions are related to users.

Modify file permissions

1. Modify file permissions in binary form

What is binary form? Take the permission of main.c as an example

-rw-rw-r-- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c

The permission of the file is rw-rw-r -- and the corresponding binary is 664. How to calculate it? See the table below

readable

Writable

Executable

Character representation

r

w

x

Digital representation

4

2

1

The owner's permission is rw-, corresponding to 4 + 2 + 0, that is, the final permission is 6, and so on. The permission of the user group is 6 and that of other users is 4

The chmod command is required to modify file permissions, as shown below

$ ls -l
-rw-rw-r-- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c
$ chmod 666 main.c
$ ls -l
-rw-rw-rw- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c

Don't make a mistake in binary calculation

2. Modify file permissions by adding and subtracting assignment

Still use the chmod command and start directly

$ ls -l
-rw-rw-rw- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c
$ chmod o-w main.c
$ ls -l
-rw-rw-r-- 1 ubuntu ubuntu    2 Oct 25 15:25 main.c

File owner user

File group user group

other users

u

g

o

+And - respectively indicate adding and removing corresponding permissions

After simply understanding the file operation under Linux, you begin to enter the code programming stage

Linux error

Gets the error description at the time of the system call

The file operation under Linux belongs to system call. The errors of system call in Linux are stored in errno. For example, if the file does not exist, errno is set to 2, that is, the macro definition ENOENT, and the corresponding error description is No such file or directory.

strerror is required to print the error description of system call. It is defined as follows

#include <string.h>
char *strerror(int errnum);

To view the meaning of all errno s in the system, you can use the following code:

/* Function: obtain the errno string
*   char *strerror(int errno)
*/
#include <stdio.h>
#include <string.h>     //for strerror()
//#include <errno.h>
int main()
{
    int tmp = 0;
    for(tmp = 0; tmp <=256; tmp++)
    {
        printf("errno: %2d\t%s\n",tmp,strerror(tmp));
    }
    return 0;
}

You can run it manually to see the output effect.

Print error message

As mentioned earlier, the errors of Linux system calls are stored in errno, which is defined as follows

#include <errno.h>
int errno;

In addition to strerror's ability to output error descriptions, perror can also, and is more convenient

Print the system error message perror. The function prototype and header file are defined as follows

#include <stdio.h>
void perror(const char *s);

Use example:

/**
 * @brief The file does not exist. Print the error description when opening fails
 */
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h> //strerror
#include <errno.h>  //errno

int main() {
    int fd = open("test.txt", O_RDWR);
    if(fd == -1) {
        perror("open");
        //printf("open: %s\n",strerror(errno));
    }
    close(fd);
    return 0;
}

When the file test.txt does not exist, print as follows

./main 
open: No such file or directory

System IO function

There are two systems for C to read and write binary stream files in UNIX Environment:

  1. fopen,fread,fwrite ;
  2. open, read, write;

fopen series is a standard C library function;

The open series is defined by POSIX and is the system call in UNIX system.

File operations are nothing more than open, close, read, write and lseek. Start with opening a file

open/close

open is defined as follows

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

-pathname: the path of the file to open

-flags: there are other settings for file operation permissions

O_RDONLY, O_WRONLY, O_RDWR these three settings are mutually exclusive

-mode: octal number, indicating the operation permission of the created new file, for example: 0775

close is defined as follows

#include <unistd.h>
int close(int fd);
Open file

Open an existing file through open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open("test.txt", O_RDONLY);// Open a file
    if(fd == -1) {
        perror("open");
    }
    // Read / write operation
    close(fd); // close
    return 0;
}

If the file exists, open the file; File does not exist, open failed, error description is

No such file or directory.

create a file

Create a new file through open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
    // Create a new file
    int fd = open("test.txt", O_RDWR | O_CREAT, 0777);
    if(fd == -1) {
        perror("open");
    }
    // close
    close(fd);
    return 0;
}

Here's something to note. Let's look at the output first

$ ls -l test.txt
-rwxrwxr-x 1 dengzr dengzr    0 Oct 27 19:50 test.txt

File permissions are given when creating files. File permissions have been described in Linux file permissions above

When creating a file, the assigned permission is 777, but the created file permission is 775, which is what we need to pay attention to.

In linux system, when we create a new file or directory, these new files or directories will have default access rights. The default access permissions are viewed through the command umask.

$ umask
0002

The final permission for users to create files is mode & ~ umask.

For example, the file permission mode created in Demo = 0777, so the final permission is 0775

 777 -> 111111111 
~002 -> 111111101 &
 775 -> 111111101

Modify default access rights

To change the umask value, you can change the umask value by commanding umask mode. For example, if I want to change the umask value to 000, I can use the command umask 000.

$ umask 
0002
$ umask 000
$ umask 
0000

Delete test.txt and rerun the program creation

$ ./create 
$ ls -l test.txt 
-rwxrwxrwx 1 dengzr dengzr 0 Oct 27 20:06 test.txt

ps: this method does not permanently change the umask value, but changes the umask value of the current session. Open a new shell and enter the umask command. You can see that the umask value is still the default 002. To permanently change the umask value, modify the file / etc/bashrc and add a line umask 000 to the file.

read/write

The two most basic functions of file I/O are read and write, which are also called unbuffered I/O in unix/linux programming practice tutorial.

Ubbuffered here means that read and write have no caching mechanism (no buffering in the application layer).

read is defined as follows

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

Parameters:

-fd: file descriptor

-buf: buffer to hold read data

-count: the size of the read data

Return value:

-Success:

>0: returns the actual number of bytes read =0: the file has been read

-Failed: - 1 and set errno

Simple application, example Demo

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>  //errno

int main() {
    int fd = open("test.txt", O_RDWR);
    if(fd == -1) {
        perror("open");
    }
    char buff[1024];
    memset(buff,'\0',1024);
    int readLen = read(fd,buff,1024);
    printf("readLen:%d,data:%s",readLen,buff);
    close(fd);
    return 0;
}

Demo reads the data in test.txt and displays it

$ ./main 
readLen:5,data:text

write is defined as follows

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

Parameters:

-fd: file descriptor

-buf: data block to be written

-count: the size of the written data

Return value:

-Success: bytes actually written

-Failed: Return - 1 and set errno

The Demo is as follows

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>  //errno

int main() {
    int fd = open("test.txt", O_WRONLY | O_CREAT ,0775);
    if(fd == -1) {
        perror("open");
    }
    char buf[256] = "text";
    int Len = write(fd,buf,strlen(buf));
    printf("readLen:%d,data:%s\n",Len,buf);
    // close(fd);
    return 0;
}

close is commented out in the Demo, and the data is written successfully

$ ./main 
readLen:4,data:text
$ cat test.txt 
text

Compared with standard C library functions such as fwrite, write has no buffer and does not need fflush or close to write the buffered data to the disk, but the program must be closed after open ing, which is a good programming habit.

ps: in fact, write is buffered. In the system layer that users can't see, we can understand that there is no buffer

lseek

Function: offset the file pointer

"lseek" is defined as follows:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

Parameters:

-fd: file descriptor

-Offset: offset

- whence:

SEEK_SET: sets the offset of the file pointer

SEEK_CUR: set the offset, the current position + the value of the second parameter offset

SEEK_END: set the offset, file size + the value of the second parameter offset

Return value: returns the position of the file pointer

lseek is no different from the standard C library function fseek. Let's have a brief understanding of several functions

1. Move the file pointer to the file header

lseek(fd, 0, SEEK_SET);

2. Get the position of the current file pointer

lseek(fd, 0, SEEK_CUR);

3. Get file length

lseek(fd, 0, SEEK_END);

An example Demo is as follows

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
    int fd = open("test.txt", O_RDWR);
    if(fd == -1) {
        perror("open");
        return -1;
    }
    // Gets the length of the file
    int len = lseek(fd, 0, SEEK_END);
    printf("file len:%d\n",len);
    // Close file
    close(fd);
    return 0;
}
./main 
file len:4

Standard input / output / error under linux

When I talk about file descriptors in file IO operations, I have to mention standard I / O / errors in linux

stdin, stdout and stderr, which we often see in the process of learning C language, are standard input, standard out and standard error output called terminal, which correspond to fopen / fwrite / Fred in the standard C library.

However, under Linux, the file APIs provided at the operating system level represent files with file descriptors. The corresponding standard input, standard output and standard error output are 0, 1 and 2, and the macro is defined as STDIN_FILENO,STDOUT_FILENO ,STDERR_FILENO, which corresponds to open/read/write in the system API interface library.

Standard input

In c language, it is expressed as calling scanf function to accept user input content, that is, input content from terminal devices. fscanf can also be used to indicate that stdin receives content or read accepts content. The corresponding standard input file identifier is 0 or STDIN_FILENO.

#include<stdio.h>
#include<string.h>
int main()
{
    char buf[1024];
    //Standard input in C language
    scanf("%s",buf);
    //Standard input stdin under UNIX
    fscanf(stdin,"%s",buf);
    //Operating system level STDIN_FILENO
    read(0,buf,strlen(buf));
    return 0;
}

ps: note that read cannot be used with stdin

Standard out

In c language, it is expressed as calling printf function to output the content to the terminal. Use fprintf to indicate that stdout can also output content to the terminal or wirte to the terminal. The file identifier of the corresponding standard output is 1 or STDOUT_FILENO.

#include<stdio.h>
#include<string.h>
#include <unistd.h>  
int main()
{
    char buf[1024];
    //Standard input and output in C language
    scanf("%s",buf);
    printf("buf:%s\n",buf);
    //Standard input stdin under UNIX 
    fscanf(stdin,"%s",buf);
    fprintf(stdout,"buf:%s\n",buf);
   //Operating system level STDIN_FILENO
    read(STDIN_FILENO,buf,strlen(buf)); 
    write(STDOUT_FILENO,buf,strlen(buf));
    return 0;
}

Standard error output

Like standard output, standard errors are output to the terminal. The standard error corresponding to standard C library is stderr, and the file identifier of standard error output corresponding to system API interface library is 2 or STDERR_FILENO.

#include<stdio.h>
#include<string.h>
int main()
{
    char buf[1024]="error";
    fprintf(stderr,"%s\n",buf);

    write(2,buf,strlen(buf));
    return 0;
}

Since there is standard output, why should there be standard error? What makes you bright?

A simple Demo lets you know that Zhuge is a cow

#include <stdio.h>
int main()
{
    fprintf(stdout, "stdout");
    fprintf(stderr, "stderr");
    return 0;
}

Guess whether to output stdout or stderr first. According to normal thinking, output stdout first and then stderr.

However, stderr belongs to Zhuge stream and likes to seize the first opportunity, so stderr is output first, and then stdout is output.

~Cough, cough, pull away. Actually stdout is a device, stderr is not. For a block device, it can be entered only when the following conditions are met: enter; Buffer full; flush is called. Stderr is output directly because it has no buffer.

Talk about stdin and STDIN_FILENO differences

I didn't understand before. I thought stdin was equal to 0. In fact, stdin type is FILE *; STDIN_FILENO type is int, which cannot be compared. Secondly, stdin belongs to standard I/O, advanced input and output functions, corresponding to fread, fwrite, fclose, etc; STDIN_FILENO belongs to the system API interface library, corresponding to read, write and close.

The above are my fragmentary knowledge points. Summarize the notes.

·················END·················

Author: Meng fan

Posted on Mon, 08 Nov 2021 04:38:21 -0500 by Vik