回到INSMOD_MAIN。
1691 check_module_parameters(f, &persist_parms);
1692
1693 if (optind < argc) {
1694 if (!process_module_arguments(f, argc - optind, argv + optind, 1))
1695 goto out;
1696 }
1697 arch_create_got(f); /* DEPMOD */
1698 hide_special_symbols(f);
当在命令行里运行程序时,我们可以在程序名后跟上参数。在insmod里也可以这样。不过要实现这个功能,模块需要做点工作。为了使自己能接受命令行参数,模块必须使用MODULE_PARM来处理这些变量。例如linux ipv6部分代码里的:
124 /* Default to forward because I got too much mail already. */
125 static int forward = NF_ACCEPT;
126 MODULE_PARM(forward, "i");
Insmod——MODULE_PARM宏
MODULE_PARM在linux-2.4.0/include/linux/module.h中。
209 /* Used to verify parameters given to the module. The TYPE arg should
210 be a string in the following format:
211 [min[-max]]{b,h,i,l,s}
212 The MIN and MAX specifiers delimit the length of the array. If MAX
213 is omitted, it defaults to MIN; if both are omitted, the default is 1.
214 The final character is a type specifier:
215 b byte
216 h short
217 i int
218 l long
219 s string
220 */
221
222 #define MODULE_PARM(var,type) /
223 const char __module_parm_##var[] /
224 __attribute__((section(".modinfo"))) = /
225 "parm_" __MODULE_STRING(var) "=" type
226
227 #define MODULE_PARM_DESC(var,desc) /
228 const char __module_parm_desc_##var[] /
229 __attribute__((section(".modinfo"))) = /
230 "parm_desc_" __MODULE_STRING(var) "=" desc
151 /* Indirect stringification. */
152
153 #define __MODULE_STRING_1(x) #x
154 #define __MODULE_STRING(x) __MODULE_STRING_1(x)
被MODULE_PARM和MODULE_PARM_DESC处理的变量,会将一条类似“parm_var=type”,“parm_desc_var=desc”的信息加入.modinfo段里。以上面的例子,假定模块名为ipv6,在insmod命令里,可以使用insmid ipv6 forward=1。这样的命令,在这个命令里,模块中的变量forward将被设为1。
好了,现在来看看check_module_parameters这个函数,它和INSMOD_MAIN在同一个文件里。
1335 /* Check that all module parameters have reasonable definitions */
1336 static void check_module_parameters(struct obj_file *f, int *persist_flag)
1337 {
1338 struct obj_section *sec;
1339 char *ptr, *value, *n, *endptr;
1340 int namelen, err = 0;
1341
1342 sec = obj_find_section(f, ".modinfo");
1343 if (sec == NULL) {
1344 /* module does not support typed parameters */
1345 return;
1346 }
1347
1348 ptr = sec->contents;
1349 endptr = ptr + sec->header.sh_size;
1350 while (ptr < endptr && !err) {
1351 value = strchr(ptr, '=');
1352 n = strchr(ptr, '/0');
1353 if (value) {
1354 namelen = value - ptr;
1355 if (namelen >= 5 && strncmp(ptr, "parm_", 5) == 0
1356 && !(namelen > 10 && strncmp(ptr, "parm_desc_", 10) == 0)) {
1357 char *pname = xmalloc(namelen + 1);
1358 strncpy(pname, ptr + 5, namelen - 5);
1359 pname[namelen - 5] = '/0';
1360 err = check_module_parameter(f, pname, value+1, persist_flag);
1361 free(pname);
1362 }
1363 } else {
1364 if (n - ptr >= 5 && strncmp(ptr, "parm_", 5) == 0) {
1365 error("parameter %s found with no value", ptr);
1366 err = 1;
1367 }
1368 }
1369 ptr = n + 1;
1370 }
1371
1372 if (err)
1373 *persist_flag = 0;
1374 return;
1375 }
函数的主体是check_module_parameter,这个函数也在同一文件里。
Insmod——check_module_parameter函数
1266 /* Check that a module parameter has a reasonable definition */
1267 static int check_module_parameter(struct obj_file *f, char *key, char *value, int *persist_flag)
1269 {
1270 struct obj_symbol *sym;
1271 int min, max;
1272 char *p = value;
1273
1274 sym = obj_find_symbol(f, key);
1275 if (sym == NULL) {
1276 /* FIXME: For 2.2 kernel compatibility, only issue warnings for
1277 * most error conditions. Make these all errors in 2.5.
1278 */
1279 lprintf("Warning: %s symbol for parameter %s not found", error_file, key);
1280 return(1);
1281 }
1282
1283 if (isdigit(*p)) {
1284 min = strtoul(p, &p, 10);
1285 if (*p == '-')1286
1286 max = strtoul(p + 1, &p, 10);
1287 else
1288 max = min;
1289 } else
1290 min = max = 1;
1291
1292 if (max < min) {
1293 lprintf("Warning: %s parameter %s has max < min!", error_file, key);
1294 return(1);
1295 }
1296
1297 switch (*p) {
1298 case 'c':
1299 if (!isdigit(p[1])) {
1300 lprintf("%s parameter %s has no size after 'c'!", error_file, key);
1301 return(1);
1302 }
1303 while (isdigit(p[1]))
1304 ++p; /* swallow c array size */
1305 break;
1306 case 'b': /* drop through */
1307 case 'h': /* drop through */
1308 case 'i': /* drop through */
1309 case 'l': /* drop through */
1310 case 's':
1311 break;
1312 case '/0':
1313 lprintf("%s parameter %s has no format character!", error_file, key);
1314 return(1);
1315 default:
1316 lprintf("%s parameter %s has unknown format character '%c'", error_file, key, *p);
1317 return(1);
1318 }
1319 switch (*++p) {
1320 case 'p':
1321 if (*(p-1) == 's') {
1322 error("parameter %s is invalid persistent string", key);
1323 return(1);
1324 }
1325 *persist_flag = 1;
1326 break;
1327 case '/0':
1328 break;
1329 default:
1330 lprintf("%s parameter %s has unknown format modifier '%c'", error_file, key, *p);
1331 return(1);
1332 }
1333 return(0);
1334 }
函数首先确定符号存在。根据前面注释的解释,在声明类型之前,可以min-max这样的形式声明该参数可以接受的值的个数。所以,1282~1289行解析可能存在的参数值个数声明,如果没有参数值个数取值范围,就默认为1。1296行处理变量的类型。这里与2.4.0内核有点不符,modutils-2.4.0还可以处理c和p{c, i, l, h, s}这样的类型,如果变量前有p,就表明这些参数来自文件,而且模块退出时,参数值需要回写。
做完这个检查后,如果命令行里带入了参数(optind<argc),处理这些参数。函数process_module_arguments也在这个文件中。
733 static int process_module_arguments(struct obj_file *f, int argc, char **argv, int required)
734 {
735 for (; argc > 0; ++argv, --argc) {
736 struct obj_symbol *sym;
737 int c;
738 int min, max;
739 int n;
740 char *contents;
741 char *input;
742 char *fmt;
743 char *key;
744 char *loc;
745
746 if ((input = strchr(*argv, '=')) == NULL)
747 continue;
748
749 n = input - *argv;
750 input += 1; /* skip '=' */
751
752 key = alloca(n + 6);
753
754 if (m_has_modinfo) {
755 memcpy(key, "parm_", 5);
756 memcpy(key + 5, *argv, n);
757 key[n + 5] = '/0';
758 if ((fmt = get_modinfo_value(f, key)) == NULL) {
758 if (required) {
760 error("invalid parameter %s", key);
761 return 0;
762 }
763 else {
764 if (flag_verbose)
765 lprintf("ignoring %s", *argv);
766 continue; /* silently ignore optional parameters */
767 }
768 }
769 key += 5;
770
771 if (isdigit(*fmt)) {
772 min = strtoul(fmt, &fmt, 10);
773 if (*fmt == '-')
774 max = strtoul(fmt + 1, &fmt, 10);
775 else
776 max = min;
777 } else
778 min = max = 1;
779 } else { /* not m_has_modinfo */
780 memcpy(key, *argv, n);
781 key[n] = '/0';
782
783 if (isdigit(*input))
784 fmt = "i";
785 else
786 fmt = "s";
787 min = max = 0;
788 }
789
790 sym = obj_find_symbol(f, key);
791
792 /*
793 * Also check that the parameter was not
794 * resolved from the kernel.
795 */
796 if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
797 error("symbol for parameter %s not found", key);
798 return 0;
799 }
800
801 contents = f->sections[sym->secidx]->contents;
802 loc = contents + sym->value;
803 n = 1;
804
805 while (*input) {
806 char *str;
807
808 switch (*fmt) {
809 case 's':
810 case 'c':
811 /*
812 * Do C quoting if we begin with a ",
813 * else slurp the lot.
814 */
815 if (*input == '"') {
816 char *r;
817
818 str = alloca(strlen(input));
819 for (r = str, input++; *input != '"'; ++input, ++r) {
820 if (*input == '/0') {
821 error("improperly terminated string argument for %s", key);
822 return 0;
823 }
824 /* else */
825 if (*input != '//') {
826 *r = *input;
827 continue;
828 }
829 /* else handle / */
830 switch (*++input) {
831 case 'a': *r = '/a'; break;
832 case 'b': *r = '/b'; break;
833 case 'e': *r = '/033'; break;
834 case 'f': *r = '/f'; break;
835 case 'n': *r = '/n'; break;
836 case 'r': *r = '/r'; break;
837 case 't': *r = '/t'; break;
838
839 case '0':
840 case '1':
841 case '2':
842 case '3':
843 case '4':
844 case '5':
845 case '6':
846 case '7':
847 c = *input - '0';
849 if ('0' <= input[1] && input[1] <= '7') {
850 c = (c * 8) + *++input - '0';
851 if ('0' <= input[1] && input[1] <= '7')
852 c = (c * 8) + *++input - '0';
853 }
854 *r = c;
855 break;
856
857 default: *r = *input; break;
858 }
859 }
860 *r = '/0';
861 ++input;
862 } else {
863 /*
864 * The string is not quoted.
865 * We will break it using the comma
866 * (like for ints).
867 * If the user wants to include commas
868 * in a string, he just has to quote it
869 */
870 char *r;
871
872 /* Search the next comma */
873 if ((r = strchr(input, ',')) != NULL) {
874 /*
875 * Found a comma
876 * Recopy the current field
877 */
878 str = alloca(r - input + 1);
879 memcpy(str, input, r - input);
880 str[r - input] = '/0';
881 /* Keep next fields */
882 input = r;
883 } else {
884 /* last string */
885 str = input;
886 input = "";
887 }
888 }
889
890 if (*fmt == 's') {
891 /* Normal string */
892 obj_string_patch(f, sym->secidx, loc - contents, str);
893 loc += tgt_sizeof_char_p;
894 } else {
895 /* Array of chars (in fact, matrix !) */
896 long charssize; /* size of each member */
897
898 /* Get the size of each member */
899 /* Probably we should do that outside the loop ? */
900 if (!isdigit(*(fmt + 1))) {
901 error("parameter type 'c' for %s must be followed by"
902 " the maximum size", key);
903 return 0;
904 }
905 charssize = strtoul(fmt + 1, (char **) NULL, 10);
906
907 /* Check length */
908 if (strlen(str) >= charssize-1) {
909 error("string too long for %s (max %ld)",
910 key, charssize - 1);
911 return 0;
912 }
913 /* Copy to location */
914 strcpy((char *) loc, str); /* safe, see check above */
915 loc += charssize;
916 }
917 /*
918 * End of 's' and 'c'
919 */
920 break;
921
922 case 'b':
923 *loc++ = strtoul(input, &input, 0);
924 break;
925
926 case 'h':
927 *(short *) loc = strtoul(input, &input, 0);
928 loc += tgt_sizeof_short;
929 break;
930
931 case 'i':
932 *(int *) loc = strtoul(input, &input, 0);
933 loc += tgt_sizeof_int;
934 break;
935
936 case 'l':
937 *(long *) loc = strtoul(input, &input, 0);
938 loc += tgt_sizeof_long;
939 break;
940
941 default:
942 error("unknown parameter type '%c' for %s",
943 *fmt, key);
944 return 0;
945 }
946 /*
947 * end of switch (*fmt)
948 */
949
950 while (*input && isspace(*input))
951 ++input;
952 if (*input == '/0')
953 break; /* while (*input) */
954 /* else */
955
956 if (*input == ',') {
957 if (max && (++n > max)) {
958 error("too many values for %s (max %d)", key, max);
959 return 0;
960 }
961 ++input;
962 /* continue with while (*input) */
963 } else {
964 error("invalid argument syntax for %s: '%c'",
965 key, *input);
966 return 0;
967 }
968 } /* end of while (*input) */
969
970 if (min && (n < min)) {
971 error("too few values for %s (min %d)", key, min);
972 return 0;
973 }
974 } /* end of for (;argc > 0;) */
975
976 return 1;
977 }
因为使用命令行参数时,一定是param=value这样的形式。所以,746~752行就是分开=2边的字符串。如果模块文件包含.modinfo这个段,那么就通过get_modinfo_value函数在.modinfo段里查找参数的格式。这个函数在同一文件里,而且很简单。
Insmod——get_modinfo_value函数
401 static char * get_modinfo_value(struct obj_file *f, const char *key)
402 {
403 struct obj_section *sec;
404 char *p, *v, *n, *ep;
405 size_t klen = strlen(key);
406
407 sec = obj_find_section(f, ".modinfo");
408 if (sec == NULL)
409 return NULL;
410
411 p = sec->contents;
412 ep = p + sec->header.sh_size;
413 while (p < ep) {
414 v = strchr(p, '=');
415 n = strchr(p, '/0');
416 if (v) {
417 if (v - p == klen && strncmp(p, key, klen) == 0)
418 return v + 1;
419 } else {
420 if (n - p == klen && strcmp(p, key) == 0)
421 return n;
422 }
423 p = n + 1;
424 }
425
426 return NULL;
427 }
在这里调用process_module_parameters时,参数required为1。因此,如果在.modinfo段里找不到这个参数的格式,就要出错返回。如果没有.modinfo段,那么只能简单地根据参数设置值是数字还是字符来确定格式。790行在文件的符号集中查找与=左边字符串相同的符号。796行确定该符号存在,而且不是内核提供的(回忆一下,来自内核的符号保存在SHN_HIRESERVE+1的段,其他模块的符号保存在SHN_HIRESERVE+2以上的模块。由此,可以知道,命令行参数设定的只能是模块自己的符号。
805行开始根据数据声明的格式处理数据。对于字符型数据,如果字符串没有用””包含,则“,”就是分割符。而且在””里的字符串可以使用转义字符。每个字符串通过obj_string_patch保存到obj_file里。如果是数值型数据,则直接写到符号对应的值里。另外,还要测试传给参数的值的个数是否符合声明。