3 . File descriptors
3.1 PCB process control block
The process control block (PCB) is a data structure set by the system to manage the process. The system uses it to record the external characteristics of the process and describe the movement change process of the process.
We can use locate sched.h to see the location
3.2 File Descriptor Table
The member variable files_struct *file of the structure PCB points to the file descriptor table, and the structure of the file can be found through the file table 0/1/2/3....
And 0/1/2 is the default descriptor, which means standard input, standard output, standard error
STDIN_FILENO 0
STDOUT_FILENO 1
STDERR_FILENO 2
A file structure in Linux represents an open file, and each open file in the system has an associated file in kernel space. It is created by the kernel when the file is opened and passed to any function that operates on the file. After all instances of the file are closed, the kernel frees this data structure.
3.3 Maximum number of open files
The default number of open files for a process is 1024.
Command to view ulimit-a to view the corresponding value of open filesx. Default is 1024
Can be modified with ulimit -n 4096.
Of course, this value can also be permanently modified by modifying the system configuration file, but this is not recommended. .
cat /pro/s/fs/ile-max can view the maximum number of files that the computer can open. Affected by memory size.
3.4 FILE structure
FILE is the C language file structure definition, and the opening of the file and the operation of the file must pass through this structure
struct _iobuf{ char *_ptr; //next location for file input int _cnt; //Relative position of the current buffer char *_base; //the start of the file int _flag; //file sign int _file; //file descriptor number int _charbuf; //file buffer int _bufsiz; //buffer size char *_tmpfname; };
Two processes open the file at the same time, the system call is executed
1. Each process will independently create a file description table, which does not affect each other when reading
2. Open in the way of reading and writing, read first and then write, and start to overwrite the new content written from the position after reading
The code of process 1, read 15 characters first, read 15 characters after sleeping for 20s
#include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<string.h> int main() { char buf[100] = ""; int fd = open("1.txt", O_RDWR); if(fd < 0) { perror("error"); } int ret = read(fd, buf, 15); printf("%d, %s\n", ret, buf); sleep(20); strcpy(buf ,""); ret = read(fd, buf, 15); printf("%d, %s\n", ret, buf); close(fd); return 0; }
The code of process 2, write first, then read and write the 10 characters after the position
#include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> int main() { char buf[] = "asjfkasjf"; int fd = open("./1.txt", O_RDWR); if(fd < 0) { perror("error"); } int ret = write(fd, buf, 40); printf("%d\n", ret); char buf1[1000] = ""; read(fd, buf1, 10); close(fd); return 0; }
Two processes open a file at the same time, in standard IO mode
1. Both processes will read all the starting files into the buffer
2. When the buffer is full, the content will be read from the file again (updates may occur)
3. Because both processes read the open content at the same time, they can operate the original content independently
4. The final result of the document is subject to the combined effect of the two procedures
Process 1:
#include<stdio.h> #include<string.h> #include<unistd.h> #include<fcntl.h> int main() { FILE *fp = fopen("./1.txt", "r+"); if(fp == NULL) { perror("Error"); } char buf[100] = ""; fread(buf, 2, 5, fp); printf("%s\n", buf); strcpy(buf, ""); sleep(10); fread(buf, 2, 100, fp); fclose(fp); return 0; }
Process 2:
#include<stdio.h> #include<string.h> int main() { FILE *fp = fopen("./1.txt", "r+"); if(fp == NULL) perror("Error"); char buf[100] = "Hahahahahahahahahaha"; fwrite(buf, 2, 50, fp); strcpy(buf ,""); fread(buf, 2, 50, fp); fclose(fp); return 0; }
They will execute their own, and when the program is finished, the final result will be refreshed
4. Blocking, non-blocking
Reading regular files will not block, no matter how many bytes are read, read will return within a limited time. Reading from a terminal device or network is not necessarily true. If the data input from the terminal does not have a newline, calling read to read the terminal device will block. If no data packets are received on the network, calling read to read from the network will block, as for blocking. It's also indeterminate how long, if -- it blocks there until no data arrives. Likewise, writing to regular files is non-blocking, while writing to a terminal device or network is not.
Now let's clarify the concept of blocking (Block). When a process calls a blocking system function, the process is put into sleep (Sleep) state, and the kernel schedules other processes to run until the event the process is waiting for occurs (such as a packet received on the network, or a call to It is possible to continue to run when the sleep time specified by sleep is up. Opposite to the sleep state is the Running state. In the Linux kernel, the processes in the running state are divided into two situations:
1. It is being scheduled for execution, the CPU is in the context of the process, the program counter (eip) holds the instruction address of the process, the general register holds the intermediate result of the process's operation process, and the instructions of the process are being executed, The address space of this process is being read and written. .
2. Ready state, the process does not need to wait for any event to occur and can be executed at any time, but the CPU is temporarily executing another process, so the process is waiting in a ready queue to be scheduled by the kernel. There may be multiple ready processes in the system at the same time, so what should be scheduled for execution? The kernel's scheduling algorithm is based on priority and time slice, and it will dynamically adjust its priority and time slice according to the operation of each process. Let each process have a fair chance to execute, and at the same time take into account the user experience, and do not let the process that interacts with the user respond too slowly
Block read terminal: [block readtty.c]
Non-blocking read terminal: [nonblock_readtty.c]
Non-blocking read terminal and wait timeout: [nonblock_timeout.c]
Note that blocking and non-blocking are for files. instead of properties for read, write, etc. read terminal, blocking read by default.
summary:
Blocking is a property of device files, network files
Scenarios that cause blocking: read device files, read network files. (Reading regular files without blocking concept) For example: /devtty - terminal file.
//Check the blocking phenomenon of tty #include <unistd . h> #include <std1ib.h> #include <stdio. h> { int main(void) char buf[10] ; int n; n = read(STDIN_ FILENO, buf, 10); if(n < 0) { perror("read STDIN FILENO"); //printf("%d",errno); exit(1); } write(STDOUT_ FILENO,buf, n); return 0; }
The device file does not need to be closed, it can be reopened and the file attributes can be changed
//View tty set to non-blocking property #include <unistd .h> #include <fcnt1. h> #include <errno. h> #include <stdio. h> #include <std1ib.h> #include <string.h> int main(void) { char buf[10]; int fd,n fd = open(" /dev/tty",0_ RDONLY |0_ NONBLOCK) ; if(fd<0) { perror("open /dev/tty"); exit(1); } tryagain: n = read(fd, buf, 10); if(n<0) { if Cerrno != EAGAIN) { // if(errno != EWOULDBLOCK) perror("read /dqv/tty"); exit(1); } else { write(STDOUT_ FILENO,"try again\n", strlen("try again\n")); sleep(2); goto tryagain; } write(STDOUT_ FILENO, buf, n); close(fd); return 0; }
Optimize the code to prevent infinite waits
//View tty set to non-blocking properties, optimize the code #include <unistd.h> #include <fcnt1 .h> #include <errno .h> #include <stdio.h> #include <std1ib.h> #include <string.h> #include <unistd.h> int main(void) { char buf[10]; int fd, n; fd = open(" /dev/tty", 0_ RDONLY 10_ NONBLOCK); if(fd<0) { perror("open /dev/tty"); exit(1); } tryagain: n = read(fd, buf, 10); if(n<0) { if (errno != EAGAIN) { // if(errno != EWOULDBLOCK) perror("read /dqv/tty"); exit(1); } else { write(STDOUT_ FILENO, "try again\n", strlen("try again\n")); sleep(2); goto tryagain; } } write(STDOUT_ FILENO,buf, n); close(fd); return 0; }
Optimize code to prevent infinite wait
//View tty set to non-blocking properties, optimize the code #include <unistd.h> #include <fcnt1 .h> #include <errno .h> #include <stdio .h> #include <std1ib.h> #include <string.h> #include <unistd .h> int main(void) { char buf[10]; int fd, n,i; fd = open(" /dev/tty", 0 RDONLY|O_ NONBLOCK); if(fd<0) { perror("open /dev/tty"); exit(1); } printf("open /dev/tty ok... %d\n",fd); for(i=0;i<5;i++) { n = read(fd, buf, 10); if(n>0) { break; } if(n<0) { if (errno != EAGAIN) { // if(errno != EWOULDBLOCK) perror("read /dqv/tty"); exit(1); } else { write(STDOUT_ FILENO,"try again\n", strlen("try again\n")); sleep(2); } } } if(i==5) { write (STDOUT_ FILENO,"time out \n", strlen("time out \n")); } else { write(STDOUT_ _FILENO,buf, n); } close(fd); return 0; }
5. fcntl modify file attributes
Change the access control attributes of an [already opened] file.
Focus on mastering the use of two parameters, F. _GETFL and F_ SETFL
//View tty set to non-blocking properties, optimize the code #include <unistd.h> #include <fcnt1. h> #include <errno. h> #include <stdio. h> #include <std1ib.h> #include <string.h> int main(void) { char buf[10]; int fd,n,i, flags ; flags = fcnt1(STDIN_ FILENO,F_ GETFL); //Get stdin attribute information if(flags = -1) { perror("fcnt1 error"); exit(1); } f1ags |= 0 NONBLOCK; int ret = fcnt1(STDIN FILENO, F_ SETFL, flags); if(ret = -1) { perror("fcnt1 error"); exit(1); } for(i=0;i<5;i++) { n = read(fd, buf, 10); if(n>0) { break; } if(n<0) { if (errno != EAGAIN) { // if(errno != EWOULDBLOCK) perror("read /dev/tty"); exit(1); } else { write(STDOUT_ FILENO,"try again\n", strlen("try again\n")); sleep(2) ; } } } if(i==5) { write(STDOUT_ FILENO,"time out \n", strlen("time out \n")); } else { write(STDOUT_ FILENO, buf, n); } close(fd); return 0; }
summary:
int flags = fcntl(fd, F_GETFL)
Get file status: F_GETFL
Set file status: F_SETFL
6. Iseek function
In Linux, the system function Iseek can be used to modify the file offset (read and write position).
Each open file records the current read and write position. When the system opens a file by default, the read and write position is 0, indicating the beginning of the file. Usually, how many bytes are read and written will move the read and write position back by how many bytes.
But there is one exception, if opened in 0_APPEND mode, each write operation will append data at the end of the file, and then move the read and write position to the new end of the file.
lseek is similar to the fseek function of the standard V/0 library, which can move the current read and write position (or offset).
Recall the role and common parameters of fseek. SEEK SET, SEEK_ CUR, SEEK_ END.
int fseek(FILE *stream, long offset, int whence);
Return value: success 0
fail: -1
Special: Returns 0 beyond the end of the file; returns -1 when back beyond the beginning of the file.
off_ t 1seek(int fd,off t offset,int whence) ;
Return value: failure: -1;
Success: is the offset backward from the beginning of the file
Special: lseek allows setting an offset past the end of the file, the file will be extended accordingly (false extension)
The interpretation of offset depends on the value of whence:
If when == SEEK .SET, the file offset is set to offset bytes from the beginning of the file, and the offset must be non-negative at this time
If when == SEEK _CUR, set the file offset to the current value + offset, and the offset can be positive or negative
If when == SEEK END, set the file offset to file length + offset, at this time offset can be positive or negative
Note that the file "read" and "write" use the same offset.
6.2 truncate function: truncate file
#include <unistd.h>
#include <sys/types .h>
int truncate(const char *path,off _t length);
//return value:
success 0;
Failure -1: For example, the file does not exist
//parameter 1: the file to be processed
//Parameter 2: off _t the number of bytes to be truncated (the final size of the file in bytes)
Iseek only records the new file offset in the kernel, it does not bow any IO operation, so it is not a system call IO, but the offset will be used for the next read/write operation
Pipes, FIFO s and sockets do not support setting file offsets and cannot call Iseek on them
6.3 od display file or stream
This is useful for accessing or visually inspecting characters in a file that cannot be displayed directly on the terminal.
od -A x -tcx filename //View file: address hexadecimal, data ASCII characters && hexadecimal
od -A d -tcd filename //View file: address decimal, data ASCII characters && decimal
7. dub and dub2 function redirection
Function: Copy an existing file descriptor to point to the same file.
int dup(int o1dfd);
Parameters: oldfd already has a file descriptor
Success: returns a new file descriptor:
Failed: -1 Set errno to the appropriate value.
test dub
#include <stdio .h> #include <std1ib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcnt1.h> int main(int argc, char *argv[]) { int fd = open(argv[1], 0_ RDWR); int newfd = dup(fd) ; printf("newfd = %d\n",newfd); int ret01=write(fd,"argv[1]\n" ,strlen("argv[1]\n")); printf("fd..... ret01=%d\n", ret01); int ret02=wri te(newfd, "newfd\n" , strlen("newfd\n")); printf("newf..... ret02=%d\n",ret02); return 0 }
test dub2
Function: Realize the pointing of the newfd file descriptor, and re-point to the file pointed to by the o1 dfd file descriptor
int dup2(int oldfd, int newfd);
success: return value = newfd file descriptor
Failed: -1 Set errno to the appropriate value.
#include <stdio.h> #include <std1ib .h> #include <string.h> #include <unistd.h> #include <sys/types .h> #include <sys/stat .h> #include <fcnt1 .h> int main(int argc, char *argv[]) { int fd01 = open(argv[1], 0_ RDWR |0_ APPEND); int fd02 = open(argv[2], 0_ RDWR |0_ APPEND); printf("fd02 = %d\n", fd02) ; int newfd = dup2 (fd01 , fd02) ; printf("newfd = %d\n",newfd) ; int ret01-write(fd01, "argv[1]\n",strlen("argv[1]\n")); printf("f..... ret01=%d\n" ,ret01); int ret02=wri te(fd02 , "newfd\n" , strlen("newfd\n")); printf("newf..... ret02=%d\n" , ret02); return 0: }