首先讲解Linux操作系统的守护进程,然后对udev守护进程的特别讲解,最后教大家使用uedv(自动挂载u盘)。
特别讲解usb设备连接ARM时,流程和相关信息的查看。
1.ARM的守护进程
ARM的操作系统是Linux,也就是探索Linux操作系统的守护进程。 当提到ARM架构下的Linux操作系统时,我们实际上是在讨论运行在ARM硬件平台上的Linux系统。ARM处理器因其能效比较高而被广泛应用于嵌入式系统和移动设备中。在这样的系统中,守护进程扮演着至关重要的角色。
1.1 守护进程(什么是守护进程)
概念:在Linux操作系统中,守护进程是一种后台运行的程序,它独立于任何用户会话,通常用于执行系统任务或提供持续的服务。
特点:
-
后台运行:守护进程在后台运行,不与任何终端或控制台交互。
-
持续运行:它们通常在系统启动时开始运行,并持续运行直到系统关闭。
-
无用户交互:守护进程设计用于无需用户输入即可独立运行。
-
系统权限:很多守护进程以root权限运行,因为它们需要访问系统资源或执行管理任务。
-
服务提供:它们提供网络服务、文件系统管理、打印服务、系统日志记录等核心功能。
1.2 守护进程的作用(为什么要用到守护进程)
守护进程的作用是执行对系统运行至关重要的后台任务。这些任务可能包括:
-
网络服务:如Web服务器、邮件服务器等。
-
系统监控:如监控系统性能、资源使用情况。
-
定时任务:如定时备份、定时执行脚本等。
-
硬件管理:如打印机队列管理、硬件状态监控。
-
用户服务:如邮件传输代理、数据库服务等。
使用守护进程的好处包括:
-
提高效率:自动化系统管理任务,提高系统运行效率。
-
稳定性:确保关键服务的持续性和稳定性。
-
安全性:通过隔离关键服务来增强系统的安全性。
-
可维护性:简化系统维护和故障排除。
1.3 使用守护进程(编写创建一个守护进程)
创建一个守护进程通常涉及以下步骤:
-
创建一个前台进程:守护进程需要被进程管理器(如systemd或SysVinit)管理。
-
从终端脱离:重定向标准输入、输出和错误流到
/dev/null
或日志文件。 -
运行在后台:确保进程不依赖于任何终端会话。
-
处理信号:正确处理如SIGTERM等信号,以便优雅地关闭。
-
守护进程的日志记录:使用系统日志服务(如syslog或systemd-journald)来记录信息。
1.操作系统的守护进程自带的,能看懂、知道是干什么的。
2.编写守护进程(代码创建)
您提供的代码示例和说明非常详细,涵盖了守护进程的创建、调试和自启动设置的多个方面。下面是对您提供的信息进行排版和补充的完整内容:
2.1daemon() 函数
daemon()
函数用于创建守护进程,它在 Unix/Linux 系统中常用。函数原型如下:
#include <unistd.h> int daemon(int nochdir, int noclose);
-
nochdir
参数:指示是否改变守护进程的当前工作目录。如果为非零值,守护进程的当前工作目录将保持不变。 -
noclose
参数:指示是否关闭所有文件描述符。如果为非零值,守护进程不会关闭标准输入、标准输出和标准错误。 -
返回值:成功创建守护进程时返回 0,失败时返回 -1。
时间日志守护进程
以下是一个使用 daemon()
创建的时间日志守护进程的示例代码:
// time_daemon.c #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <time.h> #include <stdio.h> #include <stdbool.h> static bool flag = true; void func1_handler(int signum) { printf("Process ending\n"); flag = false; } int main() { struct sigaction act; act.sa_handler = func1_handler; act.sa_flags = 0; sigaction(SIGQUIT, &act, NULL); sigemptyset(&act.sa_mask); int ret_daemon = daemon(0, 0); if (ret_daemon == -1) { perror("daemon"); exit(-1); } while (flag == true) { int fd = open("/home/orangepi/time.log", O_WRONLY | O_CREAT | O_APPEND, 0666); if (fd == -1) { perror("open"); exit(-1); } time_t t = time(0); char* time_buf = asctime(localtime(&t)); write(fd, time_buf, strlen(time_buf)); close(fd); sleep(10); } return 0; }
检测守护进程是否运行
以下是一个检测 time_daemon
守护进程是否运行的程序:
//test.c #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <time.h> #include <stdio.h> #include <stdbool.h> int main() { char *cmd = "ps -aux | grep time_daemon | grep -v grep"; char *buffer; buffer = (char *)malloc(128); FILE *file = popen(cmd, "r"); fgets(buffer, 128, file); printf("%s\n", buffer); if (strstr(buffer, "time_daemon") != NULL) { printf("time_daemon is running\n"); return 1; } else { printf("time_deamon is not running\n"); return -1; } }
防止时间日志退出的守护进程
以下是一个防止时间日志守护进程退出的 check
守护进程:
// check.c #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <time.h> #include <stdio.h> #include <stdbool.h> static bool flag = true; int fd; void handler(int sig) { printf("I got a signal %d\nI'm quitting.\n", sig); flag = false; } int check_Process() { char *cmd = "ps -aux | grep time_daemon | grep -v grep"; char *buffer; buffer = (char *)malloc(128); FILE *file = popen(cmd, "r"); fgets(buffer, 128, file); printf("%s\n", buffer); if (strstr(buffer, "time_daemon") != NULL) { printf("time_daemon is running\n"); write(fd, "running\n", strlen("running\n")); return 1; } else { printf("time_deamon is not running\n"); write(fd, "not running\n", strlen("not running\n")); return -1; } } int main() { fd = open("./flag.log", O_CREAT | O_APPEND | O_RDWR, 0666); if (-1 == daemon(0, 0)) { printf("daemon error\n"); exit(1); } struct sigaction act; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGQUIT, &act, NULL)) { printf("sigaction error.\n"); exit(0); } while (flag) { write(fd, "1\n", strlen("1\n")); int ret = check_Process(); if (ret == -1) { system("/home/orangepi/Project/daemon/time_daemon &"); } sleep(2); } close(fd); return 0; }
调试守护进程
在调试守护进程时,由于它们不与终端交互,因此不能使用 printf
将调试信息输出到控制终端。相反,可以使用文件 I/O 函数,如 open
、write
、read
等,将调试信息写入日志文件或文本文件中。
2.2设置守护进程开机自启动
有几种方法可以设置守护进程开机自启动:
-
使用 systemd:创建 systemd 服务单元文件,并启用服务。
-
使用 init 脚本:在
/etc/init.d/
目录下创建 init 脚本,并添加到适当的运行级别。 -
使用 rc.local:编辑
/etc/rc.local
文件,在启动时运行守护进程。
以下是使用 systemd 设置开机自启动的示例步骤:
-
创建服务单元文件:
sudo nano /etc/systemd/system/time_daemon.service
内容可能如下:
[Unit] Description=Time Logging Daemon [Service] Type=simple ExecStart=/path/to/time_daemon Restart=always User=orangepi [Install] WantedBy=multi-user.target
请将
/path/to/time_daemon
替换为您的time_daemon
程序的实际路径。 -
启动并启用服务:
sudo systemctl start time_daemon.service sudo systemctl enable time_daemon.service
-
对于
check
守护进程,重复上述步骤,创建相应的check.service
文件。
通过上述步骤,您可以确保 time_daemon
和 check
守护进程在系统启动时自动运行。如果您遇到任何问题或需要进一步的帮助,请随时提问。
以下是使用 rc.local 设置开机自启动的示例步骤:
2. udev守护进程
2.1 udev(什么是udev)
概念:udev
是 用户空间的设备管理器、更是Linux 系统中的一个用户空间守护进程,负责管理设备节点的动态创建和删除。它基于内核的 uevent 机制工作,能够根据硬件状态的变化动态地创建或移除设备文件。
特点:通过使用 udev
,Linux 系统能够更加灵活和自动化地管理硬件设备,提高了系统的易用性和可维护性。
-
事件驱动:
udev
响应来自内核的 uevent,这些事件通常与硬件设备的添加、移除或更改有关。 -
规则系统:
udev
使用一套复杂的规则系统来确定如何处理设备事件。 -
动态设备节点:
udev
可以自动创建或移除位于/dev
目录下的设备节点,无需重启系统。 -
持久化设备名称:
udev
支持持久化设备名称,即使设备被移除再重新插入,也能保持相同的设备节点名。 -
访问控制:
udev
可以设置设备节点的权限,控制哪些用户或组可以访问特定的硬件设备。
2.2 udev的作用(使用udev的好处)
udev
的主要作用包括:
-
自动化:自动管理设备的添加和移除,无需手动干预。
-
即插即用:支持即插即用功能,用户可以无缝地使用USB等热插拔设备。
-
设备管理:简化了设备的管理,使得设备管理更加灵活和高效。
-
权限管理:提供了细粒度的访问控制,增强了系统的安全性。
-
系统维护:减少了系统维护的复杂性,因为许多设备相关的任务由
udev
自动处理。
2.3 udev的工作流程
-
内核检测到硬件状态变化(如设备插入)。
-
内核发送
uevent
到用户空间。 -
udev
监听到uevent
并触发规则处理。 -
udev
根据规则执行操作,如创建设备节点或执行脚本。 -
操作完成后,
udev
通知系统和其他应用程序。
3. 配置、使用udev
3.1 如何使用udev
-
监控
udev
事件:使用udevadm monitor
命令来实时查看udev
事件。 -
查询设备信息:使用
udevadm info
命令来获取设备的详细信息。 -
测试规则:使用
udevadm test
命令来测试规则文件。
3.2 udev配置文件
-
/etc/udev/udev.conf
:这是udev
的主配置文件,包含了udev
的全局设置。
3.3 udev规则文件
-
/etc/udev/rules.d/
:这是自定义udev
规则文件的存放位置。 -
/lib/udev/rules.d/
:系统默认的udev
规则文件存放位置。 -
/run/udev/rules.d/
:在系统运行时,udev
会自动将某些规则放入此目录。
通过编写规则文件,用户可以根据设备的特征(如Vendor ID、Product ID、Serial Number等)来指定设备的行为,如自动挂载文件系统、设置权限、执行自定义脚本等。
使用 udev
可以极大地提高硬件设备的管理效率,使得Linux系统更加灵活和强大。
4. 使用udev自动挂载U盘
4.1 U盘为什么要挂载
在Linux系统中,"挂载"是指将一个文件系统映射到系统目录结构中的一个点,使得用户可以访问该文件系统中的文件。U盘(或任何存储设备)在使用前需要挂载的原因包括:
-
文件访问:允许用户读取和写入U盘中的文件。
-
系统识别:让操作系统识别U盘作为一个可访问的存储介质。
-
设备管理:通过挂载,系统可以更有效地管理设备和文件系统。
-
持久访问:挂载后,即使设备被拔出再插入,系统也能通过相同的路径访问U盘。
4.2 挂载U盘
挂载U盘可以通过手动或自动(使用udev)的方式进行。
4.2.1 手动挂载U盘
手动挂载U盘涉及以下步骤:
-
识别设备:使用
lsblk
或fdisk -l
命令识别U盘的设备名称(如/dev/sdb1
)。 -
创建挂载点:创建一个目录作为挂载点,如
mkdir /mnt/usb
。 -
挂载设备:使用
mount
命令将U盘挂载到创建的目录,如mount /dev/sdb1 /mnt/usb
。 -
访问文件:现在可以访问挂载点
/mnt/usb
下的文件。流出:
orangepi@orangepizero2:~$ sudo mkdir /mnt/usb orangepi@orangepizero2:~$ sudo mount /dev/sda /mnt/usb
不成功,改为 sudo mount /dev/sda1 /mnt/usb,成功。在 Linux 系统中,
/dev/sda
通常指的是整个磁盘设备,而/dev/sda1
指的是该磁盘上的一个特定分区。 挂载/dev/sda1
而不是/dev/sda
可以更精确地控制您想要访问的存储内容,并且可以避免潜在的跨分区操作。自动挂载:(可能是不用udev,还未试验)
如果分区成功挂载,您应该在
/mnt/usb
下看到该 USB 设备的文件系统内容。现在,您可以像访问本地磁盘一样访问这个 USB 设备上的数据。请注意,为了保持挂载在系统重启后依然有效,您可能需要将挂载点添加到
/etc/fstab
文件中。这通常涉及到找到 USB 设备的 UUID(通用唯一识别码),并将其与挂载点关联起来。要找到设备的 UUID,您可以使用
blkid
命令:sudo blkid
这将列出所有已识别的块设备及其 UUID。找到对应于
/dev/sda1
的 UUID 后,您可以按照以下格式将其添加到/etc/fstab
文件中:UUID=<UUID-of-sda1> /mnt/usb <fs-type> <mount-options> 0 0
将
<UUID-of-sda1>
替换为实际的 UUID,<fs-type>
替换为文件系统类型(如vfat
、ext4
等),<mount-options>
替换为所需的挂载选项(如defaults
、ro
(只读)等)。编辑
/etc/fstab
文件通常需要 root 权限,可以使用nano
或vim
等文本编辑器:sudo nano /etc/fstab
或者:
sudo vim /etc/fstab
在文件中添加上述行后,保存并退出编辑器。这样,每次系统启动时,
/dev/sda1
都会自动挂载到/mnt/usb
。
手动挂载是临时的,当系统重启或U盘被拔出后,挂载不再存在。
4.2.2 udev自动挂载
使用udev自动挂载U盘可以省去手动挂载的步骤,udev会根据规则自动完成挂载。配置udev自动挂载的步骤包括:
-
确定挂载点:选择或创建一个目录作为U盘的永久挂载点,如
/mnt/usb
。 -
创建规则文件:在
/etc/udev/rules.d/
目录下创建一个规则文件,如70-usb-automount.rules
。(此处的名字决定了它的加载顺序。由于我在使用70-usb-automount.rules时不能正确的挂载U盘,而重名为 usbpan.rules后,改变了它的加载顺序,所以导致了挂载成功,在这里就用更改后的名字) -
编写udev规则:在规则文件中编写udev规则,指定何时以及如何自动挂载U盘。例如:
1.这个规则文本是人工智能给出的:(目前测试还不行,好像成功了) ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", RUN+="/bin/mount -t auto /dev/%k /mnt/usb" 2.usbpan.rules自动挂载U盘 ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"
这条规则表示当一个USB设备被添加,且其供应商ID和产品ID分别匹配时,自动以自动识别的文件系统类型将该设备挂载到
/mnt/usb
。 -
重新加载udev规则:使用
udevadm control --reload-rules
命令重新加载udev规则。(就是重启下。不过触发 udev:如果 USB 设备已经连接,触发 udev 来检查规则:sudo udevadm trigger) -
测试规则:插入U盘后,使用
udevadm monitor --property
命令监控udev事件,确保规则被正确触发。 -
访问文件:U盘被插入时,udev将自动将其挂载到指定的挂载点。
用tree命令查看:
(成功地安装了
tree
命令,并使用它列出了/media/
目录的内容。tree
是一个递归地列出目录内容的命令,它可以以树状图的方式显示目录和子目录的结构,非常直观。根据
tree /media/
命令的输出,我们可以看到:复制/media/ ├── sdc └── sdc1
这表明在
/media/
目录下有两个目录:sdc
和sdc1
。通常,sdc
可能代表一个整个磁盘设备,而sdc1
是该磁盘上的一个分区。在大多数 Linux 系统中,可移动存储设备(如 USB 闪存驱动器或外部硬盘驱动器)会被自动挂载到/media/
目录下。)
使用udev自动挂载U盘的好处是方便和自动化,用户无需每次手动执行挂载命令,当U盘被拔出时,udev也可以自动卸载设备,保持系统的整洁和安全。
注:挂载的U盘,设备节点都是在根目录下。