(UNIX basics)
1. Unix Architecture
Vc/U2tfuuvOjqTwvcD4NCjxoMiBpZD0 = "2-List the names of all files in a directory, that is, the implementation of the command ls"> 2. List the names of all files in a directory, that is, the implementation of the command ls
Figure 1.3. List all the files in a directory
#include "apue.h"#include
int main(int argc, char *argv[]){ DIR *dp; struct dirent *dirp; if (argc != 2) err_quit("usage: ls directory_name"); if ((dp = opendir(argv[1])) == NULL) err_sys("can't open %s", argv[1]); while ((dirp = readdir(dp)) != NULL) printf("%s\n", dirp->d_name); closedir(dp); exit(0);}
3 No buffer IO (open, read, write, lseek, close)
Figure 1.4. List all the files in a directory
#include "apue.h"#define BUFFSIZE 4096int main(void){ int n; char buf[BUFFSIZE]; while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error"); if (n < 0) err_sys("read error"); exit(0);}
The constants STDIN_FILENO and STDOUT_FILENO are defined in
4. Standard IO
Standard I/O functions provide a buffer-free interface. It has two advantages:
1) Don't worry about how to select the optimal buffer size
2) simplified processing of input rows
The function getc reads one character at a time, and this character is written by putc. After the last byte of input has been read, getc returns the constant EOF (defined in
#include "apue.h"int main(void){ int c; while ((c = getc(stdin)) != EOF) if (putc(c, stdout) == EOF) err_sys("output error"); if (ferror(stdin)) err_sys("input error"); exit(0);}
5 print the process ID
Figure 1.6. Print the process ID
#include "apue.h"int main(void){ printf("hello world from process ID %d\n", getpid()); exit(0);}
When this program runs, it cballs the function getpid to obtain its process ID.
6. Enter and execute the READ command from the standard.
Figure 1.7. Read commands from standard input and execute them
# Include "apue. h" # include
Int main (void) {char buf [MAXLINE];/* from apue. h */pid_t pid; int status; printf ("%");/* print prompt (printf requires % to print %) */while (fgets (buf, MAXLINE, stdin )! = NULL) {if (buf [strlen (buf)-1] = "\ n") buf [strlen (buf)-1] = 0; /* replace newline with null */if (pid = fork () <0) {err_sys ("fork error");} else if (pid = 0) {/* child */execlp (buf, buf, (char *) 0); err_ret ("couldn't execute: % s", buf); exit (127 );} /* parent * // waitpid () will temporarily stop the execution of the current process until a signal arrives or the sub-process ends. If (pid = waitpid (pid, & status, 0) <0) err_sys ("waitpid error"); printf ("% ");} exit (0 );}
We call fork to create a new process, which is a copy of the caller. we say that the caller is the parent and that the newly created process is the child. then fork returns the non-negative process ID of the new child process to the parent, and returns 0 to the child. because fork creates a new process, we say that it is called once by the parent but returns twice in the parent and in the child.
The Fork () function returns two times at a time, one is its own process ID, and the other is a sub-process, which is 0.
7 Demonstrate strerror and perror
#include "apue.h"#include
Int main(int argc, char *argv[]){ fprintf(stderr, "EACCES: %s\n", strerror(EACCES)); errno = ENOENT; perror(argv[0]); exit(0);}
The C standard defines two functions that help print error information
# Include
Char * strerror (int errnum); // This function maps errnum (which is usually the errno value) to an error message string and returns a pointer to this string. // The perror function generates an error message when a standard error occurs based on the current errno value, and then returns # include
Void perror (const char * msg); // it first outputs the string directed by msg, followed by a colon, a space, and an error message corresponding to the errno value, the last line break
8 Print user ID and group ID
#include "apue.h"Int main(void){ printf("uid = %d, gid = %d\n", getuid(), getgid()); exit(0);}
Because it is a root user, uid is 0. I have not assigned a group, so gid is also 0
9 signal
Figure 1.10. Read commands from standard input and execute them
#include "apue.h" #include
static void sig_int(int); /* our signal-catching function */ Int main(void) { char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; if (signal(SIGINT, sig_int) == SIG_ERR) err_sys("signal error"); printf("%% "); /* print prompt (printf requires %% to print %) */ while (fgets(buf, MAXLINE, stdin) != NULL) { if (buf[strlen(buf) - 1] == "\n") buf[strlen(buf) - 1] = 0; /* replace newline with null */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ execlp(buf, buf, (char *)0); err_ret("couldn't execute: %s", buf); exit(127); } /* parent */ if ((pid = waitpid(pid, &status, 0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); } void sig_int(int signo) { printf("interrupt\n%% "); }
A signal is a technology that notifies the process of a certain situation. For example, if a process performs a division operation with a Division value of 0, it sends the name SIDFPE (floating point exception) to the process. There are three options for process processing:
1. Ignore this signal. Some signals indicate hardware exceptions, such as dividing by 0 or a unit other than the access address space. The consequences of these exceptions are unknown and therefore are not recommended.
2. Process by default. For processes divided by 0, the system terminates the process by default.
3. Provide a signal. This function is called when a signal occurs, which is called capturing the signal. We need to provide self-compiled functions to process it.
10 system calls and database functions
System calling actually refers to the underlying call. In the linux program design, it refers to the underlying call. It is designed for hardware. Library Function calls are intended for application development, which is equivalent to the application's api
1. System Call
Functions provided by the system call, such as open, close, read, write, and ioctl, must contain the header file unistd. h. take write as an example: its function prototype is size_t write (int fd, const void * buf, size_t nbytes), and its operation object is file descriptor or file handle fd (file descriptor ), to write a file, you must first open a file with the write permission called by the open system to obtain the fd of the opened file, for example, fd = open (\ "/dev/video \", o_RDWR ). Fd is an integer value. Each time a new file is opened, the obtained fd is the current maximum fd plus 1 (three file descriptor values are allocated by default in Linux: 0-standard input, 1-standard output, 2-standard error)
System calls are usually used to access the underlying file (low-level file access). For example, you can directly access the device file in the driver.
System calls are related to operating systems, so there is generally no cross-operating system portability.
2. database functions
File operation functions provided by standard C library functions, such as fopen, fread, fwrite, fclose, fflush, and fseek, must contain the header file stdio. h. taking fwrite as an example, its function prototype is size_t fwrite (const void * buffer, size_t size, size_t item_num, FILE * pf), and its operation object is FILE pointer FILE * pf, to write a FILE, you must use the fopen function with the write permission to open a FILE and obtain the FILE structure pointer pf, for example, pf = fopen (\ "~ /Proj/filename \ ", \" w \"). In fact, because library functions ultimately implement file operations through system calls, each FILE structure pointer obtained by opening a FILE has a FILE descriptor fd corresponding to the kernel space. There are also predefined FILE pointers: stdin-standard input, stdout-standard output, stderr-standard error.
Library Function calls are usually used to access general files in applications.
Library Function calls are system-independent, so they are portable.
Because the library function is called Based on the C library, it is impossible to use it for device operations in the driver of the kernel space.
The usage of library functions also has system calling overhead. Why not use system calling directly?
This is because reading and writing files is usually a large amount of data (this large amount is relative to the data operation unit implemented by the underlying driver's system call, using library functions can greatly reduce the number of system calls. This result is due to the buffer technology. In user space and kernel space, buffer is used for file operations. For example, if you use fwrite to write files, the content is first written to the user space buffer, when the user space buffer is full or the write operation ends, the user buffer content is written to the kernel buffer. In the same way, when the kernel buffer is full or the write ends, the kernel buffer content is written to the corresponding hardware media of the file.