Linux/Unix Programming Manual -2
The basic concept
The kernel
The kernel functions
- Process scheduling (which processes get CPU usage and how long each process uses it)
- Memory management (virtual memory management: isolation between processes and between processes and kernel; Just keep part of the process in memory to increase CPU usage.
- Providing a file system
- Creating a terminating process
- Provides device access interfaces
- Provides system call apis
User state and core state
Hardware executes instructions to switch the CPU between the two states. User mode running can only access user mode memory, operating system bit domain kernel space (shell is user process)
File descriptor: Essentially an index pointing to the open file record table maintained by the kernel for each process
process
Process memory layout
- Text: Instructions for a process
- Data: program static variables
- Heap: Dynamically allocates additional memory
- Stack :(threads only have their own stack)
Process creation
- Fork: the child inherits the parent’s data segment, heap segment, and stack segment, creates a copy in memory, and shares read-only program text
- Execve loads a new program that destroys existing text, data, heap, and stack segments
Special processes – IDEL processes (the first process, N processors, N idle processes, when the CPU is idle) – Init processes – Kthread processes
Memory mapping (mmap()
)
- File mapping: Maps part of a file to the virtual memory of a process
- Anonymous mapping: Determines whether the content of the mapping is visible to other processes by passing in flags (usually used for parent-child communication)
Linux/Unix Programming Manual -3
System programming concepts
System call flow (General: Application – >C shell function (exception positive) – > System_call () routine (exception negative) – > service routine (exception positive))
- The system call is made through the shell function of the C library
- The shell function copies the call parameters to the register and the system call number to the CPU register (%eax).
- The shell function executes an interrupt instruction (int 0x80) that causes the processor to switch from user to kernel state and executes the code pointed to by the interrupt vector (0x80).
- In response to 0x80, the kernel calls system_call() to handle the interrupt
- The kernel stack holds register values
- Verify the validity of the system call number, parameter validity, execute the task, and return the result to the system_call() list
- Restores each register value from the kernel stack, putting the system call return value on the stack.
- Return to the shell function and switch to user mode.
- If the system call returns an error, the shell uses that value to set the global variable errno. The shell then returns the caller with an integer value (ex:-1) indicating success.
tip:
- Some system calls still return -1 after success (ex: getPriority ()). Errno should be set to 0 before the call for easy troubleshooting (p39).
void jkstack(void) __attribute__((noreturn));
Is to tell the compiler that the function will not be returned to the caller, so that the compiler can remove unnecessary function return code during optimization.
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
#define LINUX_REBOOT_MAGIC2A 85072278
#define LINUX_REBOOT_MAGIC2B 369367448
#define LINUX_REBOOT_MAGIC2C 537993216
/*672274793 = 0x28121969 85072278 = 0x05121996 369367448 = 0x16041998 537993216 = 0x20112000*/
Copy the code
Linux/Unix Programming Manual -4 (File I/O)
I/ O-related system calls
char buffer[BUFFER_SIZE]
/ / 1
fd = open(pathname,flags,mode) Flags specifies how to open the file and whether to create it. Mode specifies the permission to create the file
/ / 2
numread=read(fd, buffer, count) // Read up to count bytes of data from the fd and store them in buffer
/ / 3
numwritten=write(fd,buffer,count) // Read at most count from buffer and write fd
/ / 4
status=close(fd) // Release the fd and the corresponding kernel resources
Copy the code
/proc/$pid/fdinfo gets the fd opened by any process in the system
Pos: 0 // Current file offset flags: 0100002 mnt_id: 17Copy the code
// tee.c
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[]){
int append = 0;
int opt;
while ((opt=getopt(argc, argv, "a"))! =1) {if ((unsigned char) opt =='a'){ append=1;}
}
if (optind>=argc) {
printf("%s [-a] file\n", argv[0]);
}
intfd = open(argv[optind], O_CREAT|O_WRONLY|(append? O_APPEND:O_TRUNC), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if (fd == - 1) {printf("opening file %s", argv[optind]); }ssize_t numRead;
char buf[BUF_SIZE];
int oFd[2] = {fd, STDOUT_FILENO};
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE))>0) {for (int i=0; i<(int) (sizeof(oFd)/sizeof(oFd[0])); i++){if(write(fd, buf, numRead)! =numRead){printf("couldn't wirte whole buff"); }}}if (numRead==- 1) {printf("read error");
}
if (close(fd)==- 1) {printf("colse error");
}
_exit(- 1);
}
Copy the code
Linux/Unix Programming Manual -5 (Delve deeper into file I/O)
Relationship between file descriptors and open files (important)
- The file descriptor table at the process level
- A set of flags that control file descriptor operations (close-on-exec flags)
- Opens a reference to a file handle
- Open file table at system level
- Current file offset
- Flags the status parameter used to open the file
- File access mode
- Signal driven I/ O-related Settings
- A reference to the file i-node
- I-node table of the file system
- File types (FIFO, socket, regular file)
- Pointer to the list of locks held by the file
- Various properties of the file
The file descriptors for processes A and B point to the same file handle: either fork() inheritance or UNIX domain socket passing;
The file descriptors of processes A and B point to different file handles. Different file handles point to the same entry in the I-Node list: Open () on the same file. (Unified process opens () twice on the same file.)
Different file descriptors in the same process point to the same handle, possibly the DUP () implementation
Copy the file descriptor
./myscripts > t.log 2>&1
#include<unistd.h>
#include<fcntl.h>
int newfd;
int dup(int oldfd);
int dup2(int oldfd, int newfd); // Ignore the close(newfd) error
int fcntl(int fd, int cmd, ...);
FCNTL (olDFd, F_DUPFD, startfd) is greater than or equal to the minimum unused value of startfd as the descriptor number
// The following three lines are consistent when successful; Dup2 does not close(newfd) when oldfg does not exist, and does nothing when new=old
close(2); newfd=dup(1)
newfd=dup2(1.2)
close(2); newfd=fcntl(1, F_DUPFD,2)
#include<unistd.d>
// Thread safety read, write
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwirte(int fd, const void *buf, size_t count, off_t offset);
#include<sys/uio.h>
struct iovec{
void *iov_base;
size_t iov_len;
}
// Multiple buffer reads and writes
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
Copy the code
Non-blocking I/O (Critical)
Specify O_NONBLOCK when opening the file
- O_NONBLOCK = O_NONBLOCK = O_NONBLOCK = O_NONBLOCK = O_NONBLOCK = O_NONBLOCK
- After the open succeeds, subsequent I/ OS also fail
- Pipes, FIFO, sockets, and devices (terminal/pseudo-terminal) support non-blocking mode
- Normal file I/O does not block (kernel buffers) and O_NONBLOCK is generally ignored
Large file I/O (LFS)
Greater than 2GB (32-bit)
Two modes are supported:
- Large file operation API designed by LFS
- Define the macro _FILE_OFFSET_BITS as 64
/ dev/fd directory
The /dev/fd for each process is linked to /proc/$pid/fd
Is/dev / [stdin | stdout | stderr] respectively link to/dev/fd / [0 | 1 | 2]
// The following is equivalent for the same process
fd = open("/dev/fd/1", O_WRONLY)
fd = dup(1)
Copy the code
Implementation of temporary files: unlink files immediately after opening, the open file table maintained by the process will be released at the end of the process.