应用层获得SIGIO信号如何区分是kill_fasync的第3个参数(poll_in)或(poll_out)

羅昇 咸鱼  2017-11-29 04:12:16
static ssize_t xxx_write(...)
{
...
echo "....">设备 完成写操作时发送信号
if(dev->async_queue)
kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
}
static ssize_t xxx_read(...)
{
...
完成读设备操作时发送信号
if(dev->async_queue)
kill_fasync(&dev->async_queue,SIGIO,POLL_OUT);
}
以上是驱动中的读写两个函数,在应用程序中都会获得SIGIO信号
signal(SIGIO,xxx);xxx为处理函数
问题是,如何在应用程序中区分到底是读完成后产生的SIGIO信号,还是写完成后产生的
请教下应用程序怎么在接受信号后把读写分开
例如,写完成后产生SIGIO信号,进入处理函数,经过什么判断知道是写完成,我们就进行读操作
反之一样
还有,kill_fasync莫非只能传SIGIO信号?如果是传其他信号第三个参数的填写有何要求?
...全文
328 1 收藏 11
写回复
11 条回复
羅昇 2018年12月17日
引用 10 楼 adaslove520 的回复:
通过F_SETSIG可以在应用层区分kill_fasync的第三个参数,现象如下: 1.在未调用 F_SETSIG 设置通知的信号时(如指定为SIGIO、SIGUSR1等),默认采用SIGIO信号,测试POLL_IN、POLL_OUT、POLL_MSG,接收到的si_code值都是128; 原因是F_SETSIG未设置时,kill_fasync默认调用do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group),在最终调用的 __send_signal 函数中,q->info.si_code = SI_KERNEL,导致应用层进程接收到的 si_code 值变为128(SI_KERNEL的值为128)。 参考链接:F_SETSIG详细说明,http://www.tutorialspoint.com/unix_system_calls/fcntl.htm 2.F_SETSIG设置信号为SIGIO时,kill_fasync第三个参数分别为POLL_IN/POLL_OUT/POLL_MSG时,应用层接收时,值分别为0x1,0x2,0x3; 因此,在设置F_SETSIG信号情况下,应用层可通过 si_code 值来区分是哪一种 POLL_xx 类型; 3.POLL_IN/POLL_OUT/POLL_MSG内核中该三宏的实际值应该为0x20001,0x20002,0x20003,应该是在最终发送信号函数 __send_signal 中值被改变,这块没有深入研究; 注:内核版本 Linux version 4.1.22 以下贴下我的验证代码: SIGIO也可以改为其他信号,如SIGUSR1,已验证通过。 1.驱动层: 通过条件选择,循环发送 kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_IN); kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_OUT); kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_MSG); 2.应用层:
int main(int argc,char** argv)
{
    ...
    struct sigaction sigio_act;
	//设置信号集
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGIO);
	
	//设置 SIGIO 对应的信号处理函数
    sigio_act.sa_sigaction = sig_handler;
	//当sa_flags包含 SA_SIGINFO 标志时,系统使用sa_sigaction函数作为信号处理函数,否则使用sa_handler.
	sigio_act.sa_flags = SA_SIGINFO;
	if(sigaction(SIGIO, &sigio_act, NULL) < 0) {
		error_msg("install SIGIO sigaction error");
	}
	
    fcntl(devmem_fd, F_SETOWN, getpid());
	int oflags = fcntl(devmem_fd, F_GETFL);
	fcntl(devmem_fd, F_SETFL, oflags | FASYNC);
	/*若F_SETSIG未设置, 默认发送 SIGIO, 即使设置 SA_SIGINFO, siginfo_t 附加信息仍为默认内容, si_code 为默认 SI_KERNEL(值为128);
     *若设置 F_SETSIG 为 仍一个有效信号(包括SIGIO),即可收到完整的 siginfo_t 附加信息;
	 */
	fcntl(devmem_fd, F_SETSIG, SIGIO);	//fcntl(devmem_fd, F_SETSIG, SIGUSR1);
	while(1){
		sleep(1);
	}
    ...
} 
没想到过了一年了还有人能发现这个问题,谢啦!
回复 点赞
糯米团阿离 2018年12月13日
通过F_SETSIG可以在应用层区分kill_fasync的第三个参数,现象如下: 1.在未调用 F_SETSIG 设置通知的信号时(如指定为SIGIO、SIGUSR1等),默认采用SIGIO信号,测试POLL_IN、POLL_OUT、POLL_MSG,接收到的si_code值都是128; 原因是F_SETSIG未设置时,kill_fasync默认调用do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group),在最终调用的 __send_signal 函数中,q->info.si_code = SI_KERNEL,导致应用层进程接收到的 si_code 值变为128(SI_KERNEL的值为128)。 参考链接:F_SETSIG详细说明,http://www.tutorialspoint.com/unix_system_calls/fcntl.htm 2.F_SETSIG设置信号为SIGIO时,kill_fasync第三个参数分别为POLL_IN/POLL_OUT/POLL_MSG时,应用层接收时,值分别为0x1,0x2,0x3; 因此,在设置F_SETSIG信号情况下,应用层可通过 si_code 值来区分是哪一种 POLL_xx 类型; 3.POLL_IN/POLL_OUT/POLL_MSG内核中该三宏的实际值应该为0x20001,0x20002,0x20003,应该是在最终发送信号函数 __send_signal 中值被改变,这块没有深入研究; 注:内核版本 Linux version 4.1.22 以下贴下我的验证代码: SIGIO也可以改为其他信号,如SIGUSR1,已验证通过。 1.驱动层: 通过条件选择,循环发送 kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_IN); kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_OUT); kill_fasync(&fpgacomm_devp->async_queue, SIGIO, POLL_MSG); 2.应用层:
int main(int argc,char** argv)
{
    ...
    struct sigaction sigio_act;
	//设置信号集
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGIO);
	
	//设置 SIGIO 对应的信号处理函数
    sigio_act.sa_sigaction = sig_handler;
	//当sa_flags包含 SA_SIGINFO 标志时,系统使用sa_sigaction函数作为信号处理函数,否则使用sa_handler.
	sigio_act.sa_flags = SA_SIGINFO;
	if(sigaction(SIGIO, &sigio_act, NULL) < 0) {
		error_msg("install SIGIO sigaction error");
	}
	
    fcntl(devmem_fd, F_SETOWN, getpid());
	int oflags = fcntl(devmem_fd, F_GETFL);
	fcntl(devmem_fd, F_SETFL, oflags | FASYNC);
	/*若F_SETSIG未设置, 默认发送 SIGIO, 即使设置 SA_SIGINFO, siginfo_t 附加信息仍为默认内容, si_code 为默认 SI_KERNEL(值为128);
     *若设置 F_SETSIG 为 仍一个有效信号(包括SIGIO),即可收到完整的 siginfo_t 附加信息;
	 */
	fcntl(devmem_fd, F_SETSIG, SIGIO);	//fcntl(devmem_fd, F_SETSIG, SIGUSR1);
	while(1){
		sleep(1);
	}
    ...
} 
回复 点赞
糯米团阿离 2018年12月13日
引用 8 楼 羅昇 的回复:
测试POLL_IN和POLL_OUT时接收到的si_code值都是128
@羅昇 针对这个问题,我发现以下现象:
回复 点赞
jklinux 2017年11月30日
sigaction函数你是怎样调用?
回复 点赞
jklinux 2017年11月30日
按理, siginfo_t里的si_code就是才对的
回复 点赞
羅昇 2017年11月30日
我用的内核版本 是4.10,问题是我还是没搞懂应用怎么处理驱动信号中的附加参数
回复 点赞
jklinux 2017年11月30日
也有可能你的内核版本还没用上这个参数。我在linux-4.11上看到有设:
  
// reason就是传入的POLL_IN/POLL_OUT
450 static void send_sigio_to_task(struct task_struct *p,
451                    struct fown_struct *fown,
452                    int fd, int reason, int group)
453 {
454     /*
455      * F_SETSIG can change ->signum lockless in parallel, make
456      * sure we read it once and use the same value throughout.
457      */
458     int signum = ACCESS_ONCE(fown->signum);
459 
460     if (!sigio_perm(p, fown, signum))
461         return;
462 
463     switch (signum) {
464         siginfo_t si;
465         default:
466             /* Queue a rt signal with the appropriate fd as its
467                value.  We use SI_SIGIO as the source, not 
468                SI_KERNEL, since kernel signals always get 
469                delivered even if we can't queue.  Failure to
470                queue in this case _should_ be reported; we fall
471                back to SIGIO in that case. --sct */
472             si.si_signo = signum;
473             si.si_errno = 0;
474                 si.si_code  = reason;
475             /* Make sure we are called with one of the POLL_*
476                reasons, otherwise we could leak kernel stack into
477                userspace.  */
478             BUG_ON((reason & __SI_MASK) != __SI_POLL);
479             if (reason - POLL_IN >= NSIGPOLL)
480                 si.si_band  = ~0L;
481             else
482                 si.si_band = band_table[reason - POLL_IN];
483             si.si_fd    = fd;
484             if (!do_send_sig_info(signum, &si, p, group))
485                 break;
486         /* fall-through: fall back on the old plain SIGIO signal */
487         case 0:
488             do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group);
489     }
490 }

回复 点赞
羅昇 2017年11月30日
引用 1 楼 jklinux 的回复:
可以试下用sigaction函数代替signal函数来处理信号, sigaction里可用void (*sa_sigaction)(int, siginfo_t *, void *);处理函数,可获取的参数较多.查看下siginfo_t里有没有符合你的数据了
我试了下sigaction函数并把siginfo的信息都打印了出来

union sigval {
	int sival_int;
	void *sival_ptr;
};
typedef struct {
	int si_signo;
	int si_code;
	union sigval si_value;
	int si_errno;
	pid_t si_pid;
	uid_t si_uid;
	void *si_addr;
	int si_status;
	int si_band;
} siginfo_t;

struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction=xxx_handler;
act.sa_flags=SA_SIGINFO;
if(sigaction(SIGIO,&act,NULL) < 0)
{
        printf("install sigal error\n");
}

void xxx_handler(int signum,siginfo_t *info,void *myact)
{
	printf("Receive signal number:%d\n",signum);
	printf("si_signo:%d\n", info->si_signo);
	printf("si_code:%d\n", info->si_code);
	printf("si_value int:%d\n",info->si_value.sival_ptr);
	printf("si_value void*:%X\n",(unsigned long)info->si_value.sival_ptr);
	printf("si_errno:%d\n", info->si_errno);
	printf("si_pid:%d\n", info->si_pid);
	printf("si_uid:%d\n", info->si_uid);
	printf("si_addr:%X\n",(unsigned long)info->si_addr);
	printf("si_status:%d\n", info->si_status);
	printf("si_band:%d\n", info->si_band);
}
驱动向应用发出信号 kill_fasync(&dev->async_queue, SIGIO, POLL_IN)打印出以下结果

si_signo:29
si_code:128
si_value int:0
si_value void*:0
si_errno:0
si_pid:0
si_uid:0
si_addr:0
si_status:0
si_band:0
驱动向应用发出信号 kill_fasync(&dev->async_queue, SIGIO, POLL_OUT)打印出以下结果

si_signo:29
si_code:128
si_value int:0
si_value void*:0
si_errno:0
si_pid:0
si_uid:0
si_addr:0
si_status:0
si_band:0
POLL_IN和POLL_OUT的结果是一样的,驱动发信号时应用中的信号处理函数只接收到了si_signo、si_code两个数据,后面的数值都为0,还是没找到kill_fasync第三个参数的意义是在哪里体现的??
回复 点赞
jklinux 2017年11月30日
可以试下用sigaction函数代替signal函数来处理信号, sigaction里可用void (*sa_sigaction)(int, siginfo_t *, void *);处理函数,可获取的参数较多.查看下siginfo_t里有没有符合你的数据了
回复 点赞
羅昇 2017年11月30日
测试POLL_IN和POLL_OUT时接收到的si_code值都是128
回复 点赞
羅昇 2017年11月30日
引用 6 楼 jklinux 的回复:
sigaction函数你是怎样调用?
void xxx_handler(int signum,siginfo_t *info,void *myact);

struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction=xxx_handler;
act.sa_flags=SA_SIGINFO;
if(sigaction(SIGIO,&act,NULL) < 0)
{
        printf("install sigal error\n");
}
回复 点赞
发动态
发帖子
Linux/Unix社区
创建于2007-08-27

4738

社区成员

1.1w+

社区内容

Linux/Unix社区 专题技术讨论区
社区公告
暂无公告