Tip: the following is the main content of this article. The following cases can be used for reference
1, System call
System call: few functions are used to access and control files and devices. These functions are called system calls, which are directly provided by UNIX (and Linux). They are also interfaces to the operating system itself.
System call is a method between the library and the operating system. A program calls the system call by calling the library method to realize the desired content on the operating system. Programs can also call system calls directly.
System calls related to file operations
int open(const char* pathname, int flags);//Used to open an existing file int open(const char* pathname, int flags,mode_t mode);//Used to create a new file and set access permissions Parameter introduction: pathname: The path and name of the file to be opened flags : Open the sign, as O_WRONLY Write only open O_RDONLY Read only open O_RDWR Open read / write mode O_CREAT Create if file does not exist O_APPEND Append at the end of the file O_TRUNC Empty the file and write again mode: Permissions such as "0600" Return value: file descriptor ssize_t read(int fd, void* buf, size_t count); //Read file contents Parameter introduction: fd Corresponding open file descriptor buf Space for storing data count How many bytes of data do you plan to read from the file at a time Return value: the number of bytes actually read ssize_t write(int fd, const void* buf,size_t count); //Write to file Parameter introduction: fd Corresponding open file descriptor buf Store data to be written count Plan how much data to write to the file at a time int close(int fd); //Close file Parameter introduction: fd File descriptor to close
File descriptor:
The file descriptor is a data of type int.
Ordinary users cannot directly access the files in the kernel. Users can only access the data in the kernel through system calls. In order for the kernel to know which file to access, it is necessary to tell the kernel the ID generated after the program is started. After the kernel gets the ID, it can find the corresponding file and operate the file accordingly. This ID is the file descriptor.
Analysis: the parent process opens a file first, and the child process can share it after fork ing?
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <fcntl.h> //Print results of analysis program execution int main() { char buff[128] = {0}; //myfile.txt create it yourself and write the content as "abcdef" int fd = open("myfile.txt",O_RDONLY); pid_t pid = fork(); assert( pid != -1 ); if ( pid == 0 ) { read(fd,buff,1); printf("child buff=%s\n",buff); sleep(1); read(fd,buff,1); printf("child buff=%s\n",buff); } else { read(fd,buff,1); printf("parent buff=%s\n",buff); sleep(1); read(fd,buff,1); printf("parent buff=%s\n",buff); } close(fd); }
Since the PCB of the child process created by fork copies the parent process, and the pointer of the file table in the PCB of the child process to the open file only copies the value in the PCB of the parent process, the parent and child processes will share all the file descriptors opened by the parent process before fork. As shown in the figure below:
Exercise: write a program to copy an ordinary file (similar to cp command)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <fcntl.h> #include <unistd.h> /* *Copy an ordinary file named "passwd" in the current directory. The new file is called "newpasswd" */ int main() { char buff[256] = {0}; int fdr = open("passwd",O_RDONLY); assert( fdr != -1 ); int fdw = open("newpasswd",O_WRONLY|O_CREAT,0600); assert( fdw != -1 ); int n = 0; while( (n = read(fdr,buff,256)) > 0 ) { write(fdw,buff,n); } close(fdr); close(fdw); }
2, Differences between system calls and library functions
Difference: the implementation of system call in the kernel belongs to kernel space, and the implementation of library function in function library belongs to user space.
The execution process of system call is as follows:
3, Process replace exec
(1) Introduction to exec System Functions
exec series replacement process: pcb is replaced with the previous process entity.
#include <unistd. h> / / header file to be referenced exec Introduction to family methods execl take as an example: /* *path:The path name of the newly replaced program *arg :The first parameter passed to the main function of a new program, usually the name of the program *arg This is followed by the list of remaining parameters. The number of parameters is variable, and the null pointer must be used as the last parameter */ int execl(const char* path, const char * arg,...); int execlp(const char* file, const char * arg,...); int execle(const char* path, const char * arg,...,char* const envp[]); int execv(const char * path, char* const argv[]); int execvp(const char * file, char* const argv[]); int execve(const char * path, char* const argv[],char* const envp[]);
(2) Use of exec System Functions
Taking ps replacing the current program as an example, this paper introduces the use of exec System functions. Note that the functions of this series of methods are the same, there is no difference, but the parameters are different
The code example used by execl is as follows: (ps replaces the current program)
Example 1:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> int main() { printf("main pid=%d\n",getpid()); //execl is executed successfully without returning. It is directly executed from the main function of the new program. The error code is returned only if it fails execl("/bin/ps","ps","-f"(char*)0); perror("execl error"); exit(0); }
Example 2:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> int main() { //Store the parameters passed to the main function of the new program char* myargv[] = {"ps","-f",(char*)0}; //Store the environment variables passed to the main function of the new program char* myenvp[] = {"MYSTR=hello","VAL=100",(char*)0}; printf("main pid=%d\n",getpid()); //execl does not return if it is executed successfully, but the error code is returned if it fails execve("/bin/ps",myargv,myenvp); perror("execve error"); exit(0); }
(3) fork and exec are used together to create a new process
In the following example, the current main program copy generates a sub process, and the sub process replaces itself with the new program "b".
The current main program is as follows:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <fcntl.h> #include <errno.h> int main(int argc, char* argv[],char* envp[]) { printf("main pid=%d\n",getpid()); pid_t pid = fork(); assert( pid != -1 ); if ( pid == 0 ) { char * myargv[] = {"b","hello","abc","123",(char*)0}; //char * myenvp[] = {"MYSTR=hello","VAL=100",(char*)0}; execve("./b",myargv,envp); perror("execl error"); exit(0); } wait(NULL); printf("main over\n"); exit(0); }
The newly replaced b procedure is as follows:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> int main(int argc, char* argv[],char* envp[]) { printf("b pid=%d\n",getpid()); int i = 0; printf("argc=%d\n",argc); for( ; i < argc; i++ ) { printf("argv[%d]=%s\n",i,argv[i]); } for( i = 0; envp[i] != NULL; i++ ) { printf("envp[%d]=%s\n",i,envp[i]); } exit(0); }