作者:-林之涵_396 | 来源:互联网 | 2023-09-11 18:34
1.从不可重入到可重入可重入指,函数被并发调用。若函数写全局变量,则不可重入,改为堆分配,则可重入。2.从可重入到信号安全信号安全指,函数A执行途中,处理信号,信号处理函
1. 从 不可重入 到 可重入
可重入指,函数被并发调用。
若函数写 全局变量,则不可重入,改为 堆分配,则可重入。
2. 从 可重入 到 信号安全
信号安全指,函数A执行途中,处理信号,信号处理函数中 又调用 函数A。
若函数使用 全局变量,则信号不安全。若改为 堆分配,也不安全,因为 malloc使用了锁,
所以函数使用 锁,则信号不安全,因为,函数执行途中已获得锁,但由于处理信号,有需要申请锁,导致死锁。
3. 怎么知道函数是否安全
可重入函数 后面 通常 加了 _r
信号安全函数 使用 man 7 signal 查看
4. 怎么避免信号导致不安全
首先 信号内使用 系统调用是安全的,所以将 信号转换为 pipe,通过监听pipe 处理信号。
应注意pipe应设置为 非阻塞,否则由于 管道满导致 写阻塞,导致死锁。
5. 示例
5.1 ngx对 strerror的处理
由于 strerror 不是 Async-Signal-Safe,所以采用预读取所有 errno 信息
static ngx_str_t ngx_unknown_error = ngx_string("Unknown error");
static ngx_str_t *ngx_sys_errlist;
static ngx_err_t ngx_first_error;
static ngx_err_t ngx_last_error;
u_char *
ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)
{
ngx_str_t *msg;
if (err >= ngx_first_error && err msg = &ngx_sys_errlist[err - ngx_first_error];
} else {
msg = &ngx_unknown_error;
}
size = ngx_min(size, msg->len);
return ngx_cpymem(errstr, msg->data, size);
}
ngx_int_t
ngx_strerror_init(void)
{
char *msg;
u_char *p;
size_t len;
ngx_err_t err;
ngx_first_error = 0;
ngx_last_error = NGX_SYS_NERR; // errno 数量
len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t);
ngx_sys_errlist = malloc(len);
if (ngx_sys_errlist == NULL) {
goto failed;
}
for (err = ngx_first_error; err msg = strerror(err);
if (msg == NULL) {
ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error;
continue;
}
len = ngx_strlen(msg);
p = malloc(len);
if (p == NULL) {
goto failed;
}
ngx_memcpy(p, msg, len);
ngx_sys_errlist[err - ngx_first_error].len = len;
ngx_sys_errlist[err - ngx_first_error].data = p;
}
return NGX_OK;
failed:
err = errno;
ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));
return NGX_ERROR;
}