1. Signal
Nginx uses a lot of signals when managing master and worker processes. The first 31 signals that are defined by Linux are the most commonly used, and Nginx uses them by redefining the processing of some of these signals, such as receiving a SIGUSR1 signal, which means that the file needs to be reopened.
When using a signal, Nginx defines the behavior of a ngx_signal_t struct used to describe the received signal:
typedef struct { // 需要处理的信号 int signo; // 信号对应的字符串名称 char *signame; // 这个信号对应着的 Nginx 命令 char *name; // 收到 signo 信号后就会回调 handler 方法 void (*handler)(int signo, siginfo_t *siginfo, void *ucontext);}ngx_signal_t;
Based on the ngx_signal_t structure, Nginx defines an array that defines all the signals that the process will process. Such as:
#define Ngx_reconfigure_signal htpngx_signal_t signals[] = {{Ngx_signal_value (ngx_reconfigure_signal), "SIG" ng X_value (ngx_reconfigure_signal), "Reload", Ngx_signal_handler}, {Ngx_signal_value (ngx_reopen_signal), "SIG" Ngx_value (ngx_reopen_signal), "REOPEN", Ngx_signal_handler}, {Ngx_signal_value (Ngx_noaccept_signa L), "SIG" Ngx_value (ngx_noaccept_signal), "", Ngx_signal_handler}, {Ngx_signal_value (ngx_terminate_sig NAL), "SIG" Ngx_value (ngx_terminate_signal), "Stop", Ngx_signal_handler}, {Ngx_signal_value (ngx_shutdo wn_signal), "SIG" Ngx_value (ngx_shutdown_signal), "Quit", Ngx_signal_handler}, {Ngx_signal_value (ngx_c hangebin_signal), "SIG" Ngx_value (ngx_changebin_signal), "", Ngx_signal_handler}, {sigalrm, "sigalrm", "", Ngx_signal_handler}, {SIGINT, "SIGINT", "" ", Ngx_signal_handler}, {SIGIO," SIGIO "," ", Ngx_signal_handler}, {SIGCHLD, "SIGCHLD", "", Ngx_signal_handler}, {Sigsys, "Sigsys, Sig_ign", "", null}, {sigpipe, "sigpipe, Sig_ign", "", null}, { 0, NULL, "", NULL}};
From the array, join the received SIGHUP signal, will call the Ngx_signal_handler method to process, in order to re-read the configuration file, or when you receive the following command sent by the user:
./nginx -s reload
This newly-started Nginx process sends a SIGHUP signal to the actual running Nginx server process (the Nginx process that pulls up after executing this command does not restart the server, but is used only to send a signal, which resets in the Ngx_get_options method Ngx_signa The global variable, while the main method checks its non 0 o'clock, calls the Ngx_signal_process method to send a signal to the running Nginx server, after which the main method returns, and the newly initiated Nginx process exits), so that the running service process will also call NGX _signal_handler method to process this signal. The specific code flow is as follows.
Newly-started Nginx process:
int ngx_cdeclmain(int argc, char *const *argv){ ... // 这个 ngx_signal 全局变量会在 ngx_get_options 函数中被赋值 // (检测到输入的命令行参数中有 -s,则 ngx_signal 的值即为 "reload") if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); } ...}
The Ngx_signal_process function is then called, and the newly-started Nginx exits immediately after the call is completed.
Ngx_int_tngx_signal_process (ngx_cycle_t *cycle, char *sig) {ssize_t n; ngx_pid_t pid; ngx_file_t file; ngx_core_conf_t *CCF; U_char Buf[ngx_int64_len + 2]; CCF = (ngx_core_conf_t *) ngx_get_conf (cycle->conf_ctx, ngx_core_module); Ngx_memzero (&file, sizeof (ngx_file_t)); Logs/nginx.pid file, which holds the process ID of the master process File.name = ccf->pid; File.log = cycle->log; FILE.FD = Ngx_open_file (File.name.data, Ngx_file_rdonly, Ngx_file_open, ngx_file_default_acces S); if (file.fd = = ngx_invalid_file) {return 1; } n = ngx_read_file (&file, buf, Ngx_int64_len + 2, 0); if (Ngx_close_file (file.fd) = = Ngx_file_error) {} if (n = = ngx_error) {return 1; } while (n--&& (buf[n] = = CR | | buf[n] = LF)) {/* void */}//Get the master process ID of the running Nginx server PID = Ngx_atoi (buf, ++n); if (PID = = (ngx_pid_T) {ngx_error) {return 1; } return Ngx_os_signal_process (cycle, SIG, PID);}
After the function obtains the ID of the master process from the Logs/nginx.pid file to the running Nginx server, it calls the Ngx_os_signal_process function for processing.
ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid){ ngx_signal_t *sig; // 首先确定将要发送给 pid 的命令 name 在 signals 数组中存在 for (sig == signals; sig->signo != 0; sig++) { if (ngx_strcmp(name, sig->name) == 0) { // 找到后向该 pid 发送 name 命令对应的信号 if (kill(pid, sig->signo) != -1) { return 0; } } } return 1;}
After the signal is sent, the newly-activated nginx (ie./nginx-s reload) exits directly. Then, after the running Nginx server receives the signal (SIGHUP) for the reload command, the Ngx_signal_handler function is called Be processed.
Nginx after defining the signals array of type ngx_signal_t, the Ngx_init_signals method initializes all signals:
ngx_int_tngx_init_signals (ngx_log_t *log) {ngx_signal_t *sig; struct Sigaction sa; Iterate through the signals array, handling each structure of the ngx_signal_t type for (sig = Signals; Sig->signo! = 0; sig++) {Ngx_memzero (&sa, sizeof (struct sigaction)); if (Sig->handler) {//Set signal processing method is handler sa.sa_sigaction = sig->handler; Sa.sa_flags = Sa_siginfo; } else {sa.sa_handler = sig_ign; }//The signal mask Sa.sa_mask is all set to 0 sigemptyset (&sa.sa_mask); if (Sigaction (Sig->signo, &sa, NULL) = =-1) {#if (ngx_valgrind) ngx_log_error (Ngx_log_alert, log, ngx_er Rno, "Sigaction (%s) failed, ignored", sig->signame); #else Ngx_log_error (Ngx_log_eme RG, log, Ngx_errno, "Sigaction (%s) failed", Sig->signame); return ngx_error; #endif}} return NGX_OK;}
Call this function to set the signal processing function for all the specified signals, the process can process the signal. If Nginx is required to process a new signal, you can add a new ngx_signal_t member directly to the signals array.
2. Signal Volume
Semaphores are used to ensure that two or more pieces of code are not concurrently accessed, and are a tool to ensure an orderly access to shared resources. Using semaphores as a mutex can lead to process sleep, so use caution, especially for servers in which Nginx, which processes tens of thousands of requests at the same time, can cause a significant decrease in performance.
Nginx only uses the semaphore as a simple mutex. Before using semaphores, first call the Sem_init method to initialize the semaphore.
2.1 Ngx_shmtx_create: Initialization of a semaphore
ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name){ mtx->lock = &addr->lock; if (mtx->spin == (ngx_uint_t) -1) { return NGX_OK; } mtx->spin = 2048;#if (NGX_HAVE_POSIX_SEM) mtx->wait = &addr->wait; // 初始化一个未命名的信号量,并且该信号量是在进程间共享, // 信号量的初值为 0 if (sem_init(&mtx->sem, 1, 0) == -1) { } else { mtx->semaphort = 1; }#endif return NGX_OK;}
2.2 Ngx_shmtx_destroy: Destroying the semaphore
voidngx_shmtx_destroy(ngx_shmtx_t *mtx){#if (NGX_HAVE_POSIX_SEM) if (mtx->semaphore) { // 销毁该信号量, // 注,只有在不存在进程在等待一个信号量时才能安全销毁信号量 if (sem_destroy(&mtx->sem) == -1) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, "sem_destroy() failed"); } }#endif}
3. File lock
The Linux kernel provides file-based mutexes, and the Nginx framework encapsulates 3 methods that provide Nginx modules and file mutexes to protect shared data.
The file-based mutex in Nginx is implemented by the Fcntl method:
int fcntl(int fd, int cmd, struct flock *lock);
- FD: Must be a handle to a file that has been successfully opened. In fact, the file path specified by the Lock_file configuration entry in the Nginx.conf file, which is used for the file mutex, is opened and the resulting handle is passed to the Fcntl method as an FD parameter, providing a locking mechanism.
- cmd: Represents the lock operation performed. There are only two values in Nginx: F_setlk and F_SETLKW, both of which represent an attempt to obtain a mutex, but when
- uses F_SETLK, if the mutex is already occupied by another process, the Fcntl method does not wait for the other process to release the lock and return it after it has acquired the lock. Instead of immediately returning to get the mutex, the
- is different when using F_SETLKW, the Fcntl method waits until the lock is occupied, and the current process blocks in the Fcntl method when no other process releases it, causing the current process to move from the executable state to sleep.
-
Lock: Describes the information for this lock. The type is a flock struct, as follows:
struct Flock {...//lock type, value F_rdlck, F_wrlck, F_unlckshort l_type;//lock region Start address relative position short L_ whence;//Lock zone Start address offset, together with l_whence to determine the lock area long l_start;//lock length, 0 means lock to the end of the file long l_len;//the process Idlong l_pid;}
As you can see from the flock structure, the function of a file lock is not limited to a common mutex, it can also lock some of the contents of the file. However, the Nginx encapsulated file lock is only used to protect the order of code snippets (for example, when load balancing is used to ensure that only one worker process can handle a new TCP connection at the same time), it is much simpler to use: a Lock_file file corresponds to a global mutex, And it takes effect on either the master process or the worker process. Therefore, for L_start, L_len, l_pid, are filled with 0, and l_whence is filled with seek_set, only need this file to provide a lock. The value of L_type depends on whether the user wants to implement a blocking sleep lock or to implement a lock that does not block sleep.
For a file lock, Nginx encapsulates 3 methods:
- NGX_TRYLOCK_FD: Implements a mutex that does not block the process and does not cause the process to go to sleep;
- NGX_LOCK_FD: The supplied mutex will cause the current process to go to sleep when the lock has been taken by another process, until the current process is re-dispatched by the Linux kernel until the lock is successfully received, so it is a blocking operation;
- NGX_UNLOCK_FD: Used to release mutexes.
3.1 NGX_TRYLOCK_FD
ngx_err_t ngx_trylock_fd(ngx_fd_t fd){ struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; // F_SETLK 表示若获取写锁失败则立刻返回,不会导致当前进程阻塞 if (fcntl(fd, F_SETLK, &fl) == -1) { // 若返回的错误码为 NGX_EAGAIN 或 NGX_EACCESS 时表示 // 当前没有拿到互斥锁,否则认为 fcntl 执行错误 return ngx_errno; } return 0;}
3.2 ngx_lock_fd
ngx_err_tngx_lock_fd(ngx_fd_t fd){ struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; // F_SETLKW 表示当没有获取到锁时将会导致当前进程进入阻塞, // 直到获取到锁时才会再次获得调度 if (fcntl(fd, F_SETLKW, &fl) == -1) { return ngx_errno; } return 0;}
3.3 ngx_unlock_fd
ngx_err_tngx_unlock_fd(ngx_fd_t fd){ struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; // 释放当前进程已经拿到的互斥锁 if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0;}
The communication mechanism between nginx processes (signal, semaphore, file Lock)