Linux basic IO-system call interface open/read, etc. & file system & soft and hard link & static/dynamic library

1. Practice file-related system call interfaces such as open/read/write/close, and compare fd and FILE structures vertically

1. open function

(1) Function prototype
(2) Function function

        It is used to open or create a file. When opening or creating a file, you can specify the attributes of the file and the permissions of the user. If the target file does not exist, when the file needs to be created, use the open function with three parameters, otherwise, use the open function with two parameters.
(3) Parameters
        1) pathname: the target file to be opened or created
        2) flags: When opening a file, you can pass in multiple parameter options, and use one or more of the following constants to perform an " OR " operation to form flags:
                    O_RDONLY : read-only open;
                    O_WRONLY : open for write only;
                    O_RDWR: open for reading and writing; ( these three constants, one must be specified )
                    O_CREAT : If the file does not exist, create it; (requires the mode option to indicate the access rights of the new file)
                    O_APPEND : Append write.
        3) mode: the user's access authority: it can be represented by R/W/X, or it can be represented by octal
(4) Return value:
        Returns the file descriptor of the open file on success; -1 on failure.

2. read function
(1) Function prototype
(2) Function function
        Read data from a file.
(3) Parameters

        1) fd: the file descriptor of the file to read data from;
        2) buf: refers to the buffer, there must be a buffer to receive the read data;
        3) count: Indicates how many characters should be read when calling read once.

(4) Return value:
        Returns the number of characters read: 0 means read EOF, -1 means error.
3. write function
(1) Function prototype
(2) Function function
        write content to the target file
(3) Parameters

        1) fd: the file descriptor of the file to be written;
        2) buf: the content to be written to the file;
        3) count: how much content is written.
(4) Return value

        Returns the number of characters written to the file on success; -1 on failure.
4. close function

(1) Function prototype
(2) Function function
        Close open files.
(3) Function parameters
         fd : The file descriptor of the file to close.
(4) Return value
        0 for success; -1 for error.
5. Use the above functions to write files
Write the code as follows:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

intmain()
{
    umask(0);//Close reading
    int fd = open("myfile", O_WRONLY|O_CREAT,0644);
    if(fd < 0)//Open failed
    {   
        perror("open");
        exit(1);
    }   
    int count = 5;
    const char* msg = "hello may!\n";
    while(count--)
    {   
        write(fd, msg ,strlen(msg));//Write a string to the file without writing the end mark of the string '\0'
    }   
    close(fd);
    return 0;
}
The results are as follows:
Among them, the myfile file is the file created by the program, and the five "hello may!" are also the data written by the program.

6. Use the above functions to read data from the file
Write the code as follows:

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

intmain()
{
    umask(1);//Close writing
    int fd = open("myfile", O_RDONLY);
    if(fd < 0)//Open failed
    {   
        perror("open");
        exit(1);
    }   
    const char* msg = "hello may!\n";
    char buf[1024];
    while(1)
    {   
        size_t ret = read(fd, buf ,strlen(msg));//类似write
        if(ret < 0)//read failed
        {   
            perror("read");
            break;
        }   
        else if(ret == 0)//end of file
        {   
            break;
        }   
        else
        {   
            buf[ret] = '\0';//After reading the content of the file, add an end mark to it
            printf("%s\n",buf);
        }   
    }   
    close(fd);
    return 0;
}

The results are as follows:

8. Vertically compare fd and FILE structures
(1) File descriptor fd
        Through the above code, we can know that the file descriptor (file descriptor) is a small integer starting from 0.
        Normally, after a program is loaded from the hard disk into memory, the program becomes a process. By default, a Linux process will have 3 default open file descriptors: 0 standard input (stdin), 1 standard output (stdout), 2 standard error (stderr), and the corresponding physical devices are generally: keyboard, monitor, monitor. The three file descriptors corresponding to these three files are 0, 1, and 2, respectively. So when a new file is created later, the file descriptor of the new file cannot be 0, 1, 2. So in Linux, the allocation of file descriptors starts from 3, and allocates from the current smallest and unallocated file descriptor.
        When we open a file, the OS has to create a corresponding data structure in memory to describe the target file. So there is a file structure to represent the opened file object. The process needs to perform operations such as open, so it is necessary to associate the process with the file. So each process has a pointer *files, pointing to a table files_struct. The most important thing about this table is that it contains an array of pointers, each of which is a pointer to an open file. Therefore, the file descriptor is essentially the subscript of the array, which also explains that the file descriptor will be a small integer. So holding the file descriptor, you can find the corresponding file. (see picture below)
(2) FILE structure
         In the stdio.h header file of the C language, the structure FILE for file operations is defined, so we can perform file operations by returning a file pointer (pointer to the FILE structure) through fopen.
         The two most important member variables in the FILE structure are: the file descriptor and the size of the buffer. The buffering of the C library is divided into three categories: no buffering, line buffering, and full buffering.
         Because IO-related functions correspond to system call interfaces, and library functions encapsulate system calls. So in essence, accessing files is accessed through fd. Therefore, the FILE structure in the C library must encapsulate fd.
        Some members of the FILE structure:
struct FILE
{
    char *_ptr;//The next position of the file input
    int _cnt;//The relative position of the current buffer
    char *_base;//Refers to the base position (the starting position of the file)
    int _flag;//File flag
    int _file;//Validation of the file
    int _charbuf;//Check the buffer status, if the buffer is not read
    int _bufsiz;//The size of the file
    char *_tmpfname;//Temporary file name
};

        Write a piece of code as follows:
#include <stdio.h>
#include <string.h>

intmain()
{
    const char* msg0 = "hello printf\n";
    const char* msg1 = "hello fwrite\n";
    const char* msg2 = "hello write\n";
    
    printf("%s",msg0);
    fwrite(msg1,strlen(msg0),1,stdout);
    write(1,msg2,strlen(msg2));
    
    fork();
    return 0;
}
The results are as follows:
As you can see, there are only three statements when outputting the program results to the screen; but when writing the results to a file, there are five statements. The reason is:

    1) Generally, the C library function writes to the file fully buffered (when the buffer is slow or the process exits), while writing to the screen is line buffered;
    2) The printf and fwrite functions have their own buffers, and the write function has no buffers;
    3) So when writing to the file, it is line buffered, and the data placed in the buffer will not be flushed immediately, even after fork. When the parent process refreshes the data in the buffer after fork, the child process also has a copy of the same data, and randomly generates two copies of the data. So a total of five pieces of data are written to the file.
2. Write a simple add/sub/mul/div function, package it into a static/dynamic library, and use it separately
        Only the add function and static/dynamic library are implemented here, other functions are the same


(1) Implement a simple function first and test whether the code is correct

add.h (addition)
#pragma once

int add(int a, int b);

add.c
#include "add.h"

int add(int a, int b)
{
    return a+b;
}

sub.h (subtraction)
#pragma once

int sub(int a, int b);

sub.c
#include "sub.h"

int sub(int a, int b)
{
    return a-b;
}

mul.h (multiplication)
#pragma once

int mul (int a, int b);

mul.c
#include "mul.h"//Multiplication

int mul (int a, int b)
{
    return a*b;
}

div.h (divide)
#pragma once

int div(int a, int b);

div.c
#include "div.h"

int div(int a, int b)
{
    return a/b;
}
main.c (test function)
 
 
#include <stdio.h>#include "add.c"int main(){ int a = 10; int b = 20; printf("add(10+20) = %d\n",add(10,20));
    printf("sub(20+10) = %d\n",sub(20,10));    printf("mul(20+10) = %d\n",mul(20,10));    printf("div(20+10) = %d\n",div(20,10)); return 0;}

The results are as follows:
(2) Generate static library

        Because the static library is compiled and linked, the code of the library is linked into the executable file, so the static library is no longer needed when the program is running. So after generating the executable file, delete the static library, and the program runs the same. The specific implementation is as follows:

(3) Generate dynamic library

(1) Generation of dynamic library
(2) There are three ways to use the dynamic library

Compile options:
        l : Link dynamic library (as long as the library name is sufficient)
        L : The path where the link library is located
        It can be seen that the method of linking the static library does not use the dynamic library. And the dynamic library is only linked when the program is running, and multiple programs share the code of the library, so after the executable file is generated, if the linked library is deleted, the executable program cannot be run.
 There are three ways to use dynamic libraries:
        1) Copy the .so dynamic library file to the system shared library path, generally referring to /usr/lib
Learn a command ldd here, you can view the dynamic library that the application depends on

         2) Change LD_LIBRARY_PATH

Some little cuties may encounter such problems:
It can be solved with the following statement:

        This operation is to install a library. If this problem still occurs after installation, you can try to use -L to specify the following search path as the current one, which should be solved. After installation, compile and link as follows:

        3) ldconfig configure /etc/ld.so.conf.d/, ldconfig update
        I don't particularly understand this method, so I can't give you a detailed explanation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325856733&siteId=291194637