July 05, 2013
The gap between embedded systems and desktop systems is lessening.
The gap between embedded systems and desktop systems is lessening. Sure, there are often bits of high-performance code in an embedded system (although, desktop systems are getting pretty performance oriented on audio and video processing). Many embedded systems are turning to Linux to handle networking, GUIs, and other tasks usually found on desktop systems. The popularity of low-cost Linux platforms like the Raspberry Pi and the Beagle Bone hasn’t hurt either. There’s even an increase in interest in AMP (Asymmetric Multiprocessing), where a multicore processor will run Linux on some cores and some traditional RTOS on the others.
To paraphrase an old Army saying, when you want to get something done, there’s the right way, the wrong way, and the UNIX way. If you aren’t an experienced UNIX/Linux user, you may wind up reinventing the wheel when you need to perform some task. That’s why I wanted to talk a little about how things work on a typical system that might be of use in an embedded Linux system.
What brought this to mind was a piece of code I recently was asked to examine. There was nothing wrong with it per se, but it wasn’t necessarily very efficient. The program needed to do something every 15 minutes. It didn’t have to be that precise, either. It just needed to wake up periodically and take a few data samples to send over the network.
The programmer simply put the program in a loop watching the time. That works, of course, but it does eat up resources unnecessarily. Sleeping would have been a little better since the operating system would suspend the calling process until it was time to run.
However, you can do even better that. The key is to use the cron facility provided by nearly all UNIX and Linux systems. The idea behind cron is simple: Each user has a protected file (a crontab) that has a line for each job the system should launch at a particular time.
The file is hidden away in the system, so you can’t edit it directly as a regular user. Instead, you should use one of the following commands:
crontab file crontab –e
The first command replaces your current crontab with a new one. The second lets you edit the file interactively. If you want to see what’s already there you can use the –l option and to remove your crontab, you can use –r.
If the /etc/cron.allow file exists, your user ID has to appear there or you won’t be able to use cron. There is also (possibly) an /etc/cron.deny file, which lists users who are not allowed to use cron. Of course, root can always use cron regardless.
A crontab file can have two kinds of active lines. You can assign environment variables in the way you expect:
This is just like setting an environment variable. However, note that you don’t get any replacement like you do in the shell, so the following command won’t behave the way you expect:
The other type of line that can appear are lines with six fields separated by spaces or tabs. The fields are in this order: minute, hour, day of month, month, day of week, and command.
It might seem odd that you’d specify the day of the month and the day of the week. In reality, you’d rarely specify both (if you do, the command runs when either field matches, which isn’t usually very useful). You can use an asterisk for any field to indicate that any value matches. For example:
0 12 1 * * dayone_command
This line runs dayone_command at 12:00 on the first of every month. You can also specify lists and ranges:
0,5,10 0-4 * * * a_command
This rule runs a_command between midnight and 4am at 0 minutes after the hour, 5 minutes after the hour, and 10 minutes after the hour. There are some other special rules. For example, you could rewrite “0,5,10” as 0-10/5. You can use the same trick after an asterisk. For example “*/2” in the hour field indicates every two hours.
Month and day of week can also handle names (but not lists or ranges of names). So you can say, “Jan” for January (or, for that matter, you can spell it out – cron only looks at the first three letters). If you use numbers for the day of the week, both 0 and 7 represent Sunday.
The command runs all the way to the end of the line. If you need to use multiple lines, the percent character (%) stands in for a newline (unless you escape it with a backslash; then it is just a percent sign).
Cron also takes some special strings that stand in for the first five fields if you want to use them: @reboot, @yearly, @annually, @monthly, @weekly, @daily, @midnight, and @hourly. These all mean what you think (well, @reboot means when cron starts) and all except @reboot can be duplicated by using the right values in the regular fields.
Like most Linux files, the # character indicates a comment. There is a system-wide crontab at /etc/crontab that uses almost the exact syntax except the 6th field is a user name and then the command to run. Many systems have default entries in this crontab that will run scripts hourly, daily, weekly, and monthly if they appear in /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, and /etc/cron.monthly, respectively. Having a program write a script into these directories is an easy way to use cron from a program.
The system crontab that sets these up looks like this:
SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # m h dom mon dow user command 17 * * * * root cd / && run-parts --report /etc/cron.hourly 25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) 52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
Remember, these have the user name because it is a system-wide crontab. Modern versions of cron also look for system crontabs in /etc/cron.d.
There is also a similar program called anacron that can be used instead of cron (although it relies on cron, so it doesn’t actually replace it). This is very similar to cron, but it understands that the system may not be on continuously. The rules use a delay form instead of an absolute runtime and can’t run tasks any faster than once a day, so the file format isn’t the same as cron. There are other cron replacements, including fcron, dcron, cronie, and Vixiecron.
Another alternative, if you are interested, is the possibility of using a remote cron job to cause an action on your system via the network. You could use a “cloud-based” service like https://www.setcronjob.com/, http://www.onlinecronjobs.com/, or http://www.easycron.com/ or you could use cron on a remote Linux box and have it execute commands on the embedded system using ssh or some other mechanism.
If you want to try cron, you might enjoy the cron translator, which converts a cron date/time specification to English or this site, which takes the same input and shows you the next few dozen times a cron job would run given a particular date/time specification.
So while my friend’s code worked by looping for 15 minutes, he might have been better off writing a simple program to do the job he needed and exiting. Then cron would launch the program every 15 minutes. If you have enough resources, of course, it probably doesn’t matter at all. However, if you are fighting to squeeze features into a tiny embedded system, you have a trade to make. If you need cron anyway, the incremental cost of using it will be tiny, if not zero. The cost of having a process running or even sleeping for a long duration will probably be higher. If you could convince yourself you don’t need to run the cron daemon, then that might be a different decision, depending on your overall system.