linux设备模型十三(mdev原理)

udev 和mdev 是两个使用uevent 机制处理热插拔问题的用户空间程序,两者的实现机理不同。

udev 是基于netlink 机制的,它在系统启动时运行了一个deamon 程序udevd,通过监听内核发送的uevent 来执行相应的热拔插动作,包括创建/删除设备节点,加载/卸载驱动模块等等。

mdev 是基于uevent_helper 机制的,它在系统启动时修改了内核中的uevnet_helper 变量(通过写/proc/sys/kernel/hotplug),值为“/sbin/mdev”。这样内核产生uevent 时会调用uevent_helper 所指的用户级程序,也就是mdev,来执行相应的热拔插动作。

udev 使用的netlink 机制在有大量uevent 的场合效率高,适合用在PC 机上;

而mdev 使用的uevent_helper 机制实现简单,适合用在嵌入式系统中。

另外要说明的一点是,uevent_helper 的初始值在内核编译时时可配置的,默认值为/sbin/hotplug。如果想修改它的值,写/proc/sys/kernel/hotplug 文件就可以了,例如:

echo "/sbin/mdev" > /proc/sys/kernel/hotplug

 

mdev有两个主要用途:初始人口和动态更新。 都在内核中需要sysfs支持并将其挂载在/ sys。 对于动态更新,还需要在内核中启用热插拔。

 

下面给出init脚本的典型代码片段:

[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /sbin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s

 

如果没有procfs,上述内容将成为:

[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug = /sbin/mdev
[3] mdev -s

 

当然,更加“完整”的设置需要在前一个代码片段之前执行此操作:

[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts

 

这里简单的解释是[1]你需要在执行mdev之前安装/ sys。 然后你[2]指示内核在添加或删除设备时执行/ sbin / mdev,以便可以创建或销毁设备节点。 然后用[3]创建/ dev与系统启动时创建的所有设备节点。

对于“完整”设置,[4]确保/dev是一个tmpfs文件系统(假设您的闪存用完了)。 然后你想[5]创建/ dev / pts挂载点,最后[6]挂载devpts文件系统。

 

这里可以看一下我的文件系统的启动设置。

# /etc/fstab: static file system information.
#
# Use 'vol_id --uuid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
#       <file system>   <mount point>   <type>  <options>       <dump>  <pass>
        proc            /proc           proc    defaults        0       0
        sysfs           /sys            sysfs   defaults        0       0
        tmpfs           /var            tmpfs   defaults        0       0
        tmpfs           /tmp            tmpfs   defaults        0       0
        tmpfs           /dev            tmpfs   defaults        0       0

挂载文件系统

 

在启动脚本,中指定内核中使用的热插拔程序

/etc/init.d/rcS
#echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

 

 

这里我们看一下mdev的软件代码。


int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int mdev_main(int argc UNUSED_PARAM, char **argv)
{
	RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);

	INIT_G();

#if ENABLE_FEATURE_MDEV_CONF
	G.filename = "/etc/mdev.conf";
#endif

	/* We can be called as hotplug helper */
	/* Kernel cannot provide suitable stdio fds for us, do it ourself */
	bb_sanitize_stdio();

	/* Force the configuration file settings exactly */
	umask(0);

	xchdir("/dev");

	if (argv[1] && strcmp(argv[1], "-s") == 0) {
		/*
		 * Scan: mdev -s
		 */
		struct stat st;

#if ENABLE_FEATURE_MDEV_CONF
		/* Same as xrealloc_vector(NULL, 4, 0): */
		G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
#endif
		xstat("/", &st);
		G.root_major = major(st.st_dev);
		G.root_minor = minor(st.st_dev);

		putenv((char*)"ACTION=add");

		/* Create all devices from /sys/dev hierarchy */
		recursive_action("/sys/dev",
				 ACTION_RECURSE | ACTION_FOLLOWLINKS,
				 fileAction, dirAction, temp, 0);
	} else {
		char *fw;
		char *seq;
		char *action;
		char *env_devname;
		char *env_devpath;
		unsigned my_pid;
		unsigned seqnum = seqnum; /* for compiler */
		int seq_fd;
		smalluint op;

		/* Hotplug:
		 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
		 * ACTION can be "add", "remove", "change"
		 * DEVPATH is like "/block/sda" or "/class/input/mice"
		 */
		env_devname = getenv("DEVNAME"); /* can be NULL */
		G.subsystem = getenv("SUBSYSTEM");
		action = getenv("ACTION");
		env_devpath = getenv("DEVPATH");
		if (!action || !env_devpath /*|| !G.subsystem*/)
			bb_show_usage();
		fw = getenv("FIRMWARE");
		seq = getenv("SEQNUM");
		op = index_in_strings(keywords, action);

		my_pid = getpid();
		open_mdev_log(seq, my_pid);

		seq_fd = -1;
		if (seq) {
			seqnum = atoll(seq);
			seq_fd = wait_for_seqfile(seqnum);
		}

		dbg1("%s "
			"ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
			"%s%s",
			curtime(),
			action, G.subsystem, env_devname, env_devpath,
			fw ? " FW:" : "", fw ? fw : ""
		);

		snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
		if (op == OP_remove) {
			/* Ignoring "remove firmware". It was reported
			 * to happen and to cause erroneous deletion
			 * of device nodes. */
			if (!fw)
				make_device(env_devname, temp, op);
		}
		else {
			make_device(env_devname, temp, op);
			if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
				if (op == OP_add && fw)
					load_firmware(fw, temp);
			}
		}

		dbg1("%s exiting", curtime());
		if (seq_fd >= 0) {
			xwrite_str(seq_fd, utoa(seqnum + 1));
			signal_mdevs(my_pid);
		}
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		RELEASE_CONFIG_BUFFER(temp);

	return EXIT_SUCCESS;
}

 

检查查看后我们可以发现,这个应用程序主要执行三件事

  • 切换到/dev目录[xchdir("/dev")]
  • 依据第一个参数,是不是-s讲应用分为两部分。第一部分,shell中执行mdev -s命令这个一般在系统启动后执行,主要是扫描/sys/dev目录下的char和block目录,并根据这里面的的设备号和名字,在/dev目录下创建所有设备驱动的节点。
  • 第二种情况就是第一个参数不是-s,那就是转们给某一个设备创建设备节点。

 

可以看到,/sys/dev目录下都是以注册设备为名字的,符号链接文件。

 

当人这个符号连接里面的uevent就是存放,设备的信息。

可以看到上面这个例子。

 

下面就是这部分的应用程序代码了

	if (argv[1] && strcmp(argv[1], "-s") == 0) {
		/*
		 * Scan: mdev -s
		 */
		struct stat st;

#if ENABLE_FEATURE_MDEV_CONF
		/* Same as xrealloc_vector(NULL, 4, 0): */
		G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
#endif
		xstat("/", &st);
		G.root_major = major(st.st_dev);
		G.root_minor = minor(st.st_dev);

		putenv((char*)"ACTION=add");

		/* Create all devices from /sys/dev hierarchy */
		recursive_action("/sys/dev",
				 ACTION_RECURSE | ACTION_FOLLOWLINKS,
				 fileAction, dirAction, temp, 0);
	} else {

 

		xstat("/", &st);
		G.root_major = major(st.st_dev);
		G.root_minor = minor(st.st_dev);

上面这句为获取根文件系统所在的设备标识

 

 

		/* Create all devices from /sys/dev hierarchy */
		recursive_action("/sys/dev",
				 ACTION_RECURSE | ACTION_FOLLOWLINKS,
				 fileAction, dirAction, temp, 0);

这个函数是递归函数,它会把/sys/dev目录下的所有文件文件夹都去查看一遍,如果发现dev文件,那么将按照/etc/mdev.conf文件进行相应的配置。如果没有配置文件,那么直接创建设备节点。

 

这里列出代码,不进行升入分析,只需要知道他的目的是为了扫描/dev目录,根据每个目录里面的uevent里面的参数,执行mknod命令来创建设备就行。


/* fileName is (l)stat'ed (depending on ACTION_FOLLOWLINKS[_L0]).
 *
 * If it is a file: fileAction in run on it, its return value is returned.
 *
 * In case we are in a recursive invocation (see below):
 * normally, fileAction should return 1 (TRUE) to indicate that
 * everything is okay and processing should continue.
 * fileAction return value of 0 (FALSE) on any file in directory will make
 * recursive_action() also return 0, but it doesn't stop directory traversal
 * (fileAction/dirAction will be called on each file).
 *
 * [TODO: maybe introduce -1 to mean "stop traversal NOW and return"]
 *
 * If it is a directory:
 *
 * If !ACTION_RECURSE, dirAction is called and its
 * return value is returned from recursive_action(). No recursion.
 *
 * If ACTION_RECURSE, directory is opened, and recursive_action() is called
 * on each file/subdirectory.
 * If any one of these calls returns 0, current recursive_action() returns 0.
 *
 * If !ACTION_DEPTHFIRST, dirAction is called before recurse.
 * Return value of 0 (FALSE) is an error: prevents recursion,
 * the warning is printed (unless ACTION_QUIET) and recursive_action() returns 0.
 * Return value of 2 (SKIP) prevents recursion, instead recursive_action()
 * returns 1 (TRUE, no error).
 *
 * If ACTION_DEPTHFIRST, dirAction is called after recurse.
 * If it returns 0, the warning is printed and recursive_action() returns 0.
 *
 * ACTION_FOLLOWLINKS mainly controls handling of links to dirs.
 * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
 * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
 */

int FAST_FUNC recursive_action(const char *fileName,
		unsigned flags,
		int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
		int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
		void* userData,
		unsigned depth)
{
	struct stat statbuf;
	unsigned follow;
	int status;
	DIR *dir;
	struct dirent *next;

	if (!fileAction) fileAction = true_action;
	if (!dirAction) dirAction = true_action;

	follow = ACTION_FOLLOWLINKS;
	if (depth == 0)
		follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0;
	follow &= flags;
	status = (follow ? stat : lstat)(fileName, &statbuf);
	if (status < 0) {
#ifdef DEBUG_RECURS_ACTION
		bb_error_msg("status=%d flags=%x", status, flags);
#endif
		if ((flags & ACTION_DANGLING_OK)
		 && errno == ENOENT
		 && lstat(fileName, &statbuf) == 0
		) {
			/* Dangling link */
			return fileAction(fileName, &statbuf, userData, depth);
		}
		goto done_nak_warn;
	}

	/* If S_ISLNK(m), then we know that !S_ISDIR(m).
	 * Then we can skip checking first part: if it is true, then
	 * (!dir) is also true! */
	if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
	 !S_ISDIR(statbuf.st_mode)
	) {
		return fileAction(fileName, &statbuf, userData, depth);
	}

	/* It's a directory (or a link to one, and followLinks is set) */

	if (!(flags & ACTION_RECURSE)) {
		return dirAction(fileName, &statbuf, userData, depth);
	}

	if (!(flags & ACTION_DEPTHFIRST)) {
		status = dirAction(fileName, &statbuf, userData, depth);
		if (status == FALSE)
			goto done_nak_warn;
		if (status == SKIP)
			return TRUE;
	}

	dir = opendir(fileName);
	if (!dir) {
		/* findutils-4.1.20 reports this */
		/* (i.e. it doesn't silently return with exit code 1) */
		/* To trigger: "find -exec rm -rf {} \;" */
		goto done_nak_warn;
	}
	status = TRUE;
	while ((next = readdir(dir)) != NULL) {
		char *nextFile;
		int s;

		nextFile = concat_subpath_file(fileName, next->d_name);
		if (nextFile == NULL)
			continue;

		/* process every file (NB: ACTION_RECURSE is set in flags) */
		s = recursive_action(nextFile, flags, fileAction, dirAction,
						userData, depth + 1);
		if (s == FALSE)
			status = FALSE;
		free(nextFile);
//#define RECURSE_RESULT_ABORT -1
//		if (s == RECURSE_RESULT_ABORT) {
//			closedir(dir);
//			return s;
//		}
	}
	closedir(dir);

	if (flags & ACTION_DEPTHFIRST) {
		if (!dirAction(fileName, &statbuf, userData, depth))
			goto done_nak_warn;
	}

	return status;

 done_nak_warn:
	if (!(flags & ACTION_QUIET))
		bb_simple_perror_msg(fileName);
	return FALSE;
}

 

fileAction是根据找文件和创建,dir是找目录或创建目录。我们这里就看一下怎么创建文件。


/* File callback for /sys/ traversal.
 * We act only on "/sys/.../dev" (pseudo)file
 */
static int FAST_FUNC fileAction(const char *fileName,
		struct stat *statbuf UNUSED_PARAM,
		void *userData,
		int depth UNUSED_PARAM)
{
	size_t len = strlen(fileName) - 4; /* can't underflow */
	char *path = userData;	/* char array[PATH_MAX + SCRATCH_SIZE] */
	char subsys[PATH_MAX];
	int res;

	/* Is it a ".../dev" file? (len check is for paranoid reasons) */
	if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
		return FALSE; /* not .../dev */

	strcpy(path, fileName);
	path[len] = '\0';

	/* Read ".../subsystem" symlink in the same directory where ".../dev" is */
	strcpy(subsys, path);
	strcpy(subsys + len, "/subsystem");
	res = readlink(subsys, subsys, sizeof(subsys)-1);
	if (res > 0) {
		subsys[res] = '\0';
		free(G.subsystem);
		if (G.subsys_env) {
			bb_unsetenv_and_free(G.subsys_env);
			G.subsys_env = NULL;
		}
		/* Set G.subsystem and $SUBSYSTEM from symlink's last component */
		G.subsystem = strrchr(subsys, '/');
		if (G.subsystem) {
			G.subsystem = xstrdup(G.subsystem + 1);
			G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
			putenv(G.subsys_env);
		}
	}

	make_device(/*DEVNAME:*/ NULL, path, OP_add);

	return TRUE;
}

创建设备,这里是通过uevent里面的DEVNMAE来创建设备名的。


/* mknod in /dev based on a path like "/sys/block/hda/hda1"
 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
 * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed).
 * NB2: "mdev -s" may call us many times, do not leak memory/fds!
 *
 * device_name = $DEVNAME (may be NULL)
 * path        = /sys/$DEVPATH
 */
static void make_device(char *device_name, char *path, int operation)
{
	int major, minor, type, len;
	char *path_end = path + strlen(path);

	/* Try to read major/minor string.  Note that the kernel puts \n after
	 * the data, so we don't need to worry about null terminating the string
	 * because sscanf() will stop at the first nondigit, which \n is.
	 * We also depend on path having writeable space after it.
	 */
	major = -1;
	if (operation == OP_add) {
		strcpy(path_end, "/dev");
		len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
		*path_end = '\0';
		if (len < 1) {
			if (!ENABLE_FEATURE_MDEV_EXEC)
				return;
			/* no "dev" file, but we can still run scripts
			 * based on device name */
		} else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) {
			dbg1("dev %u,%u", major, minor);
		} else {
			major = -1;
		}
	}
	/* else: for delete, -1 still deletes the node, but < -1 suppresses that */

	/* Determine device name */
	if (!device_name) {
		/*
		 * There was no $DEVNAME envvar (for example, mdev -s never has).
		 * But it is very useful: it contains the *path*, not only basename,
		 * Thankfully, uevent file has it.
		 * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7:
		 * MAJOR=116
		 * MINOR=7
		 * DEVNAME=snd/controlC0
		 */
		strcpy(path_end, "/uevent");
		len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
		if (len < 0)
			len = 0;
		*path_end = '\0';
		path_end[1 + len] = '\0';
		device_name = strstr(path_end + 1, "\nDEVNAME=");
		if (device_name) {
			device_name += sizeof("\nDEVNAME=")-1;
			strchrnul(device_name, '\n')[0] = '\0';
		} else {
			/* Fall back to just basename */
			device_name = (char*) bb_basename(path);
		}
	}
	/* Determine device type */
	/*
	 * http://kernel.org/doc/pending/hotplug.txt says that only
	 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
	 * But since 2.6.25 block devices are also in /sys/class/block.
	 * We use strstr("/block/") to forestall future surprises.
	 */
	type = S_IFCHR;
	if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block")))
		type = S_IFBLK;

#if ENABLE_FEATURE_MDEV_CONF
	G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
#endif
	for (;;) {
		const char *str_to_match;
		regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
		char *command;
		char *alias;
		char aliaslink = aliaslink; /* for compiler */
		char *node_name;
		const struct rule *rule;

		str_to_match = device_name;

		rule = next_rule();

#if ENABLE_FEATURE_MDEV_CONF
		if (!env_matches(rule->envmatch))
			continue;
		if (rule->maj >= 0) {  /* @maj,min rule */
			if (major != rule->maj)
				continue;
			if (minor < rule->min0 || minor > rule->min1)
				continue;
			memset(off, 0, sizeof(off));
			goto rule_matches;
		}
		if (rule->envvar) { /* $envvar=regex rule */
			str_to_match = getenv(rule->envvar);
			dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
			if (!str_to_match)
				continue;
		}
		/* else: str_to_match = device_name */

		if (rule->regex_compiled) {
			int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
			dbg3("regex_match for '%s':%d", str_to_match, regex_match);
			//bb_error_msg("matches:");
			//for (int i = 0; i < ARRAY_SIZE(off); i++) {
			//	if (off[i].rm_so < 0) continue;
			//	bb_error_msg("match %d: '%.*s'\n", i,
			//		(int)(off[i].rm_eo - off[i].rm_so),
			//		device_name + off[i].rm_so);
			//}

			if (regex_match != 0
			/* regexec returns whole pattern as "range" 0 */
			 || off[0].rm_so != 0
			 || (int)off[0].rm_eo != (int)strlen(str_to_match)
			) {
				continue; /* this rule doesn't match */
			}
		}
		/* else: it's final implicit "match-all" rule */
 rule_matches:
		dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
#endif
		/* Build alias name */
		alias = NULL;
		if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
			aliaslink = rule->ren_mov[0];
			if (aliaslink == '!') {
				/* "!": suppress node creation/deletion */
				major = -2;
			}
			else if (aliaslink == '>' || aliaslink == '=') {
				if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
					char *s;
					char *p;
					unsigned n;

					/* substitute %1..9 with off[1..9], if any */
					n = 0;
					s = rule->ren_mov;
					while (*s)
						if (*s++ == '%')
							n++;

					p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
					s = rule->ren_mov + 1;
					while (*s) {
						*p = *s;
						if ('%' == *s) {
							unsigned i = (s[1] - '0');
							if (i <= 9 && off[i].rm_so >= 0) {
								n = off[i].rm_eo - off[i].rm_so;
								strncpy(p, str_to_match + off[i].rm_so, n);
								p += n - 1;
								s++;
							}
						}
						p++;
						s++;
					}
				} else {
					alias = xstrdup(rule->ren_mov + 1);
				}
			}
		}
		dbg3("alias:'%s'", alias);

		command = NULL;
		IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
		if (command) {
			/* Are we running this command now?
			 * Run @cmd on create, $cmd on delete, *cmd on any
			 */
			if ((command[0] == '@' && operation == OP_add)
			 || (command[0] == '$' && operation == OP_remove)
			 || (command[0] == '*')
			) {
				command++;
			} else {
				command = NULL;
			}
		}
		dbg3("command:'%s'", command);

		/* "Execute" the line we found */
		node_name = device_name;
		if (ENABLE_FEATURE_MDEV_RENAME && alias) {
			node_name = alias = build_alias(alias, device_name);
			dbg3("alias2:'%s'", alias);
		}

		if (operation == OP_add && major >= 0) {
			char *slash = strrchr(node_name, '/');
			if (slash) {
				*slash = '\0';
				mkdir_recursive(node_name);
				*slash = '/';
			}
			if (ENABLE_FEATURE_MDEV_CONF) {
				dbg1("mknod %s (%d,%d) %o"
					" %u:%u",
					node_name, major, minor, rule->mode | type,
					rule->ugid.uid, rule->ugid.gid
				);
			} else {
				dbg1("mknod %s (%d,%d) %o",
					node_name, major, minor, rule->mode | type
				);
			}
			if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
				bb_perror_msg("can't create '%s'", node_name);
			if (ENABLE_FEATURE_MDEV_CONF) {
				chmod(node_name, rule->mode);
				chown(node_name, rule->ugid.uid, rule->ugid.gid);
			}
			if (major == G.root_major && minor == G.root_minor)
				symlink(node_name, "root");
			if (ENABLE_FEATURE_MDEV_RENAME && alias) {
				if (aliaslink == '>') {
//TODO: on devtmpfs, device_name already exists and symlink() fails.
//End result is that instead of symlink, we have two nodes.
//What should be done?
					dbg1("symlink: %s", device_name);
					symlink(node_name, device_name);
				}
			}
		}

		if (ENABLE_FEATURE_MDEV_EXEC && command) {
			/* setenv will leak memory, use putenv/unsetenv/free */
			char *s = xasprintf("%s=%s", "MDEV", node_name);
			putenv(s);
			dbg1("running: %s", command);
			if (system(command) == -1)
				bb_perror_msg("can't run '%s'", command);
			bb_unsetenv_and_free(s);
		}

		if (operation == OP_remove && major >= -1) {
			if (ENABLE_FEATURE_MDEV_RENAME && alias) {
				if (aliaslink == '>') {
					dbg1("unlink: %s", device_name);
					unlink(device_name);
				}
			}
			dbg1("unlink: %s", node_name);
			unlink(node_name);
		}

		if (ENABLE_FEATURE_MDEV_RENAME)
			free(alias);

		/* We found matching line.
		 * Stop unless it was prefixed with '-'
		 */
		if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
			break;
	} /* for (;;) */
}

我们要看到的就是它确实调用这个mknod系统函数来创建了设备节点就行,具体其他的都是一些字符串解析,参数分析之类。

			if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
				bb_perror_msg("can't create '%s'", node_name);

 

 

看完了mdev -s方式,下面这种方式就更简单了。不用递归的搜索过程,参数,路径,都是argv传过来的。可以直接找到路径,调用make_device函数来创建设备。当然删除也是相同的道理,这里就不再叙述。

		char *fw;
		char *seq;
		char *action;
		char *env_devname;
		char *env_devpath;
		unsigned my_pid;
		unsigned seqnum = seqnum; /* for compiler */
		int seq_fd;
		smalluint op;

		/* Hotplug:
		 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
		 * ACTION can be "add", "remove", "change"
		 * DEVPATH is like "/block/sda" or "/class/input/mice"
		 */
		env_devname = getenv("DEVNAME"); /* can be NULL */
		G.subsystem = getenv("SUBSYSTEM");
		action = getenv("ACTION");
		env_devpath = getenv("DEVPATH");
		if (!action || !env_devpath /*|| !G.subsystem*/)
			bb_show_usage();
		fw = getenv("FIRMWARE");
		seq = getenv("SEQNUM");
		op = index_in_strings(keywords, action);

		my_pid = getpid();
		open_mdev_log(seq, my_pid);

		seq_fd = -1;
		if (seq) {
			seqnum = atoll(seq);
			seq_fd = wait_for_seqfile(seqnum);
		}

		dbg1("%s "
			"ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
			"%s%s",
			curtime(),
			action, G.subsystem, env_devname, env_devpath,
			fw ? " FW:" : "", fw ? fw : ""
		);

		snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
		if (op == OP_remove) {
			/* Ignoring "remove firmware". It was reported
			 * to happen and to cause erroneous deletion
			 * of device nodes. */
			if (!fw)
				make_device(env_devname, temp, op);
		}
		else {
			make_device(env_devname, temp, op);
			if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
				if (op == OP_add && fw)
					load_firmware(fw, temp);
			}
		}

		dbg1("%s exiting", curtime());
		if (seq_fd >= 0) {
			xwrite_str(seq_fd, utoa(seqnum + 1));
			signal_mdevs(my_pid);
		}
	}


 

 

 

 

 

 

 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的小刺猬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值