Linux异步通知---signal()、fcntl()函数介绍与使用

一、 signal()函数介绍

   signal函数是设置一个函数来处理信号,即带有 signum 参数的信号处理程序
   
   linux下man signal如下:
   
   #include <signal.h>

   typedef void (*sighandler_t)(int);

   sighandler_t signal(int signum, sighandler_t handler);

其中第一个参数signum – 在信号处理程序中作为变量使用的信号码。下面是一些重要的标准信号常量:

信号
SIGABRT(Signal Abort) 程序异常终止。
SIGFPE(Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)
SIGILL(Signal Illegal Instruction) 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。
SIGINT(Signal Interrupt) 中断信号,如 ctrl-C,通常由用户生成。
SIGSEGV(Signal Segmentation Violation) 非法访问存储器,如访问不存在的内存单元。
SIGTERM(Signal Terminate) 发送给本程序的终止请求信号。

第二个参数handler --一个指向函数的指针。它可以是一个由程序定义的函数,也可以是下面预定义函数之一

SIG_DFL默认的信号处理程序。
SIG_IGN忽视信号。

返回值
该函数返回信号处理程序之前的值,当发生错误时返回 SIG_ERR。
/* by 面朝大海0902 */

二、应用层实例代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>

static int fd;

static void sig_func(int sig)
{
	int value;
	read(fd, &value, 4);
	printf("get button : 0x%x\n", value);
}

/*
 * ./key-fasync-test /dev/my_key0
 * by 面朝大海0902
 */
int main(int argc, char **argv)
{
	int flags;
	int ret;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}
	
	signal(SIGIO, sig_func);//注册信号回调函数

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	fcntl(fd, F_SETOWN, getpid());//告诉驱动往哪个pid上发送SIGIO信号
	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | FASYNC);//触发调用驱动的fasync函数
	

	while (1)
	{
		printf("by mianchaodahai 0902\n");
		sleep(5);
	}
	
	close(fd);
	
	return 0;
}

/* by 面朝大海0902 */

三、fcntl()函数

   fcntl()函数是用来修改已经打开文件的属性的,linux下man fcntl如下:
   
   fcntl - manipulate file descriptor

   #include <unistd.h>
   #include <fcntl.h>

   int fcntl(int fd, int cmd, ... /* arg */ );

上面应用程序使用fcntl(fd, F_SETOWN, getpid())—实际设置的是用来接收SIGIO信号的进程id或者进程组id,告诉驱动往哪个pid上发送SIGIO信号。

应用层调用fcntl()对应内核会调用sys_fcntl()函数进而调用do_fcntl(),源码位于fs/fcntl.c里面(内核版本为4.9.88)

SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{	
	struct fd f = fdget_raw(fd);
	long err = -EBADF;

	if (!f.file)
		goto out;

	if (unlikely(f.file->f_mode & FMODE_PATH)) {
		if (!check_fcntl_cmd(cmd))
			goto out1;
	}

	err = security_file_fcntl(f.file, cmd, arg);
	if (!err)
		err = do_fcntl(fd, cmd, arg, f.file);

out1:
 	fdput(f);
out:
	return err;
}
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
		struct file *filp)
{
	long err = -EINVAL;

	switch (cmd) {
	... ...
	case F_SETFL:
		err = setfl(fd, filp, arg);
		break;

	case F_SETOWN:
		f_setown(filp, arg, 1);
		err = 0;
		break;
	... ...
	}
	return err;
}

可以看到每个对应的cmd都会有各自的case去处理,F_SETOWN的处理函数最终调用到**f_modown()**函数设置文件属性的pid,type,uid,euid。

static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
                     int force)
{
	write_lock_irq(&filp->f_owner.lock);
	if (force || !filp->f_owner.pid) {
		put_pid(filp->f_owner.pid);
		filp->f_owner.pid = get_pid(pid);
		filp->f_owner.pid_type = type;

		if (pid) {
			const struct cred *cred = current_cred();
			filp->f_owner.uid = cred->uid;
			filp->f_owner.euid = cred->euid;
		}
	}
	write_unlock_irq(&filp->f_owner.lock);
}

F_SETFL的处理函数最终调用到**setfl()**函数,因为文件有应用层置的FASYNC标志位而调用到对应驱动文件的fasync()函数。

static int setfl(int fd, struct file * filp, unsigned long arg)
{
... ...

	/*
	 * ->fasync() is responsible for setting the FASYNC bit.
	 */
	if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
		error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
		if (error < 0)
			goto out;
		if (error > 0)
			error = 0;
	}
... ...
}

/* by 面朝大海0902 */
关于内核驱动如何向应用层发送信号,可以参考下一节Linux异步通知—fasync_helper()、kill_fasync()函数介绍与使用

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值