“This is the 12th day of my participation in the August Text Challenge.

Function library

  1. main.c

  2. Add. c: implementation of the function —->add– programmer’s intellectual property

  3. Add.h: function declaration — is provided to the user for use

What is the function library?

Function libraries are used to protect programmers’ intellectual property or to encrypt code, and then encapsulate the implementation of a function into an unexecutable binary file that can be called. It’s called a function library.

Function library classification:

It can be divided into two categories: 1. Static library 2. Dynamic library

Function library format:

Static libraries: start with lib + library name +.a suffix

For example: libadd. A

Dynamic libraries: start with lib + library name +.so suffix

For example: libadd. So

Static library

Static libraries refer to compiling my library files and my target binaries together in the linking phase to produce executable binaries

  1. The generated executable file is large

  2. There is no need to rely on the source file after generating the executable file. When calling a function, there is no need to find the implementation of the function externally, so the call is relatively fast.

  3. When the subsequent program updates, you need to recompile once!

Static library production

Main steps:

  1. Step 1: Create four new directories

    Bin include lib SRC // Project development can include lib to othersCopy the code
  2. The second step:

    1. In the SRC directoryvim -O main.c add.c
    2. In the include directoryvim add.h
  3. Step 3: Generate the binaries required by the library functions in the bin directory

    1. gcc -c add.c -o .. /bin/add.o -I .. /include/
  4. Step 4: In the lib directory to generate library functions required binaries generated library files

    1. ar -crs .. /lib/libadd.a add.o
  5. Step 5: Link library file:

    1. gcc main.c -I .. /include/ -L .. /lib/ -ladd

Note:

  • -i: specifies the path of the header file
  • -l: specifies the path of the library file
  • -l: Specifies the library name to link to (minus the suffix and prefix) (lowercase L)

Static library detailed process:

  1. Step 1: Create four new directories:

    linux@ubuntu:~/demo/test/LIB$ ls
    bin  include  lib  src
    Copy the code
  2. The second step:

    Vim -o main.c add.c;

    //main.c
    #include <stdio.h>
    #include "add.h"
    
    int main(int argc, char const *argv[])
    {
        int a = 100;
        int b = 200;
        printf("add = %d\n",add(a,b));
    
        return 0;
    }
    Copy the code
    //add.c
    #include <stdio.h>
    #include "add.h"
    
    int add(int a,int b)
    {
        return a+b;
    }
    Copy the code

    Vim add.h in the include directory

    //add.h
    #ifndef __ADD_H__
    #define __ADD_H__
    
    int add(int a,int b);
    
    #endif
    Copy the code

    Once we’ve done that, we can compile to see if there are any problems with the code

    It says, no header file

    linux@ubuntu:~/demo/test/LIB/src$ gcc *.c add.c:2:10: fatal error: add.h: #include "add.h" ^~~~~~~ compilation. Main. c:2:10: fatal error: add.h: #include "add.h" ^~~~~~~ compilation terminatedCopy the code

    Add the -i.. /include will do

    linux@ubuntu:~/demo/test/LIB/src$ gcc *.c -I .. /include/ linux@ubuntu:~/demo/test/LIB/src$ ls add.c a.out main.c linux@ubuntu:~/demo/test/LIB/src$ ./a.out add = 300Copy the code
  3. Step 3: Generate the binaries required by the library functions in the bin directory

    Again, no link header file

    linux@ubuntu:~/demo/test/LIB/src$ gcc -c add.c -o .. /bin/add.o add.c:2:10: Fatal error: add.h: No that file or directory #include "add.h" ^~~~~~~ compilation.Copy the code

    Again, add -i.. /include will do

    linux@ubuntu:~/demo/test/LIB/src$ gcc -c add.c -o .. /bin/add.o -I .. /include/ linux@ubuntu:~/demo/test/LIB/src$ cd .. /bin/ linux@ubuntu:~/demo/test/LIB/bin$ ls add.oCopy the code
  4. Step 4: In the lib directory to generate library functions required binaries generated library files

    Note: build in the specified directory just like this.. /lib/libadd.a

    linux@ubuntu:~/demo/test/LIB/bin$ ar -crs .. /lib/libadd.a add.o linux@ubuntu:~/demo/test/LIB/bin$ cd .. /lib/ linux@ubuntu:~/demo/test/LIB/lib$ ls libadd.aCopy the code
  5. Link library file:

    linux@ubuntu:~/demo/test/LIB/src$ gcc main.c -I .. /include/ -L .. /lib/ -ladd linux@ubuntu:~/demo/test/LIB/src$ ls add.c a.out main.cCopy the code
  6. Finally, we can verify:

    linux@ubuntu:~/demo/test/LIB/src$ ./a.out 
    add = 300
    Copy the code

    GCC main.c add.c does not need to be used

The dynamic library

Dynamic libraries refer to the compilation of library files and target binaries into an executable file at the linking stage, not as a whole, but as a form of library functions in the executable that specifies which functions link to which libraries. When a function is called, it looks up the library function table, then finds the library to link to, and then calls the function externally.

  1. The calls are slower than static libraries. Dynamic libraries compile into a secondary file that is smaller than static libraries

  2. Dynamic libraries can be shared

  3. Dynamic libraries are faster for subsequent software updates without recompiling

Making a dynamic library

Main steps:

  1. Step 1: Create four new directories

    1. bin include lib src
  2. Step 2: Generate a binary non-executable file of the library file in the bin directory

    1. -fPIC: Generates position-independent code
    2. gcc -c -fPIC add.c -o .. /bin/add.o -I .. /include/
  3. Step 3: start to create dynamic library, in the lib directory to generate library functions required binary files generated library files.

    1. gcc -shared add.o -o .. /lib/libadd.so
  4. Step 4: Start linking

    1. gcc main.c -I .. /include/ -L .. /lib/ -ladd -o .. /bin/a.out
  5. Step 5: Modify the library’s environment variable path

    1. Method 1: put libadd.so in /usr/bin/ or /bin/

    2. LD_LIBRARY_PATH = LD_LIBRARY_PATH

      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/linux/demo/test/LIB/lib

Dynamic library detailed process:

Step 1: Create four new directories

bin  include  lib  src
Copy the code

Step 2: Generate a binary non-executable file of the library file in the bin directory

📣 Note: -fpIC: generates position-independent code

linux@ubuntu:~/demo/test/LIB/src$ gcc -c -fPIC add.c -o .. /bin/add.o -I .. /include/ linux@ubuntu:~/demo/test/LIB/src$ cd .. /bin/ linux@ubuntu:~/demo/test/LIB/bin$ ls add.oCopy the code

Step 3: start to create dynamic library, in the lib directory to generate library functions required binary files generated library files

linux@ubuntu:~/demo/test/LIB/bin$ gcc -shared add.o -o .. /lib/libadd.so linux@ubuntu:~/demo/test/LIB/bin$ cd .. /lib/ linux@ubuntu:~/demo/test/ lib/ lib$ls libadd.so// greenCopy the code

Step 4: Start linking

linux@ubuntu:~/demo/test/LIB/src$ gcc main.c -I .. /include/ -L .. /lib/ -ladd -o .. /bin/a.out linux@ubuntu:~/demo/test/LIB/src$ cd .. /bin/ linux@ubuntu:~/demo/test/LIB/bin$ ls add.o a.outCopy the code

Once the link is complete, try running it./a.out

linux@ubuntu:~/demo/test/LIB/bin$ ./a.out ./a.out: error while loading shared libraries: libadd.so: Cannot open shared object file: No such file or directoryCopy the code

Libadd. so => not found

Additional Linux command: LDD + Executable file name function: the link library of the file is displayed

linux@ubuntu:~/demo/test/LIB/bin$LDD a.out linux-vdso.so.1 (0x00007ffFE59f2000) libadd.so => not found Libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe753582000) /lib64/ lD-linux-x86-64.so.2 (0x00007fe753b75000)Copy the code

Next, there are two ways to set a path

Step 5: Modify the library’s environment variable path

Method 1: put libadd.so in /usr/bin/ or /bin/

linux@ubuntu:~/demo/test/LIB/bin$ sudo cp .. /usr/bin/ linux@ubuntu:~/demo/test/ lib/ bin$sudo cp.. /lib/libadd.so /bin/ / operating system // but at work you probably do not have sudo permission linux@ubuntu:~/demo/test/ lib/ bin$sudo rm /usr/lib/libadd.so // If you use the following operation, execute the above command, be careful not to delete the error, because there are many important. So files!! -l = -lCopy the code

LD_LIBRARY_PATH = LD_LIBRARY_PATH

linux@ubuntu:~/demo/test/LIB/bin$env // Specify the path of the executable file PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binCopy the code
$LD_LIBRARY_PATH linux@ubuntu:~/demo/test/LIB/bin$echo $LD_LIBRARY_PATH linux@ubuntu:~/demo/test/LIB/bin$ // None, because the specified two paths are not allowed to change, but can be added to the pathCopy the code
/ / modify library @ ubuntu Linux environment variable path: ~ / demo/test/LIB/bin $export LD_LIBRARY_PATH = $LD_LIBRARY_PATH: / home/Linux/demo/test/LIB/LIB / / look again at the Linux @ ubuntu: ~ / demo/test/LIB/bin $echo $LD_LIBRARY_PATH: / home/Linux/demo/test/LIB/LIBCopy the code

Step 6: Run it./a.out

linux@ubuntu:~/demo/test/LIB/bin$ ls
add.o  a.out
linux@ubuntu:~/demo/test/LIB/bin$ ./a.out 
add = 300  
Copy the code

📣 Note: if there are static and dynamic libraries with the same name in the specified library folder, link dynamic libraries preferentially.

Considerations for dynamic libraries

  1. Dynamic libraries have fixed paths:

    1. Under the/usr/lib
    2. Under the/lib
    3. In use, you need to put the library file into either of the two price folders
  2. LD_LIBRARY_PATH changes the library’s environment variable path to LD_LIBRARY_PATH

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/linux/class/iO/dc21071/day1/01_lib/lib

  3. In future. C files to use the library functions, directly call and link.

    gcc main.c -I .. /include/ -L .. /lib/ -ladd -o .. /bin/a.out

The difference between static and dynamic libraries

IO

Learn the premise of IO

  1. Why study IO

    Embedded: hardware is controlled by the software layer. Embedded needs to operate the memory and the kernel to complete the operation of the hardware.

  2. What are the contents of IO

    The main learning has provided a good API function interface

  3. Linux is full of files

  4. Linux is a multi – user multi – task operating system process

  5. Because IO processes interact between the application and kernel layers, the definition of learning is too abstract

  6. The Linux operating system divides files into two broad categories

    Binary files — “A. out –” are stored according to computer logic

    Text files — “.c or.txt files — “according to the programmer’s logic to store the underlying storage is the same, the logical storage is not the same

What is the IO

IO refers to: input output refers to the input and output of memory

  • Memory: the speed itself is fast, the data is lost on power failure, too expensive
  • External storage: its speed is slow, data is permanently saved, power is not lost, external storage itself is cheap.

How to Use IO

Use the concept: Everything is a file under Linux.

So the operation of the file: open the file —- read and write the file — close the file

IO is divided into two categories

1. File IO: is an API interface provided by the operating system, also known as system call. Each operating system’s IO- API interface is different. As a result, your Linux executable cannot be ported to another platform if it calls file IO.

Because the interface for file IO is provided by the operating system, the interfaces of different operating systems are inconsistent.

2. Standard IO: stdio.h — is provided by the standard C library ANSI C library API interface, standard IO file IO based on encapsulation.

File IO interface

  1. open
  2. read
  3. write
  4. close
  5. lseek

Note:

  • Windows function interfaces _open, _read, _write, _close

  • Ios function interface: Open, Read, Write, Close

Use of MAN manual

Man: interface for online function query

Now I’m going to focus on 2 and 3.

**errno **

Errno is a global variable that is used in #include to flag error messages. When the function returns an error, the global variable is reset to the corresponding number in the error message table.

The global variable is assigned to the table before the return. For example, if an open file exists, proceed, if not return -1, print the corresponding error.

file

The Linux operating system is based on the concept of files. A file is an information carrier composed of character sequences. Based on this, I/0 devices can be treated as files. Thus, the same system call used to interact with a normal file on disk can be used directly on an I/0 device. This greatly simplifies the system to deal with different equipment, improve efficiency.

There are seven main types of files in Linux: normal files, directory files, symbolic link files, pipe files, socket files, and device files.

  1. -: Common file (white) ⬜
  2. d: Directory (blue) 🟦
  3. lLink files are divided into soft links and hard links (window shortcuts) (light blue)
  4. c: character device file (/dev/input/mouse0 mouse) event1 keyboard usdo hexdump mouse0b
  5. :Block device file (disk /dev/sda)
  6. p: pipeline file (interprocess communication)
  7. s(socket) : socket file (a file related to network communication) **

File descriptor

File descriptors (FD for short) : Distinguish and reference specific files for the kernel. With Linux, all operations on devices and files are done through file descriptors.

A file descriptor is a file flag that is a non-negative integer and has a small value. Is an index value and points to a record table of open files for each process in the kernel. When an existing file is opened or a new file is created, the kernel returns a file descriptor to the process. When reading or writing a file, you need to pass the file descriptor as an argument to the corresponding function.

File descriptor-based I/O operations are one of the common operations in Linux.

📣 note:

./a.out at first run time. The kernel automatically opens three device files for him and returns three file descriptors

1. Enter —- 0 as standard

2. Standard output —- 1

Standard error output — 2

Use of THE API interface

Additional: Three elements of a function: function, argument, and return value

1.open

  1. The header file:

    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <fcntl.h>
  2. Prototype:

    1. int open(const char *pathname, int flags);
    2. int open(const char *pathname, int flags, mode_t mode);
  3. Open a file

  4. Parameters:

    1. Pathname: indicates the path and name. For example, “1. TXT “// The following parameters are of the type char*
    2. Flags: how to open a file

    The value can be: O_RDONLY: read-only permission to open a file O_WRONLY: write permission to open a file O_RDWR: read and write permission to open a file

  5. O_CREAT: opens the file if it exists, creates the file if it does not exist. If O_CREAT is used, the third argument of open is used. Granting permissions is a base 8 integer. It starts with 0.

    For example, 0664 O_EXCL: An error message is returned if the file exists

    O_NONBLOCK: opens a file in non-blocking mode O_TRUNC: opens a file in empty modeCopy the code
  6. The return value:

    1. Returns a file descriptor on success
    2. Returns -1 on failure

Note: using 0666 to create files, why 0664? The operating system has a mechanism called file mask, which erases the writable permission of other users. Use umask to view the file mask to change the value of the file mask umask +.

Code:

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

int main(int argc, const char *argv[])
{
	int fd = open("./1.txt",O_RDWR);
	if(- 1 == fd)
	{
		perror("open");
		printf("errno = %d\n",errno);
		return - 1;
	}
	perror("open1");

	return 0;
}

Copy the code
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
open1: Success
Copy the code

2.perror

#include

Void perror(const char * STR);

Function: Prints error messages in a fixed format

Parameter: s: name of the judged function Format: “STR: current error message”

Returned value: None

3.read

  1. #include

  2. Ssize_t read(int fd, void *buf, size_t count);

  3. Function: Read a file

  4. Parameters:

    1. Fd: object file descriptor — to whom to read
    2. Buf: store read data — address for storing read data
    3. Count: How many to read
  5. The return value:

    1. Success: Returns the number of bytes read
    2. Failure: -1 read to end of file returns 0

Note: But the last read returns the number of bytes read at the end of the file if the remaining bytes are not enough for the read conut, and the second read returns 0

Code:

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

int main(int argc, const char *argv[])
{
	int fd = open("1.txt",O_RDONLY);
	if(- 1 == fd)
	{
		perror("open");
		return - 1;
	}
	
	char buf[123] = {0};
	if(- 1 == read(fd,buf,123))
	{
		perror("read");
		return - 1;
	}

	printf(\n reads buf = %s\n,buf);

	if(- 1 == close(fd))
	{
		perror("close");
		return - 1;
	}

	return 0;
}
Copy the code
linux@ubuntu:~/demo/test/IO/test$./a.out The read value is buf = hello worldCopy the code

4.write

  1. #include

  2. Prototype:

    1. ssize_t write(int fd, const void *buf, size_t count);
    2. typedef long ssize_t
    3. typedef unsigned long size_t
  3. Parameters:

    1. Fd: Object file descriptor — which file to operate on
    2. Buf: Address of the data to be written — “What to write
    3. Count: Size of data to be written — The number of bytes to be written
  4. The return value:

    1. Success: Number of bytes written
    2. Failure: 1

Code:

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

int main(int argc, const char *argv[])
{
	int fd = open("./1.txt",O_WRONLY);
	if(- 1 == fd)
	{
		perror("open");
		return - 1;
	}
	char buf[123] = "hello world";

	if(- 1 == write(fd,buf,strlen(buf)))
	{
		perror("write");
		return - 1;
	}
	
	return 0;
}
Copy the code

5.close

  1. #include

  2. Prototype: int close(int fd);

  3. Parameter: fd The file descriptor to close

  4. The return value:

    1. Returns 0 on success
    2. Returns -1 on failure

Note: an executable program can open a maximum of 1024 file descriptors. This value can be modified with the kernel

Exercise: Write Hello World to a file and then read Hello World from the file and print it to the terminal

Result: Data is normal when written, but not read when read, due to the interaction between read and write Pointers

6.lseek

  1. The header file:

    1. #include <sys/types.h>
    2. #include <unistd.h>
  2. Prototype:

    1. off_t lseek(int fd, off_t offset, int whence);
    2. typedef long off_t
  3. Function: Operate read/write pointer, offset the read/write pointer

  4. Parameters:

    1. Fd: Target file descriptor: —- Specifies the file to operate on

    2. Offset: How and by how much

      1. If the number is negative, it means that this is offset forward,

      2. If the offset is out of the beginning of the file, an error is returned

      3. If the number is positive, if it is offset out of the file, it represents the end of the offset piece that goes back, enlarging the file, and filling it with ‘\0’.

        This type of file is called an empty file

      📣 Note: If no write is done to the offset, the kernel considers the offset invalid and does not increase the file size.

    3. whence: the baseline location —– is offset based on the location

      1. SEEK_SET: Offset according to the beginning of the file
      2. SEEK_CUR: Offset based on the current location of the user
      3. SEEK_END: Offset based on the end of the file
  5. The return value:

    1. Success: Returns the number of bytes offset (based on the beginning of the file)
    2. Failed: (off_t) -1
// Get the file size using the offset
int len = lseek(fd,0,SEEK_END);             
Copy the code

Code:

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

int main(int argc, const char *argv[])
{
    // Open a file to write and read
    int fd = open(argv[1],O_RDWR|O_CREAT,0666);
    if (- 1 == fd )
    {
        perror("open");
        return - 1;
    }
    // Start writing data
    
    if (- 1 == write(fd,"hello world".11))
    {
        perror("write");
        return - 1;
    }
    // Start reading/writing pointer offset

    if (- 1 == lseek(fd,- 6,SEEK_END))
    {
        perror("lseek");
        return - 1;
    }
    // Start reading data
    char buf[123] = {0}; // Store the read data
    if ( - 1 == read(fd,buf,sizeof(buf)))
    {
       perror("read");
       return - 1;
    }
    printf("buf = %s \n",buf);

    if( - 1 == close (fd))
    {
        perror("close");
        return - 1;

    }    
    return 0;
}
Copy the code

Job: implement the cp command mycp myself (make sure to copy binaries)

Loop read while reading, loop write while writing, using the argv argument

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

int main(int argc, const char *argv[])
{
    // Determine if there is a problem with my command line pass parameter
    if (3! = argc) {printf("Incorrect input parameters, e.g. :./a.out + source file + destination file \n");
        return - 1;
    }
    // Open the file to copy in read-only mode, open the file to paste to write and create and empty
    int fd = open(argv[1],O_RDONLY);
    if (- 1 == fd)
    {
        perror("open");
        return - 1;
    }
    int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664);
    if (- 1 == fd1 )
    {
        perror("open1");
        return - 1;
    }
    // Start reading
    while(1)
    {
        / / to read first
        char buf[SIZE] = {0};// as a data transfer station
        ssize_t ret = read(fd,buf,sizeof(buf));
        if(- 1 == ret)
        {
            perror("read");
            return - 1;
        }
        if (0 == ret)
        {
            printf("Read to end of file, exit while loop \n");
            break;
        }
        / / write
        if(- 1 == write(fd1,buf,ret))
        {
            perror("write");
            return - 1; }}// Read after write is complete
    close(fd);
    close(fd1);
    return 0;
}
Copy the code

File IO and standard IO differences

Document IO and standard IO each have their strengths. File IO is a system call provided by the operating system. Fast, but frequent calls to file IO reduce the efficiency of the kernel. Because each operating system PAI interface is different, it causes the problem of poor portability. Standard IO is provided by the standard C library. It is an API interface encapsulated on the basis of file IO, and its portability has been improved. And on the basis of file IO encapsulates a buffer. The purpose is to store data that is not in a hurry, so as to reduce the number of file IO calls, improve the efficiency of the kernel, so the two IO models will be used according to the specific data situation.