日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

信號是什么?

 

信號是事件發送時對進程的一種通知機制。有時也稱之為軟件中斷。信號與硬件中斷的相似之處在于打斷了程序執行的正常流程,大多情況下,無法預測信號到達的精確時間。

一個具有合適權限的進程不僅能夠向另一進程發送信號,也可以向自身發送信號。然而,發往進程的諸多信號,通常都是源于內核。

linux 信號可由如下條件產生:

  • 對于前臺進程,用戶可以通過輸入特殊的終端字符來給它發送信號。比如輸出 Ctrl+C 通常會給進程發送一個終端信號。
  • 系統異常。比如浮點異常和非法訪問段內存。
  • 系統狀態變化。比如 alarm 定時器到期引起的 SIGALRM 信號。
  • 運行 kill 命令或者調用 kill 函數。

服務器程序必須處理(或至少忽略)一些常見的信號,以免異常終止。

 

signalfd 是什么?

 

signalfd 是一個將信號抽象的文件描述符,將信號的異步處理轉換為文件的I/O 操作。通過文件描述符就緒的方法來通知信號的到來,當有信號發生時可以對其 read,這樣可以將信號的監聽放到 select、poll、epoll 等監聽隊列中。

通過文件描述符就緒的方法來通知信號的到來,當有信號發生時可以對其read,這樣可以將信號的監聽放到 select、poll、epoll 等監聽隊列中。

signalfd 的系統調用接口

#include <sys/signalfd.h>
int signalfd(int fd, const sigset_t *mask, int flags);

 

創建并返回一個用于所受信號的文件描述符。

mask:信號的集合,這里主要是你想監聽的信號的集合。

flags 可以使用以下標志位進行或(or)的結果:

  • SFD_NONBLOCK: 文件會被設置成 O_NONBLOCK,讀操作不阻塞。若不設置,一直阻塞直到計數器中的值大于0。
  • SFD_CLOEXEC: 在新的文件描述符上設置 close-on-exec ( FD_CLOEXEC ) 標志,簡單說就是 fork 子進程時不繼承。

 

獲取 signalfd 文件描述符后,我們來查看一下可以對其做哪些操作。

 

static const struct file_operations signalfd_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = signalfd_show_fdinfo,
#endif
.release = signalfd_release,
.poll = signalfd_poll,
.read = signalfd_read,
.llseek = noop_llseek,
};

 

通過上面 signalfd 實現的調用可知, 我們可以對 eventfd 進行 read、poll、close 等操作。

 

下面通過一個例子來了解下 signalfd 的使用方式,具體完整代碼可通過 man signalfd 獲取

 

int main(int argc, char *argv[])
{
...
//初始化信號集
sigemptyset(&mask);
//添加信號到信號集中
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
//將mask所指向的信號集中所包含的信號加到當前的信號掩碼中,作為新的信號屏蔽字,關閉內核的默認行為。
sigprocmask(SIG_BLOCK, &mask, NULL)
//創建 signalfd 文件描述符
sfd = signalfd(-1, &mask, 0);
for (;;) {
//阻塞等待信號發生并讀取。根據讀取的結果可以知道發生了什么信號
s = read(sfd, &fdsi, sizeof(fdsi));
if (fdsi.ssi_signo == SIGINT) {
printf("Got SIGINTn");
} else if (fdsi.ssi_signo == SIGQUIT) {
printf("Got SIGQUITn");
exit(EXIT_SUCCESS);
} else {
printf("Read unexpected signaln");
}
}
}

 

當沒有信號時,進程阻塞在 read 調用上,當有信號發生時,結果如下:

 

$ ./signalfd_demo
^C # Control-C generates SIGINT
Got SIGINT
^C
Got SIGINT
^ # Control- generates SIGQUIT
Got SIGQUIT
$

 

每次 Control + C,進程都會捕獲到一次信號,并打印具體信息。

通過如下查看,得到 signalfd 其實也是一個匿名 fd 類型。

[root@localhost ~]# ll /proc/48356/fd/
lrwx------ 1 root root 64 5月 23 11:54 3 ->anon_inode:[signalfd]

 

signalfd 源碼解析

 

接下來我們通過分析源碼的方式來探究 signalfd 的底層實現原理。

 

signalfd ( signalfd4 )

SYSCALL_DEFINE3(signalfd, int, ufd, sigset_t __user *, user_mask, size_t, sizemask)
{
...
return do_signalfd4(ufd, &mask, 0);
}
SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask, size_t, sizemask, int, flags)
{
...
return do_signalfd4(ufd, &mask, flags);
}
static int do_signalfd4(int ufd, sigset_t *mask, int flags)
{
struct signalfd_ctx *ctx;
sigdelsetmask(mask, sigmask(SIGKILL) | sigmask(SIGSTOP));
signotset(mask);
//內核新創建signalfd
if (ufd == -1) {
//創建一個signalfd_ctx內核結構
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
//設置信號集合
ctx->sigmask = *mask;
//獲取一個匿名句柄,file->f_op 設置為 signalfd_fops
ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK)));
} else { //已經創建signalfd
//合法性檢查
struct fd f = fdget(ufd);
//設置為新的值
ctx->sigmask = *mask;
//喚醒阻塞在當前進程的信號等待隊列
wake_up(¤t->sighand->signalfd_wqh);
fdput(f);
}
return ufd;
}

 

signalfd 的操作就是創建或者修改內核結構 signalfd_ctx,signalfd 本身也是一個匿名句柄。

對于 signalfd_ctx 內核結構,就只有一個字段,該字段記錄用戶設置的信號集合。

struct signalfd_ctx {
sigset_t sigmask;
};

signalfd_read

static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int nonblock = file->f_flags & O_NONBLOCK;
count /= sizeof(struct signalfd_siginfo);
//存放讀取到的數據結構
siginfo = (struct signalfd_siginfo __user *) buf;
do {
//從信號隊列中獲取一個信號,然后填充到info中
ret = signalfd_dequeue(ctx, &info, nonblock);
//把獲取到的信號填充到返回給用戶的數據結構中
ret = signalfd_copyinfo(siginfo, &info);
siginfo++;
total += ret;
nonblock = 1;
} while (--count);
return total ? total: ret;
}
static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info,
int nonblock)
{
ssize_t ret;
DECLARE_WAITQUEUE(wait, current);
spin_lock_irq(¤t->sighand->siglock);
//從掛起信號隊列中獲取信號
ret = dequeue_signal(current, &ctx->sigmask, info);
switch (ret) {
case 0: //若沒有信號,判斷是否需要阻塞
if (!nonblock)
break; //阻塞,跳出,往下走進行休眠
ret = -EAGAIN; //非阻塞,往下走到default,函數返回
default:
spin_unlock_irq(¤t->sighand->siglock);
return ret;
}
//把當前進程加入信號等待隊里中
add_wait_queue(¤t->sighand->signalfd_wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
//從掛起信號隊列中獲取信號
ret = dequeue_signal(current, &ctx->sigmask, info);
//存在信號,跳出循環,退出
if (ret != 0)
break;
//檢查當前進程是否有信號處理,返回不為0表示有信號需要處理
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
spin_unlock_irq(¤t->sighand->siglock);
schedule(); //進程調度,進入休眠
spin_lock_irq(¤t->sighand->siglock);
}
spin_unlock_irq(¤t->sighand->siglock);
//把當前進程從等待隊列中刪除
remove_wait_queue(¤t->sighand->signalfd_wqh, &wait);
__set_current_state(TASK_RUNNING);
return ret;
}

 

signalfd 的讀操作很簡單,主要操作如下:

  • 查看信號隊列中是否有信號,若有信號取出信號,并返回給用戶。
  • 若句柄是阻塞類型的,在沒有信號的情況下,則進程進入休眠,直到有信號到來。
  • 若句柄是非阻塞類型的,則直接返回 EAGAIN。
  •  

signalfd_poll

static __poll_t signalfd_poll(struct file *file, poll_table *wait)
{
struct signalfd_ctx *ctx = file->private_data;
__poll_t events = 0;
//把一個wait等待隊列掛到當前進程的信號等待隊列signalfd_wqh,其回調函數為ep_poll_callback
poll_wait(file, ¤t->sighand->signalfd_wqh, wait);
...
return events;
}


 

該函數的操作就是把 wait對象直接掛到當前進程的信號等待隊列signalfd_wqh 中,對比 timerfd 來講,區別在于 timerfd 的 wait 對象是掛到 timerfd_ctx->wqh 鏈表中。(詳情參看定時器timerfd原理)

 

signalfd 與 epoll 的結合

 

  • 當 epoll_ctl 調用 signalfd_poll 時,會把生成的 wait 對象掛到當前進程的信號等待隊列 signalfd_wqh 中,其中 wait 的回調函數為ep_poll_callback 。
  • 當觸發信號時,內核會遍歷 signalfd_wqh 上的 wait 對象,然后調用回調函數 ep_poll_callback,在該回調函數中會把觸發的事件發送到用戶態,然后喚醒由于調用 epoll_wait 而休眠的進程,喚醒后的進程調調用 ead 去取信號。

分享到:
標簽:Linux
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定