4.4.1. Auto-start Configuration
4.4.1.1. Overview and Introduction
In embedded systems or lightweight Linux environments, auto-start service management typically uses Busybox init. By creating auto-start shell scripts in the /etc/init.d directory, specific tasks or services can be automatically executed during system boot. This document provides a detailed explanation of the system boot process, the underlying principles of auto-start mechanisms, and how to customize auto-start scripts.
4.4.1.2. Detailed Functionality
System Boot Process
From Kernel to init

In the final stage of Linux kernel startup, the start_kernel() function calls reset_init() to create the first process—the idle process with pid=0. This process runs in kernel mode and is the only one not created via fork() or kernel_thread(). Through a series of calls, it eventually enters cpu_idle_loop(), where two critical processes are spawned:
init process: The ancestor of all user-space processes, responsible for managing and spawning user-space processes.
kthreadd process: The ancestor of all kernel threads, responsible for kernel thread management.
The code analysis below clearly shows that pid=0 is the ancestor of all processes and threads, while the init and kthreadd processes handle different responsibilities.
static noinline void __ref rest_init(void)
{
...
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
...
cpu_startup_entry(CPUHP_ONLINE);
}
static int __ref kernel_init(void *unused)
{
...
/* Specified via bootargs "rdinit=/sbin/init"; if set, ramdisk will be launched. */
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
...
}
/* Specified via bootargs "init=/sbin/init", including startup arguments argv_init[]. */
if (execute_command) {
ret = run_init_process(execute_command);
...
}
/* If neither rdinit nor init is specified, try the following fixed paths in order. */
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
...
}
int kthreadd(void *unused)
{
...
/* Setup a clean context for our children to inherit. */
/* Rename kernel thread to kthreadd. */
set_task_comm(tsk, "kthreadd");
...
cgroup_init_kthreadd();
for (;;) {
...
spin_lock(&kthread_create_lock);
/* Kernel threads are created by kthreadd traversing the kthread_create_list */
/* and then extracting entries to create kernel threads via create_kthread(). */
while (!list_empty(&kthread_create_list)) {
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
list_del_init(&create->list);
spin_unlock(&kthread_create_lock);
create_kthread(create);
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
Analysis of the init Process
init_main() is the entry point of the init process in Busybox. This process is responsible for setting up the user-space environment and determining the boot sequence based on the configuration in the /etc/inittab file.
int init_main(int argc UNUSED_PARAM, char **argv)
{
... ...
/* Make sure environs is set to something sane */
/* Set environment variables; SHELL points to /bin/sh */
putenv((char *) "HOME=/");
putenv((char *) bb_PATH_root_path);
putenv((char *) "SHELL=/bin/sh");
putenv((char *) "USER=root"); /* needed? why? */
... ...
/* Check if we are supposed to be in single user mode */
if (argv[1]
&& (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
) {
... ...
} else {
/* Not in single user mode - see what inittab says */
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions (i.e., INIT_SCRIPT and a pair
* of "askfirst" shells) */
/* Parse /etc/inittab file; inittab entries are executed in order: SYSINIT->WAIT->ONCE->RESPAWN|ASKFIRST. */
parse_inittab();
}
... ...
/* Now run the looping stuff for the rest of forever */
while (1) {
... ...
/* Wait for any child process(es) to exit */
while (1) {
/* -1 means wait for any child process. Returns PID of terminated child on success, -1 on error,
* or 0 if WNOHANG is specified and no status change occurred for the target child.
*/
wpid = waitpid(-1, NULL, WNOHANG);
if (wpid <= 0)
break;
/* Set init_action->pid to 0. */
a = mark_terminated(wpid);
if (a) {
message(L_LOG, "process '%s' (pid %u) exited. "
"Scheduling for restart.",
a->command, (unsigned)wpid);
}
}
... ...
} /* while (1) */
}
Within init_main(), the function parse_inittab(void) is called. If /etc/inittab is not configured, parse_inittab(void) applies some default configurations.
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions (i.e., runs INIT_SCRIPT and then starts a pair
* of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
* _is_ defined, but /etc/inittab is missing, this
* results in the same set of default behaviors.
*/
static void parse_inittab(void)
{
#if ENABLE_FEATURE_USE_INITTAB
char *token[4];
parser_t *parser = config_open2("/etc/inittab", fopen_for_read);
if (parser == NULL)
... ...
/* No inittab file - set up some default behavior */
/* Sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
... ...
}
... ...
}
Inside parse_inittab, new_init_action(SYSINIT, INIT_SCRIPT, "") is called, which determines that the initialization script to be executed next is the one defined by INIT_SCRIPT. The default SYSINIT script is /etc/init.d/rcS.
/* Default sysinit script. */
#ifndef INIT_SCRIPT
# define INIT_SCRIPT "/etc/init.d/rcS"
#endif
Checking the content of /etc/inittab in the system confirms that /etc/init.d/rcS will be executed at boot:
rcS:12345:wait:/etc/init.d/rcS
/etc/init.d/rcS
The /etc/init.d/rcS script sequentially executes files matching /etc/init.d/S??* (where ?? represents numbers), launching necessary system services. In implementation, startup scripts are divided into two types: those ending with .sh and others. The execution differs as follows:
Scripts ending in
.sh: Files ending with.share executed using thesourcecommand (. filename). Commands run within the current shell process rather than in a subshell. This allows faster execution and preserves variables and environment settings in the current shell.Non
.shscripts: Files without the.shextension are executed directly via$i start, launching them in a separate process.
#!/bin/sh
# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do
# Ignore dangling symlinks (if any).
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
# Source shell script for speed.
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
# No sh extension, so fork subprocess.
$i start
;;
esac
done
4.4.1.3. Porting and Development
During development and porting, users can easily add or modify auto-start scripts without rebuilding the system image. This is particularly useful during debugging and development, allowing quick validation of auto-start functionality. Based on the above analysis, users can add executable programs under /etc/init.d with names starting with S followed by two digits (e.g., S99auto_startup or S99auto_startup.sh). These programs will automatically run at system startup.
Example: Creating an Initialization Script
Step 1: Create the Script File
vi /etc/init.d/S99my_custom_service
Step 2: Edit the Script
Paste the following content into the editor and save the file.
#!/bin/sh
# Path to your program
PROG="/path/to/your/program"
# Optional arguments for your program
ARGS=""
start() {
echo "Starting my_custom_service"
$PROG $ARGS &
}
stop() {
echo "Stopping my_custom_service"
# Add commands to stop your service gracefully
}
restart() {
stop
sleep 1
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0
Step 3: Set Permissions
Ensure the script has execute permissions:
chmod +x /etc/init.d/S99my_custom_service
Usage Tips
Start service:
sudo /etc/init.d/S99my_custom_service startStop service:
sudo /etc/init.d/S99my_custom_service stopRestart service:
sudo /etc/init.d/S99my_custom_service restart
Notes
Replace
/path/to/your/programwith the actual path to your program.Edit the
ARGSvariable as needed to pass required parameters to your program.Add appropriate stop and restart commands in the
stopandrestartsections.Auto-start programs are prioritized by the number in their prefix (S??); smaller numbers indicate higher priority, ranging from 0 to 99.
In the current system, a pre-configured auto-start script /etc/init.d/S99auto_startup exists. During boot, this script automatically searches for startup.sh in the /app and /userdata/ directories. If startup.sh exists and has execute permissions, it will be automatically executed.
#!/bin/bash
... ...
start()
{
APP_STARTUP="/app/startup.sh"
USERDATA_STARTUP="/userdata/startup.sh"
if [ -x "$APP_STARTUP" ]; then
echo -n "<$LOG_INFO>Starting custom script: $APP_STARTUP" > /dev/kmsg
$APP_STARTUP &
fi
if [ -x "$USERDATA_STARTUP" ]; then
echo -n "<$LOG_INFO>Starting custom script: $USERDATA_STARTUP" > /dev/kmsg
$USERDATA_STARTUP &
fi
}
... ...
During application debugging and development, users can leverage this auto-start mechanism to add startup programs without regenerating the system image.
4.4.1.4. Common Issues
What could cause an auto-start script to fail?
Common causes include missing execute permissions, incorrect paths, or dependencies on services that have not yet started.How can you verify whether an auto-start script has executed?
You can check system logs or add logging output within the script to confirm successful execution.