奔Linux内核入门篇_chapter8.5.3_后台守护进程
实验详述
实验目的
通过本实验了解和熟悉Linux是如何创建和使用后台守护进程的.实验步骤
(1) 写一个用户程序, 创建一个守护进程.
(2) 该守护进程每隔5秒去查看当前内核的日志中是否有Oops错误.
实验解析
本实验完全是个用户态的程序, 需要知道
如何创建一个守护进程? (跑在后台, 无控制终端, 无法对前台输出信息)
如何去读内核的日志?
问题1可以用glibc提供的daemon()函数
man 3 daemon
NAME daemon - run in the backgroundSYNOPSIS #include <unistd.h> int daemon(int nochdir, int noclose); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): daemon(): Since glibc 2.21: _DEFAULT_SOURCE In glibc 2.19 and 2.20: _DEFAULT_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500) Up to and including glibc 2.19: _BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500)
问题2 可以用glibc提供的klogctl()函数
man 3 klogctl
NAME syslog, klogctl - read and/or clear kernel message ring buffer; set console_loglevelSYNOPSIS int syslog(int type, char *bufp, int len); /* No wrapper provided in glibc */ /* The glibc interface */ #include <sys/klog.h> int klogctl(int type, char *bufp, int len);
本实验相对简单,直接看代码.
程序源码
文件名: my_daemon.c
这个直接参考了笨叔的答案
(runninglinuxkernel_4.0/rlk_lab/rlk_basic/chapter_8/lab3_daemon/daemon_test1.c)
/* create a daemon which checks if there are opps errors in the kernel log * every 5 second */#include <stdio.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <time.h>#include <fcntl.h>#include <string.h>#include <sys/stat.h>#include <sys/klog.h>#define FALLBACK_KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT in kernel */#define FALLBACK_KLOG_BUF_LEN (1 << FALLBACK_KLOG_BUF_SHIFT)#define KLOG_CLOSE 0#define KLOG_OPEN 1#define KLOG_READ 2#define KLOG_READ_ALL 3#define KLOG_READ_CLEAR 4#define KLOG_CLEAR 5#define KLOG_CONSOLE_OFF 6#define KLOG_CONSOLE_ON 7#define KLOG_CONSOLE_LEVEL 8#define KLOG_SIZE_UNREAD 9#define KLOG_SIZE_BUFFER 10/* we use 'Linux version' string instead of Oops in this lab *///#define OOPS_LOG "Oops"#define OOPS_LOG "Linux version"int save_kernel_log(char *buffer){char path[128]; time_t t; struct tm *tm; int fd; /* time(2): returns the time as the number of seconds * since the Epoch, 1970-01-01 00:00:00 0000 (UTC). */ t = time(0); /* transform date and time to broken-down time or ASCII */ tm = localtime(&t); /* the structure tm, which is defined in <time.h> as follows: * struct tm { * int tm_sec; // Seconds (0-60) * int tm_min; // Minutes (0-59) * int tm_hour; // Hours (0-23) * int tm_mday; // Day of the month (1-31) * int tm_mon; // Month (0-11) * int tm_year; // Year - 1900 * int tm_wday; // Day of the week (0-6, Sunday = 0) * int tm_yday; // Day in the year (0-365, 1 Jan = 0) * int tm_isdst; // Daylight saving time * }; */ snprintf(path, 128, "/mnt/%d.%d.%d.%d.%d.%d.log", tm->tm_year 1900, tm->tm_mon 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); printf("%s\n", path); fd = open(path, O_WRONLY|O_CREAT, 0644); if(fd == -1) {printf("open error\n"); return -1; } write(fd, buffer, strlen(buffer)); close(fd); return 0;}int check_kernel_log(){char *buffer; char *p; ssize_t klog_size; int ret = -1; int size; printf("start kernel log\n"); /* klogctl(3): read and/or clear kernel message * ring buffer; set console_loglevel. * Here returns the total size of the kernel log buffer. * */ klog_size = klogctl(KLOG_SIZE_BUFFER, 0, 0); if (klog_size <= 0) {klog_size = FALLBACK_KLOG_BUF_LEN; } printf("kernel log size: %d\n", klog_size); buffer = malloc(klog_size 1); if (!buffer) return -1; /* Read all messages remaining in the ring buffer, * placing them in the buffer pointed to by buffer. * The call reads the last klog_size bytes from * the log buffer (nondestructively) */ size = klogctl(KLOG_READ_ALL, buffer, klog_size); if (size < 0) {printf("klogctl read error\n"); goto done; } buffer[size] = '\0'; /* strstr(3), finds the first occurrence of the substring */ /* check if oops in klog */ p = strstr(buffer,OOPS_LOG); if (p) {printf("we found '%s' on kernel log\n", OOPS_LOG); save_kernel_log(buffer); ret = 0; }done: free(buffer); return ret;}int main(void){/* The daemon(3) function is for programs wishing to * detach themselves from the controlling terminal * and run in the background as system daemons. */ if (daemon(0, 0) == -1) {printf("failed to daemon(): %s(%d)\n", strerror(errno), errno); return -1; } while (1) {check_kernel_log(); sleep(5); } return 0;}
编译
这里直接编译就行
arm-linux-gnueabi-gcc my_daemon.c -o my_daemon.exe --static
验证执行
/mnt #/mnt # pwd/mnt/mnt # find . -name "*.log"/mnt # ./my_daemon.exe/mnt #/mnt # find . -name "*.log"./2021.1.24.1.45.50.log./2021.1.24.1.45.55.log./2021.1.24.1.46.0.log/mnt #/mnt # pgrep my_daemon792/mnt # kill -9 792/mnt #/mnt #/mnt # head 2021.1.24.1.45.50.log<6>[ 0.000000] Booting Linux on physical CPU 0x0<6>[ 0.000000] Initializing cgroup subsys cpuset<5>[ 0.000000] Linux version 4.0.0 (gewkiff@ukylin) (gcc version 5.5.0 20171010 (Ubuntu/Linaro 5.5.0-12ubuntu1) ) #2 SMP Sun Sep 20 06:37:58 CST 2020<6>[ 0.000000] CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d<6>[ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache<6>[ 0.000000] Machine model: V2P-CA9<6>[ 0.000000] Memory policy: Data cache writealloc<7>[ 0.000000] On node 0 totalpages: 25600<7>[ 0.000000] free_area_init_node: node 0, pgdat c10e9e40, node_mem_map c6332000<7>[ 0.000000] Normal zone: 200 pages used for memmap/mnt #/mnt #/mnt # head 2021.1.24.1.45.55.log<6>[ 0.000000] Booting Linux on physical CPU 0x0<6>[ 0.000000] Initializing cgroup subsys cpuset<5>[ 0.000000] Linux version 4.0.0 (gewkiff@ukylin) (gcc version 5.5.0 20171010 (Ubuntu/Linaro 5.5.0-12ubuntu1) ) #2 SMP Sun Sep 20 06:37:58 CST 2020<6>[ 0.000000] CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d<6>[ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache<6>[ 0.000000] Machine model: V2P-CA9<6>[ 0.000000] Memory policy: Data cache writealloc<7>[ 0.000000] On node 0 totalpages: 25600<7>[ 0.000000] free_area_init_node: node 0, pgdat c10e9e40, node_mem_map c6332000<7>[ 0.000000] Normal zone: 200 pages used for memmap/mnt #
kernel log buffer
这是在man 3 klogctl 中看到的关于kernel log buffer的简短介绍
The kernel log buffer
The kernel has a cyclic buffer of length LOG_BUF_LEN in which
messages given as arguments to the kernel function printk()
are stored (regardless of their log level).
In early kernels, LOG_BUF_LEN had the value 4096;
from kernel 1.3.54, it was 8192; from kernel 2.1.113, it was 16384;
since kernel 2.4.23/2.6, the value is a kernel configuration option
(CONFIG_LOG_BUF_SHIFT, default value dependent on the architecture).
Since Linux 2.6.6, the size can be queried with command type 10 (see below).