更新:见底部
我在 debian Linux 上使用 systemd 版本 241 创建了一个 systemd 服务,其目的是启动accelMonitor.c
连接到内核模块的编译版本。该内核模块为脚本提供一个事件,以响应来自加速度计的 GPIO 中断。收到事件后,accelMonitor
将设备的方向打印到控制台。将脚本转换为服务的 systemd 单元文件accelMonitor
如下所示:
[Unit]
Description=Accelerometer Orientation Monitor
[Service]
Type=simple
ExecStartPre=/etc/systemd/system/deviceFileCreator
ExecStart=/etc/systemd/system/accelMonitor
RemainAfterExit=true
KillMode=process
StandardOutput=append:/var/log/accelMonitor.log
StandardError=append:/var/log/accelMonitor.log
[Install]
WantedBy=multi-user.target
该服务首先执行编译后的deviceFileCreator.c
脚本,为内核模块生成设备文件并accelMonitor
进行通信。然后,服务执行accelMonitor
脚本本身,该脚本通过所述设备文件向内核模块注册。目前,输出正在写入文件accelMonitor.log
。deviceFileCreator
和脚本都accelMonitor
使用printf
语句 和 都是用 C 编写的。
我的问题是针对这个问题的回应:当deviceFileCreator
脚本运行时,printf 语句会accelMonitor.log
正确记录到文件中。但是,当accelMonitor
脚本运行时,尽管两个脚本都使用 c 语言并使用 printf,但 .log 文件中没有正确记录任何打印语句。我希望熟悉 systemd 服务的人可以通过查看下面的代码轻松找出发生这种情况的原因。需要明确的是,accelMonitor
当不作为服务运行时,会正确打印到控制台。
到目前为止我已经尝试过:
在服务单元文件本身中,我尝试了以下选项StandardOutput
:syslog
, syslog+console
, console
, append:/var/log/accelMonitor.log
。这些选项似乎都不会打印到accelMonitor
控制台、日志或 .log 文件。我确信该accelMonitor
脚本正在运行,因为它在发生后重置了加速度计中断。
accelMonitor
我还将创建设备文件的部分拆分到deviceFileCreator
之前运行的脚本中,以防出现问题。它似乎没有改变任何东西,但是一旦启用此更改,就会导致accelMonitor.service
现在打印出它正在启动 ( [ OK ] Started Accelerometer Orientation Monitor
) 上运行,而之前它没有运行(但它最终仍然运行)。
Type=oneshot
除了 之外我也尝试过Type=simple
。
如果有人能让我深入了解为什么我没有从中获取日志信息,accelMonitor
而是从中获取日志deviceFileCreator
信息,我将不胜感激。谢谢!
该deviceFileCreator.c
文件如下所示:
// deviceFileCreator.c
#include <stdio.h>
#include <limits.h>
#include <signal.h>
#include <fcntl.h>
// Helper function to run bash commands in c
// This allows me to create the device file using terminal commands
char* exec(char* cmd) {
FILE* fp;
int status;
char path[PATH_MAX];
char* regValue;
fp = popen(cmd, "r");
if (fp == NULL)
/* Report error */
perror("popen error");
while (fgets(path, PATH_MAX, fp) != NULL)
//printf("%s", path);
regValue = path;
status = pclose(fp);
if (status == -1) {
/* Error reported by pclose() */
printf("Error reported by pclose()");
}
return regValue;
}
int main() {
int fd;
printf("Creating Device File irq_signal \n");
// Command to create device file
exec("sudo mknod /dev/irq_signal c 64 0");
/* Open the device file */
fd = open("/dev/irq_signal", O_RDONLY);
if (fd < 0) {
perror("Could not open device file\n");
return -1;
}
printf("Device file /dev/irq_signal created \n");
return 0;
}
当此脚本在启动时运行时,accelMonitor.log 中的输出为:
Creating Device File irq_signal
Device file /dev/irq_signal created
该accelMonitor.c
文件如下所示:
/*
Event handler to monitor signals from
associated kernel module. Triggers whenever
an interrupt is detected by the kernel module
(gpio_irq) from the MMA8451Q accelerometer and
is passed as a signal (SIGTX 44) to this script.
This occurs everytime the device orientation changes
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <limits.h>
#include <error.h>
#include <stdbool.h>
/* Signals to connect to Kernel Module */
#define SIGTX 44
#define REGISTER_UAPP _IO('R', 'g')
/* Define registers to be accessed on Accelerometer over i2c */
const char* deviceADDR = " 0x1C";
const char* PL_STATUS = " 0x10";
const char* INT_SOURCE = " 0x0C";
const char* PL_CFG = " 0x11";
const char* PL_COUNT = " 0x12";
const char* CTRL_REG1 = " 0x2A";
const char* CTRL_REG3 = " 0x2C";
const char* CTRL_REG4 = " 0x2D";
const char* CTRL_REG5 = " 0x2E";
/* Defines function structure for i2c-tools package */
const char* cmdGet = "i2cget -y 2";
const char* cmdSet = "i2cset -y 2";
/* Enum for current device orientation state */
enum position { left, right, down, up };
enum position oriented;
/* Function that runs bash commands in c
Allowing one to use the i2c-tools package */
char* exec(char* cmd) {
FILE* fp;
int status;
char path[PATH_MAX];
char* regValue;
fp = popen(cmd, "r");
if (fp == NULL)
/* Report error */
perror("popen error");
while (fgets(path, PATH_MAX, fp) != NULL)
//printf("%s", path);
regValue = path;
status = pclose(fp);
if (status == -1) {
/* Error reported by pclose() */
printf("Error reported by pclose()");
}
return regValue;
}
/* Function that performs i2cget */
char* get(const char* reg1, const char* reg2) {
char str[100];
char str2;
strcpy(str, cmdGet);
strcat(str, reg1);
strcat(str, reg2);
char* regValue = exec(str);
return regValue;
}
/* Function that performs i2cset */
char* set(const char* reg1, const char* reg2, const char* value) {
char str[100];
char str2;
strcpy(str, cmdSet);
strcat(str, reg1);
strcat(str, reg2);
strcat(str, value);
//printf("%s\n",str);
char* regValue = exec(str);
return regValue;
}
/* Helper function to check whether or not a bit k is set */
bool bitSet(char* x, int k) {
int n = strtoul(x, NULL, 16);
//printf("%d\n", n);
if (n & (1 << (k - 1))) {
return true;
}
else {
return false;
}
}
/* Event handler for signal from Kernel Module */
void signalhandler(int sig) {
// Never Prints
printf("Event Detected");
// This clears the interrupt, which is how I know the script is functioning
char* orientation = get(deviceADDR, PL_STATUS);
if (bitSet(orientation, 2) && bitSet(orientation, 3) && oriented != left) {
printf("Landscape Left \n");
oriented = left;
}
else if (!bitSet(orientation, 2) && bitSet(orientation, 3) && oriented != right) {
printf("Landscape Right \n");
oriented = right;
}
else if (bitSet(orientation, 2) && !bitSet(orientation, 3) && oriented != down) {
printf("Portrait Down \n");
oriented = down;
}
else if (!bitSet(orientation, 2) && !bitSet(orientation, 3) && oriented != up) {
printf("Portrait Up \n");
oriented = up;
}
}
int main() {
int fd;
signal(SIGTX, signalhandler);
/* Configure the Accelerometer */
printf("Configuring Accelerometer...\n");
set(deviceADDR, CTRL_REG1, " 0x00"); // Put in Standby
set(deviceADDR, CTRL_REG1, " 0x20"); // Set data rate to 50Hz
set(deviceADDR, PL_CFG, " 0x40"); // Enable orientation detection
set(deviceADDR, CTRL_REG4, " 0x10"); // Enable interrupts
set(deviceADDR, CTRL_REG5, " 0x10"); // Route interrupt to INT1 pin
set(deviceADDR, PL_COUNT, " 0x05"); // Set debounce to 100ms
set(deviceADDR, CTRL_REG1, " 0x02"); // Set interrupt to Active High
set(deviceADDR, CTRL_REG1, " 0x01"); // Put in Active Mode
get(deviceADDR, PL_STATUS); // Read PL_STATUS to clear initial interrupt
printf("Configured!\n");
// Also does not print
printf("PID: %d\n", getpid());
/* Open the device file */
fd = open("/dev/irq_signal", O_RDONLY);
if (fd < 0) {
perror("Could not open device file\n");
return -1;
}
/* Register app to Kernel Module */
if (ioctl(fd, REGISTER_UAPP, NULL)) {
perror("Error registering app");
close(fd);
return -1;
}
/* Wait for Signal */
printf("Wait for signal...\n");
while (1)
sleep(1);
return 0;
}
输出来自systemctl status accelMonitor.service
● accelMonitor.service - Accelerometer Orientation Monitor
Loaded: loaded (/lib/systemd/system/accelMonitor.service; enabled; vendor pre
Active: active (running) since Thu 2022-11-17 17:06:52 UTC; 26min ago
Process: 565 ExecStartPre=/etc/systemd/system/deviceFileCreator (code=exited,
Main PID: 655 (accelMonitor)
Tasks: 1 (limit: 1026)
Memory: 1.3M
CGroup: /system.slice/accelMonitor.service
└─655 /etc/systemd/system/accelMonitor
Nov 17 17:06:49 beaglebone systemd[1]: Starting Accelerometer Orientation Monitor..
Nov 17 17:06:52 beaglebone sudo[573]: root : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/bin/mknod /dev/irq_signal c 64 0
Nov 17 17:06:52 beaglebone sudo[573]: pam_unix(sudo:session): session opened for user root by (uid=0)
Nov 17 17:06:52 beaglebone sudo[573]: pam_unix(sudo:session): session closed for user root
Nov 17 17:06:52 beaglebone deviceFileCreator[565]: Creating Device File irq_signal
Nov 17 17:06:52 beaglebone deviceFileCreator[565]: Device file /dev/irq_signal created
Nov 17 17:06:52 beaglebone systemd[1]: Started Accelerometer Orientation Monitor.
更新
我没有使用,而是printf("Hello")
切换到了显式fprintf(stdout,"Hello")
和fprintf(stderr,"Hello")
版本。这是可行的,但是我有一个新问题,即仅打印出 stderr 流,即使两者StandardOutput
在服务单元文件中StandardError
设置为相同的设置(当前)。syslog