前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux驱动实践:驱动程序如何发送【信号】给应用程序?

Linux驱动实践:驱动程序如何发送【信号】给应用程序?

作者头像
IOT物联网小镇
发布于 2021-12-09 06:20:22
发布于 2021-12-09 06:20:22
2.9K00
代码可运行
举报
文章被收录于专栏:IOT物联网小镇IOT物联网小镇
运行总次数:0
代码可运行

目录

  • kill 命令和信号
    • 使用 kill 命令发送信号
    • 多线程中的信号
    • 信号注册和处理函数
  • 驱动程序代码示例:发送信号
    • 功能需求
    • 驱动程序代码
    • 驱动模块 Makefile
    • 编译和加载
  • 应用程序代码示例:接收信号
    • 注册信号处理函数
    • 测试验证

别人的经验,我们的阶梯!

大家好,我是道哥,今天我为大伙儿解说的技术知识点是:【驱动层中,如何发送信号给应用程序】。

在上一篇文章中,我们讨论的是:在应用层如何发送指令来控制驱动层的 GPIOLinux驱动实践:如何编写【 GPIO 】设备的驱动程序?。控制的方向是从应用层到驱动层:

那么,如果想让程序的执行路径从下往上,也就是从驱动层传递到应用层,应该如何实现呢?

最容易、最简单的方式,就是通过发送信号!

这篇文章继续以完整的代码实例来演示如何实现这个功能。

kill 命令和信号

使用 kill 命令发送信号

关于 Linux 操作系统的信号,每位程序员都知道这个指令:使用 kill 工具来“杀死”一个进程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ kill -9 <进程的 PID>

这个指令的功能是:向指定的某个进程发送一个信号 9,这个信号的默认功能是:是停止进程。

虽然在应用程序中没有主动处理这个信号,但是操作系统默认的处理动作是终止应用程序的执行。

除了发送信号 9kill 命令还可以发送其他的任意信号。

Linux 系统中,所有的信号都使用一个整型数值来表示,可以打开文件 /usr/include/x86_64-linux-gnu/bits/signum.h(你的系统中可能位于其它的目录) 查看一下,比较常见的几个信号是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* Signals.  */
#define	SIGINT		2	/* Interrupt (ANSI).  */
#define	SIGKILL		9	/* Kill, unblockable (POSIX).  */
#define	SIGUSR1		10	/* User-defined signal 1 (POSIX).  */
#define	SIGSEGV		11	/* Segmentation violation (ANSI).  */
#define	SIGUSR2		12	/* User-defined signal 2 (POSIX).  */
...
...
#define SIGSYS		31	/* Bad system call.  */
#define SIGUNUSED	31

#define	_NSIG		65	/* Biggest signal number + 1
				   (including real-time signals).  */

/* These are the hard limits of the kernel.  These values should not be
   used directly at user level.  */
#define __SIGRTMIN	32
#define __SIGRTMAX	(_NSIG - 1)

信号 9 对应着 SIGKILL,而信号11SIGSEGV)就是最令人讨厌的Segmentfault!

这里还有一个地方需要注意一下:实时信号和非实时信号,它俩的主要区别是:

  1. 非实时信号:操作系统不确保应用程序一定能接收到(即:信号可能会丢失);
  2. 实时信号:操作系统确保应用程序一定能接收到;

如果我们的程序设计,通过信号机制来完成一些功能,那么为了确保信号不会丢失,肯定是使用实时信号的。

从文件 signum.h 中可以看到,实时信号从 __SIGRTMIN(数值:32) 开始。

多线程中的信号

我们在编写应用程序时,虽然没有接收并处理 SIGKILL 这个信号,但是一旦别人发送了这个信号,我们的程序就被操作系统停止掉了,这是默认的动作。

那么,在应用程序中,应该可以主动声明接收并处理指定的信号,下面就来写一个最简单的实例。

在一个应用程序中,可能存在多个线程; 当有一个信号发送给此进程时,所有的线程都可能接收到,但是只能有一个线程来处理; 在这个示例中,只有一个主线程来接收并处理信号;

信号注册和处理函数

按照惯例,所有应用程序文件都创建在 ~/tmp/App 目录中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 文件:tmp/App/app_handle_signal/app_handle_signal.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

// 信号处理函数
static void signal_handler(int signum, siginfo_t *info, void *context)
{
	// 打印接收到的信号值
    printf("signal_handler: signum = %d \n", signum);
}

int main(void)
{
	int count = 0;
	// 注册信号处理函数
	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = &signal_handler;
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGUSR1, &sa, NULL);
	sigaction(SIGUSR2, &sa, NULL);

	// 一直循环打印信息,等待接收发信号
	while (1)
	{
		printf("app_handle_signal is running...count = %d \n", ++count);
		sleep(5);
	}

	return 0;
}

这个示例程序接收的信号是 SIGUSR1SIGUSR2,也就是数值 1012

编译、执行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc app_handle_signal.c -o app_handle_signal
$ ./app_handle_signal

此时,应用程序开始执行,等待接收信号。

在另一个终端中,使用kill指令来发送信号SIGUSR1或者 SIGUSR2

kill 发送信号,需要知道应用程序的 PID,可以通过指令: ps -au | grep app_handle_signal 来查看。

其中的14788就是进程的 PID

执行发送信号SIGUSR1指令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ kill -10 14788

此时,在应用程序的终端窗口中,就能看到下面的打印信息:

说明应用程序接收到了 SIGUSR1 这个信号!

注意:我们是使用kill命令来发送信号的,kill 也是一个独立的进程,程序的执行路径如下:

在这个执行路径中,我们可控的部分是应用层,至于操作系统是如何接收kill的操作,然后如何发送信号给 app_handle_signal 进程的,我们不得而知。

下面就继续通过示例代码来看一下如何在驱动层主动发送信号。

驱动程序代码示例:发送信号

功能需求

在刚才的简单示例中,可以得出下面这些信息:

  1. 信号发送方:必须知道向谁[PID]发送信号,发送哪个信号;
  2. 信号接收方:必须定义信号处理函数,并且向操作系统注册:接收哪些信号;

发送方当然就是驱动程序了,在示例代码中,继续使用 SIGUSR1 信号来测试。

那么,驱动程序如何才能知道应用程序的PID呢?可以让应用程序通过oictl函数,把自己的PID主动告诉驱动程序:

驱动程序

这里的示例代码,是在上一篇文章的基础上修改的,改动部分的内容,使用宏定义 MY_SIGNAL_ENABLE 控制起来,方便查看和比较。

以下所有操作的工作目录,都是与上一篇文章相同的,即:~/tmp/linux-4.15/drivers/

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cd ~/tmp/linux-4.15/drivers/
$ mkdir my_driver_signal
$ cd my_driver_signal
$ touch my_driver_signal.c

my_driver_signal.c 文件的内容如下(不需要手敲,文末有代码下载链接):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/cdev.h>

// 新增的头文件
#include <asm/siginfo.h>
#include <linux/pid.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <linux/pid_namespace.h>

// GPIO 硬件相关宏定义
#define MYGPIO_HW_ENABLE

// 新增部分,使用这个宏控制起来
#define MY_SIGNAL_ENABLE

// 设备名称
#define MYGPIO_NAME			"mygpio"

// 一共有4个GPIO
#define MYGPIO_NUMBER		4

// 设备类
static struct class *gpio_class;

// 用来保存设备
struct cdev gpio_cdev[MYGPIO_NUMBER];

// 用来保存设备号
int gpio_major = 0;
int gpio_minor = 0;

#ifdef MY_SIGNAL_ENABLE
// 用来保存向谁发送信号,应用程序通过 ioctl 把自己的进程 ID 设置进来。
static int g_pid = 0;
#endif

#ifdef MYGPIO_HW_ENABLE
// 硬件初始化函数,在驱动程序被加载的时候(gpio_driver_init)被调用
static void gpio_hw_init(int gpio)
{
	printk("gpio_hw_init is called: %d. \n", gpio);
}

// 硬件释放
static void gpio_hw_release(int gpio)
{
	printk("gpio_hw_release is called: %d. \n", gpio);
}

// 设置硬件GPIO的状态,在控制GPIO的时候(gpio_ioctl)被调研
static void gpio_hw_set(unsigned long gpio_no, unsigned int val)
{
	printk("gpio_hw_set is called. gpio_no = %ld, val = %d. \n", gpio_no, val);
}
#endif

#ifdef MY_SIGNAL_ENABLE
// 用来发送信号给应用程序
static void send_signal(int sig_no)
{
	int ret;
	struct siginfo info;
	struct task_struct *my_task = NULL;
	if (0 == g_pid)
	{
		// 说明应用程序没有设置自己的 PID
	    printk("pid[%d] is not valid! \n", g_pid);
	    return;
	}

	printk("send signal %d to pid %d \n", sig_no, g_pid);

	// 构造信号结构体
	memset(&info, 0, sizeof(struct siginfo));
	info.si_signo = sig_no;
	info.si_errno = 100;
	info.si_code = 200;

	// 获取自己的任务信息,使用的是 RCU 锁
	rcu_read_lock();
	my_task = pid_task(find_vpid(g_pid), PIDTYPE_PID);
	rcu_read_unlock();

	if (my_task == NULL)
	{
	    printk("get pid_task failed! \n");
	    return;
	}

	// 发送信号
	ret = send_sig_info(sig_no, &info, my_task);
	if (ret < 0) 
	{
	       printk("send signal failed! \n");
	}
}
#endif

// 当应用程序打开设备的时候被调用
static int gpio_open(struct inode *inode, struct file *file)
{
	
	printk("gpio_open is called. \n");
	return 0;	
}

#ifdef MY_SIGNAL_ENABLE
static long gpio_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
	void __user *pArg;
	printk("gpio_ioctl is called. cmd = %d \n", cmd);
	if (100 == cmd)
	{
		// 说明应用程序设置进程的 PID 
		pArg = (void *)arg;
		if (!access_ok(VERIFY_READ, pArg, sizeof(int)))
		{
		    printk("access failed! \n");
		    return -EACCES;
		}

		// 把用户空间的数据复制到内核空间
		if (copy_from_user(&g_pid, pArg, sizeof(int)))
		{
		    printk("copy_from_user failed! \n");
		    return -EFAULT;
		}

		printk("save g_pid success: %d \n", g_pid); 
		if (g_pid > 0)
		{
			// 发送信号
			send_signal(SIGUSR1);
			send_signal(SIGUSR2);
		}
	}

	return 0;
}
#else
// 当应用程序控制GPIO的时候被调用
static long gpio_ioctl(struct file* file, unsigned int val, unsigned long gpio_no)
{
	printk("gpio_ioctl is called. \n");
	
	if (0 != val && 1 != val)
	{
		printk("val is NOT valid! \n");
		return 0;
	}

	if (gpio_no >= MYGPIO_NUMBER)
	{
		printk("dev_no is invalid! \n");
		return 0;
	}

	printk("set GPIO: %ld to %d. \n", gpio_no, val);

#ifdef MYGPIO_HW_ENABLE
	gpio_hw_set(gpio_no, val);
#endif

	return 0;
}
#endif

static const struct file_operations gpio_ops={
	.owner = THIS_MODULE,
	.open  = gpio_open,
	.unlocked_ioctl = gpio_ioctl
};

static int __init gpio_driver_init(void)
{
	int i, devno;
	dev_t num_dev;

	printk("gpio_driver_init is called. \n");

	// 动态申请设备号(严谨点的话,应该检查函数返回值)
	alloc_chrdev_region(&num_dev, gpio_minor, MYGPIO_NUMBER, MYGPIO_NAME);

	// 获取主设备号
	gpio_major = MAJOR(num_dev);
	printk("gpio_major = %d. \n", gpio_major);

	// 创建设备类
	gpio_class = class_create(THIS_MODULE, MYGPIO_NAME);

	// 创建设备节点
	for (i = 0; i < MYGPIO_NUMBER; ++i)
	{
		// 设备号
		devno = MKDEV(gpio_major, gpio_minor + i);
		
		// 初始化cdev结构
		cdev_init(&gpio_cdev[i], &gpio_ops);

		// 注册字符设备
		cdev_add(&gpio_cdev[i], devno, 1);

		// 创建设备节点
		device_create(gpio_class, NULL, devno, NULL, MYGPIO_NAME"%d", i);
	}

#ifdef MYGPIO_HW_ENABLE
	for (i = 0; i < MYGPIO_NUMBER; ++i)
	{
		// 初始硬件GPIO
		gpio_hw_init(i);
	}
#endif

	return 0;
}

static void __exit gpio_driver_exit(void)
{
	int i;
	printk("gpio_driver_exit is called. \n");

	// 删除设备节点
	for (i = 0; i < MYGPIO_NUMBER; ++i)
	{
		cdev_del(&gpio_cdev[i]);
		device_destroy(gpio_class, MKDEV(gpio_major, gpio_minor + i));
	}

	// 释放设备类
	class_destroy(gpio_class);

#ifdef MYGPIO_HW_ENABLE
	for (i = 0; i < MYGPIO_NUMBER; ++i)
	{
		gpio_hw_release(i);
	}
#endif

	// 注销设备号
	unregister_chrdev_region(MKDEV(gpio_major, gpio_minor), MYGPIO_NUMBER);
}

MODULE_LICENSE("GPL");
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);

这里大部分的代码,在上一篇文章中已经描述的比较清楚了,这里把重点关注放在这两个函数上:gpio_ioctlsend_signal

(1)函数 gpio_ioctl

当应用程序调用 ioctl() 的时候,驱动程序中的 gpio_ioctl 就会被调用。

这里定义一个简单的协议:当应用程序调用参数中 cmd 为 100 的时候,就表示用来告诉驱动程序自己的 PID。

驱动程序定义了一个全局变量 g_pid,用来保存应用程序传入的参数PID

需要调用函数 copy_from_user(&g_pid, pArg, sizeof(int)),把用户空间的参数复制到内核空间中;

成功取得PID之后,就调用函数 send_signal 向应用程序发送信号。

这里仅仅是用于演示目的,在实际的项目中,可能会根据接收到硬件触发之后再发送信号。

(2)函数 send_signal

这个函数主要做了3件事情:

  1. 构造一个信号结构体变量:struct siginfo info;
  2. 通过应用程序传入的 PID,获取任务信息:pid_task(find_vpid(g_pid), PIDTYPE_PID);
  3. 发送信号:send_sig_info(sig_no, &info, my_task);
驱动模块 Makefile
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ touch Makefile

内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ifneq ($(KERNELRELEASE),)
	obj-m := my_driver_signal.o
else
	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	$(MAKE) -C $(KERNEL_PATH) M=$(PWD) clean
endif
编译驱动模块
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ make

得到驱动程序: my_driver_signal.ko 。

加载驱动模块
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo insmod my_driver_signal.ko

通过 dmesg 指令来查看驱动模块的打印信息:

因为示例代码是在上一篇GPIO的基础上修改的,因此创建的设备节点文件,与上篇文章是一样的:

应用程序代码示例:接收信号

注册信号处理函数

应用程序仍然放在 ~/tmp/App/ 目录下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ mkdir ~/tmp/App/app_mysignal
$ cd ~/tmp/App/app_mysignal
$ touch mysignal.c

文件内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>

#define MY_GPIO_NUMBER		4

char gpio_name[MY_GPIO_NUMBER][16] = {
	"/dev/mygpio0",
	"/dev/mygpio1",
	"/dev/mygpio2",
	"/dev/mygpio3"
};

// 信号处理函数
static void signal_handler(int signum, siginfo_t *info, void *context)
{
	// 打印接收到的信号值
    printf("signal_handler: signum = %d \n", signum);
    printf("signo = %d, code = %d, errno = %d \n",
	         info->si_signo,
	         info->si_code, 
	         info->si_errno);
}

int main(int argc, char *argv[])
{
	int fd, count = 0;
	int pid = getpid();

	// 打开GPIO
	if((fd = open("/dev/mygpio0", O_RDWR | O_NDELAY)) < 0){
		printf("open dev failed! \n");
		return -1;
	}

	printf("open dev success! \n");
	
	// 注册信号处理函数
	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = &signal_handler;
	sa.sa_flags = SA_SIGINFO;
	
	sigaction(SIGUSR1, &sa, NULL);
	sigaction(SIGUSR2, &sa, NULL);

	// set PID 
	printf("call ioctl. pid = %d \n", pid);
	ioctl(fd, 100, &pid);

	// 休眠1秒,等待接收信号
	sleep(1);

	// 关闭设备
	close(fd);
}

可以看到,应用程序主要做了两件事情:

(1)首先通过函数 sigaction() 向操作系统注册了信号 SIGUSR1 和 SIGUSR2,它俩的信号处理函数是同一个:signal_handler()

除了 sigaction 函数,应用程序还可以使用 signal 函数来注册信号处理函数;

(2)然后通过 ioctl(fd, 100, &pid); 向驱动程序设置自己的 PID。

编译应用程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc mysignal.c -o mysignal

执行应用程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ./mysignal

根据刚才驱动程序的代码,当驱动程序接收到设置PID的命令之后,会立刻发送两个信号:

先来看一下 dmesg 中驱动程序的打印信息:

可以看到:驱动把这两个信号(10 和 12),发送给了应用程序(PID=6259)。

应用程序的输出信息如下:

可以看到:应用程序接收到信号 10 和 12,并且正确打印出信号中携带的一些信息!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IOT物联网小镇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Linux date日期格式及加减运算
显示时间是个常用的命令,在写shell脚本中也经常会用到与日期相关文件名或时间显示。无论是linux还是windows下都是date命令。
阳光岛主
2019/02/19
8.4K0
Linux date日期格式及加减运算
linux学习(九) date命令详解
显示系统日期 要显示系统日期,只要输入: [root@DEVAPP01-1-10 bin]# date 2015年 08月 28日 星期五 17:19:22 CST 格式化显示日期 日期有很
Java架构师必看
2021/05/31
5.1K0
Javascript中的Date.now() 方法与Date.UTC() 方法 ,Date.parse() 方法
Date.parse() 方法解析一个表示某个日期的字符串,并返回从1970-1-1 00:00:00 UTC 到该日期对象(该日期对象的UTC时间)的毫秒数,如果该字符串无法识别,或者一些情况下,包含了不合法的日期数值(如:2015-02-31),则返回值为NaN。
acoolgiser
2019/01/17
2K0
Moment.js的常用方法
安装与引用 官方文档 安装 npm install moment 引用 var moment = require('moment'); 自定义配置 获取当前语言 moment.locale(); 加载语言 moment.locale('zh-cn'); UTC 获取UTC moment().utc(); UTC偏移量 moment().utcOffset(); 设置偏移量 以下是相同的 moment().utcOffset("+08:00"); moment().utcOffset(8); mome
码客说
2019/10/22
4.4K0
每天学一个 Linux 命令(50):date
昨日推荐:每天学一个 Linux 命令(49):free 命令简介 date 命令用于显示与设置系统时间。 语法格式 date [OPTION] [MMDDhhmm[[CC]YY][.ss]] 选项说明 -d<字符串> #显示字符串所指的日期与时间。字符串前后必须加上双引号 -s<字符串> #根据字符串来设置日期与时间。字符串前后必须加上双引号 -u #显示GMT --help #打印在线帮助信息 --version #打印版本信息 时间字符串列表 FORMAT controls th
民工哥
2021/03/15
1.9K0
Python时间模块那些事
python有三大时间模块,分别是time,datetime,calendar,今天来学习下
披头
2020/04/26
1.3K0
Shell date 命令详解
以给定的格式显示当前时间。 %% 一个文字的 % %a 当前locale 的星期名缩写(例如: 日,代表星期日) %A 当前locale 的星期名全称 (如:星期日) %b 当前locale 的月名缩写 (如:一,代表一月) %B 当前locale 的月名全称 (如:一月) %c 当前locale 的日期和时间 (如:2005年3月3日 星期四 23:05:25) %C 世纪;比如 %Y,通常为省略当前年份的后两位数字(例如:20) %d 按月计的日期(例如:
康怀帅
2018/02/28
1.5K0
又肝了3天,整理了80个Python DateTime 例子,必须收藏!
日常工作中,用 Python 处理时间格式的数据是非常常见的,今天就来分享 DateTime 相关的示例
周萝卜
2021/10/25
9K0
JavaScript日期处理
​ 当然,Github上提供了好多优秀的日期处理插件(如:Datejs、date-fns、jquery-dateFormat),然而当处理一些简单的日期操作去引用插件,还是挺耗费资源。
奋飛
2019/08/15
4.5K4
Js中Date对象
JavaScript的Date对象是用于处理日期和时间的全局对象,Date对象基于Unix Time Stamp,即自1970年1月1日UTC起经过的毫秒数。
WindRunnerMax
2020/10/26
21.7K0
【linux命令讲解大全】153.利用date命令管理系统时间和日期
date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
全栈若城
2024/03/02
4290
linux date -d 的一些使用方法[通俗易懂]
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/116005.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/08
1.2K0
Linux时间戳转换_时间戳转换软件
在大多数 UNIX 系统中,当前时间存储为自特定时刻以来经过的时间以简化,将时间保持为长整数。所有 UNIX 系统普遍接受的时刻是 1970 年 1 月 1 日凌晨 12:00:00。 这称为 UNIX 时间戳,并被所有现代 UNIX/Linux 系统识别。
全栈程序员站长
2022/11/09
16.4K0
Linux时间戳转换_时间戳转换软件
Quartz之CronTrigger
上篇博文简单的介绍了Quartz的基本使用,现在介绍一下最常用的触发器:CronTrigger。
栋先生
2018/09/29
1.8K0
Quartz之CronTrigger
在终端里按你的方式显示日期和时间
在 Linux 系统上,date 命令非常简单。你键入 date,日期和时间将以一种有用的方式显示。它包括星期几、日期、时间和时区:
用户8639654
2021/09/22
3.9K0
Linux命令(50)——date命令
date命令用于按照指定格式显示当前时间或者指定的时间,也可以设置系统时间。很多Shell脚本里面需要打印不同格式的时间或日期,以及要根据时间和日期执行操作,此时可以使用date命令来完成。在类Unix系统中,日期被存储为一个整数,其大小为协调世界时(UTC)1970年1月1日0时0分0秒起流逝的秒数,即Unix时间戳。
恋喵大鲤鱼
2018/12/24
3.7K0
JS基础-JS内置对象Date详解
year:表示年份的整数值。0到99会被映射至1900年至1999年,其它值代表实际年份。
用户10106350
2022/10/28
4K0
Django 过滤器
django1.4 or later html 页面从数据库中读出DateTimeField字段时,显示的时间格式和数据库中存放的格式不一致,比如数据库字段内容为2012-08-26 16:00:00,但是页面显示的却是Aug. 26, 2012, 4 p.m. 为了页面和数据库中显示一致,需要在页面格式化时间,需要添加<td>{{ dayrecord.p_time|date:"Y-m-d H:i:s" }}</td> 类似的过滤器。刷新页面,即可正常显示。
阳光岛主
2019/02/19
2.9K0
R语言 日期、时间和lubridate包
当导入数据时日期值通常以字符串的形式输入到R中,这时需要转化为以数值形式存储的日期变量。用as.Date()把文本转换为Date类型:其语法为as.Date(x,"input_format"),其中x是字符型数据,#input_format则给出了用于读入日期的适当格式
拴小林
2020/11/11
6K0
python常用内建模块之time、date、datetime
Python的time和datetime模块提供了时间日期工具, python中的时间有4种表示方式:
菲宇
2019/06/13
2.4K0
相关推荐
Linux date日期格式及加减运算
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • kill 命令和信号
    • 使用 kill 命令发送信号
    • 多线程中的信号
    • 信号注册和处理函数
  • 驱动程序代码示例:发送信号
    • 功能需求
    • 驱动程序
    • 驱动模块 Makefile
    • 编译驱动模块
    • 加载驱动模块
  • 应用程序代码示例:接收信号
    • 注册信号处理函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档