“This is the 12th day of my participation in the August Text Challenge.
Function library
-
main.c
-
Add. c: implementation of the function —->add– programmer’s intellectual property
-
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
-
The generated executable file is large
-
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.
-
When the subsequent program updates, you need to recompile once!
Static library production
Main steps:
-
Step 1: Create four new directories
Bin include lib SRC // Project development can include lib to othersCopy the code
-
The second step:
- In the SRC directory
vim -O main.c add.c
- In the include directory
vim add.h
- In the SRC directory
-
Step 3: Generate the binaries required by the library functions in the bin directory
gcc -c add.c -o .. /bin/add.o -I .. /include/
-
Step 4: In the lib directory to generate library functions required binaries generated library files
ar -crs .. /lib/libadd.a add.o
-
Step 5: Link library file:
-
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:
-
Step 1: Create four new directories:
linux@ubuntu:~/demo/test/LIB$ ls bin include lib src Copy the code
-
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
-
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
-
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
-
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
-
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.
-
The calls are slower than static libraries. Dynamic libraries compile into a secondary file that is smaller than static libraries
-
Dynamic libraries can be shared
-
Dynamic libraries are faster for subsequent software updates without recompiling
Making a dynamic library
Main steps:
-
Step 1: Create four new directories
bin include lib src
-
Step 2: Generate a binary non-executable file of the library file in the bin directory
- -fPIC: Generates position-independent code
gcc -c -fPIC add.c -o .. /bin/add.o -I .. /include/
-
Step 3: start to create dynamic library, in the lib directory to generate library functions required binary files generated library files.
gcc -shared add.o -o .. /lib/libadd.so
-
Step 4: Start linking
gcc main.c -I .. /include/ -L .. /lib/ -ladd -o .. /bin/a.out
-
Step 5: Modify the library’s environment variable path
-
Method 1: put libadd.so in /usr/bin/ or /bin/
-
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
-
Dynamic libraries have fixed paths:
- Under the/usr/lib
- Under the/lib
- In use, you need to put the library file into either of the two price folders
-
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
-
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
-
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.
-
What are the contents of IO
The main learning has provided a good API function interface
-
Linux is full of files
-
Linux is a multi – user multi – task operating system process
-
Because IO processes interact between the application and kernel layers, the definition of learning is too abstract
-
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
- open
- read
- write
- close
- 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.
-
: Common file (white) ⬜d
: Directory (blue) 🟦l
Link files are divided into soft links and hard links (window shortcuts) (light blue)c
: character device file (/dev/input/mouse0 mouse) event1 keyboard usdo hexdump mouse0b:
Block device file (disk /dev/sda)p
: pipeline file (interprocess communication)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
-
The header file:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-
Prototype:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
-
Open a file
-
Parameters:
- Pathname: indicates the path and name. For example, “1. TXT “// The following parameters are of the type char*
- 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
-
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
-
The return value:
- Returns a file descriptor on success
- 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
-
#include
-
Ssize_t read(int fd, void *buf, size_t count);
-
Function: Read a file
-
Parameters:
- Fd: object file descriptor — to whom to read
- Buf: store read data — address for storing read data
- Count: How many to read
-
The return value:
- Success: Returns the number of bytes read
- 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
-
#include
-
Prototype:
ssize_t write(int fd, const void *buf, size_t count);
typedef long ssize_t
typedef unsigned long size_t
-
Parameters:
- Fd: Object file descriptor — which file to operate on
- Buf: Address of the data to be written — “What to write
- Count: Size of data to be written — The number of bytes to be written
-
The return value:
- Success: Number of bytes written
- 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
-
#include
-
Prototype: int close(int fd);
-
Parameter: fd The file descriptor to close
-
The return value:
- Returns 0 on success
- 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
-
The header file:
#include <sys/types.h>
#include <unistd.h>
-
Prototype:
off_t lseek(int fd, off_t offset, int whence);
typedef long off_t
-
Function: Operate read/write pointer, offset the read/write pointer
-
Parameters:
-
Fd: Target file descriptor: —- Specifies the file to operate on
-
Offset: How and by how much
-
If the number is negative, it means that this is offset forward,
-
If the offset is out of the beginning of the file, an error is returned
-
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.
-
-
whence: the baseline location —– is offset based on the location
- SEEK_SET: Offset according to the beginning of the file
- SEEK_CUR: Offset based on the current location of the user
- SEEK_END: Offset based on the end of the file
-
-
The return value:
- Success: Returns the number of bytes offset (based on the beginning of the file)
- 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.