经过环境

device_create()->device_register()->device_add()函数如下所示:

int class_device_add(struct class_device *class_dev)
{
       ... ...
       kobject_uevent(&class_dev->kobj, KOBJ_ADD);         // KOBJ_ADD是一个枚举值
              //调用了kobject_uevent_env(kobj, action, NULL);              // action=KOBJ_ADD
}

环境变量

  • unix内核并不检讨环境字符串,它们的解说完全取决于各样应用进程
  • 平时在一个shell启动文件中装置环境变量来决定shell的动作
  • 修改或者扩张环境变量时,只可以影响当下进度以及未来(以前的越发)生成和调用的任何子进度的条件,但不可以影响其父进度的环境

和环境变量相关的函数如下:

#include<stdlib.h>
char *getenv(const char *name);
      返回值:指向与name关联的value的指针;若未找到,返回NULL

int putenv(char *str);
                       返回值:若成功,返回0;若出错,返回非0

int setenv(const char *name, const char *value,
            int rewrite);
int unsetenv(const char *name);
                两个函数返回值:若成功,返回0;若出错,返回-1 

管理 1

进度的开行和平息

水源执行c程序时,利用exec函数调用一个奇异的起步例程,该启动例程丛内核中拿走命令行参数和环境变量值。

The special characters
have the meaning:

wait和waitpid函数

先后正常化或更加终止时,内核都会向父进度发送SIGNAL信号。子进程终止是异步事件,所以该信号也是异步信号。而该信号一般会被父进度默许忽略。或者提供一个信号处理函数来善后。wait和waitpid函数就是内部的信号处理函数的一有的。

wait和waitpid函数分裂如下:

(1)wait会阻塞调用者进程等待直至第一个终止的子进程到来;
(2)waitpid可以通过参数设置,来实现调用者进程不阻塞,或选择要阻
塞等待的子进程;

那边的调用者指的是父进度

 

寄存器变量会存在回滚现象的探赜索隐

在调用setjmp函数时,内核会把如今的栈顶指针保存在env变量中,所以在调用longjmp函数重回该岗位时,全局变量、静态变量、易失变量和自行变量假诺在调用setjmp和longjmp函数之间它们的值被修改过,是不会回滚到setjmp函数调用从前的值(当然,编译器将auto变量优化为寄存器变量除外)。因为,这几个存储器变量的值是储存在内存相应的段中,回到原来栈顶状态时,同样访问的仍旧原来的内存空间。

然则,对于寄存器变量来说,首先要分惠氏点:寄存器变量是用动态储存的不二法门。意思是寄存器变量的值可能存在不相同的寄存器中。即使在调setjmp和longjmp函数之间它们的值被涂改过,那些值可能不会存到setjmp以前的对其赋值的寄存器中,而在调用longjmp函数后,又回去了调用setjmp函数时的事态。那几个时候再读取寄存器变量的值时,读到的是原来那一个寄存器中存储的值而不是修改过的百般寄存器中蕴藏的值,所以出现的回滚现象。

输入ls
/dev/sda1
 -l,可以看看都是通过mdev.conf里安插音信来创建的配备节点,如下图所示:

环境表结构图

管理 2

  • 每个程序都收到到一张环境表
  • 环境表也是一个字符指针数组
  • enrivon叫做环境指针
  • 指针数组叫做环境表
  • 各种指针指向的字符串叫做环境字符串

[1-9] : 匹配1~9的数字,

环境表和环境变量

sda[1-9]+ 0:0 660 * if
[ $ACTION = “add” ]; then mount /dev/$MDEV /mnt; else umount /mnt;
fi

exit和_e(E)ixt函数的状态码

无论进度如何为止,它都会在基础上实施同一段代码(由进程启动和退出图可见)。那段代码来关闭所有的文书描述符,释放具有的蕴藏空间。

程序退出后,利用退出码告知该进程的父进度。父进度经过wait或waitpid函数来形成该子进程的善后工作(获取子进程有关信息释放子进度占用资源)。若父进度没有处理子进程的淡出状态,则子过程变成僵死进度。相反的,若父进度在子进度前停下,则子进度变成孤儿进度。孤儿进度会由1号经过(init进度)接收,大概进程如下:

(1)进程终止时,内核逐个检查所有活动的进程;
(2)分析查找该终止进程的子进程;
(3)将该进程的子进程的父进程ID改为1;

/dev/$MDEV      :表示要开创/注销的万分设备节点

仓储空间分配函数

#include<stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nojy, size_t size);
void *realloc(void *ptr, size_t newsize);
         3个函数返回值:若成功,返回非空指针;若出错,返回NULL
  • malloc函数:开首值不确定;底层通过调用sbrk函数完毕;
  • calloc函数:初叶值为0;
  • realloc函数:追加或回落在此之前分配区的尺寸;当增添长度时,可能将原先分配区的始末移到另一个丰富大的区域,以便在分配区末尾伸张存储区,而新增存储区开端值不确定(例如:可变数组的利用);

注意:这几个动态分配的函数一般在分配存储空间时,会比必要的大。因为在开发空间的内外部分存储记录管理音信。由此,在应用时,千万不要越界访问,以免造成不可预见的结局。

从上面的代码和注释分析到,要运用mdev.conf配置文件,还索要配备busybox的menuconfig,
使mdev扶助mdev.conf选项才行

函数间跳转策略

在c语言中,goto语句是不可能跨函数跳转的。更加是在函数深层调用时的跳转要求,在阴差阳错处理的情形下尤其管用。

#include<setjmp.h>
int setjmp(jmp_buf env);
          返回值:若直接调用,返回0;若从longjmp返回,返回非0
void longjmp(jmp_buf env, int val);

变量值回滚问题:活动变量和寄存器变量会存在回滚现象。利用volatile属性来防止此类情形的发出。(在给变量赋值时,赋的值回首先存储在内存(存储器变量)中,然后在由cpu取走,存储在cpu的寄存器上(寄存器变量)。在做系统优化时,那些频仍利用的变量,会直接存储到寄存器中而不通过内存。)

static void make_device(char *path, int delete) //delete=0:创建, delete=1:卸载
{
       /*判断创建的设备节点是否是有效的设备*/
       if (!delete) {
              strcat(path, "/dev");
              len = open_read_close(path, temp + 1, 64);
              *temp++ = 0;
              if (len < 1) return;
       }

device_name = bb_basename(path);    //通过设备路径,来获取要创建/卸载的设备节点名称
                      //例: path =“/sys /class/sixth_dev/buttons”,那么device_name=“buttons”



type = path[5]=='c' ? S_IFCHR : S_IFBLK;     //判断如果是在/sys/class/目录下,那么就是字符设备
                                              //因为块设备,是存在/sys/block/目录下的


/* 如果配置了支持mdev.conf选项,那么就解析里边内容并执行   */
 if (ENABLE_FEATURE_MDEV_CONF) { 
       /* mmap the config file */
fd = open("/etc/mdev.conf", O_RDONLY);     //调用/etc/mdev.conf配置文件

      ... ...         //开始操作 mdev.conf配置文件
}



       if (!delete) {                  //如果是创建设备节点

              if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;   //获取主次设备号

        /*调用mknod ()创建字符设备节点*/
if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
                     bb_perror_msg_and_die("mknod %s", device_name);


              if (major == root_major && minor == root_minor)
                     symlink(device_name, "root");

              /*若配置了支持mdev.conf选项,则调用chown命令来改变属主,默认uid和gid=0 */
              if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
}

     if (delete) unlink(device_name);           //如果是卸载设备节点
}

atexit函数

一个历程最多能够注册32和函数(例如:signal函数),这个函数由exit函数自动调用。在先后终止时调用那么些函数,形成终止处理程序,来展开落成进度前的了断工作。而exit函数通过atexit函数的挂号记录来判断调用哪些函数。

uid: 

_e(E)xit函数 ISO C 定义这几个函数的目的是为经过提供一种无需运行终止处理程序或信号处理函数的法子而偃旗息鼓程序。但ISO C 对标准I/O流是或不是进行冲洗,那取决于操作系统的兑现。在unix中,是不开展冲洗的。

3.3然后我们以登记一个按键驱动为例 

exit函数

此函数由ISO C
定义,其操作包涵处理终止处理程序,然后倒闭所有标准I/O流。亟待小心的是,它不会处理公事描述符、多进度(父子进程)以及作业控制。

@
创造设备节点之后执行命令  

那几个函数怎样修改环境表的

环境表和环境字符串经常存放在内存空间的高地址处(顶部)。所以在修改它的值时,内存是不可能接二连三向高地址延伸;但又因为,它之下是逐一栈帧,所以也无法向下延长。如何修改它的值的历程如下:

(1)修改环境表

1)新value <= 旧value,直接覆盖旧value的存储空间
2)新value >= 旧value,调用malloc函数,在堆区开辟新的存储空间,
将新value复制到这里,再将这片存储区首地址写到环境表相应的位置处。

(2)新增环境表

1)新增一个环境变量,调用malloc函数开辟新的存储空间,将原来的环
境表复制到该存储区,其次再添加一个环境变量,然后在尾部赋值为NULL,
最后将environ指向该区域;
2)在 1)过程的基础上,调用realloc函数,多次添加环境变量;

注意:以那种方法修改的环境变量只在霎时程序运行时有效,当程序截至时,相应的存储区被系统回收,这一个改动就会失灵。

$  :
 
删除设备节点从前执行命令  

经过启动和平息图

管理 3

command
要进行的授命  

经过终止的景色

5种正常终止的情形:

(1)从main函数返回;
(2)调用exit;
(3)调用_exit和_Exit函数;
(4)最后一个线程调用pthread_exit;
(5)最后一个线程从其启动例程返回;  

3种越发终止情状

(1)调用abort;
(2)接到一个信号;
(3)最后一个线程对取消请求做出响应;

内存管理结构图

管理 4

  • 管理,未初叶化数据段(block started by symbol):在程序早先执
    行之前,内核将此段中的数据先河化为0或空指针;
  • 栈:每趟函数调用时,其重返地址以及调用者的环境音讯(如某些机器寄存器的值)都存放在栈中;
  • 共享库:只需在具有进度都可援引的存储区中保留这种库例程的一个副本;

3.5末段调用mdev_main
()->make_device()函数来创设/卸载设备节点,该函数如下所示:

内存存储结构补充表明

 管理 5

3.大家率先来分析device_create(),是什么来调用到/etc/mdev.conf的,前边再讲如何运用mdev.conf(也可以一直跳过,直接看上边第4小节,怎么样利用)

3.2接下去便在kobject_uevent_env()函数里添加打印新闻,
然后再一次烧内核:

class_device: argv[0]=/sbin/mdev                 //调用mdev

class_device: argv[1]=sixth_dev                      //类名

class_device: envp[0]=HOME=/

class_device: envp[1]=PATH=/sbin:/bin:/usr/sbin:/usr/bin

class_device: envp[2]=ACTION=add             //add:表示添加设备节点,  若=remove:表示卸载设备节点

class_device: envp[3]=DEVPATH=/class/sixth_dev/buttons   //设备的路径

class_device: envp[4]=SUBSYSTEM=sixth_dev                //类名

class_device: envp[5]=SEQNUM=745

class_device: envp[6]=MAJOR=252                          //主设备号

class_device: envp[7]=MINOR=0

 管理 6

组ID  

3.1
device_create()最终调用了:device_create()->device_register()->device_add():

2.其实,可以在/etc/mdev.conf文件里加入一行语句就能兑现机关装载u盘,也得以在里边干任何与设施节点相关的事

3.4终极这几个参数依照/sbin/mdev就进去了busybox的mdev.c的mdev_main()函数里:

@ Run after
creating the device.

       <device
regex
> <uid>:<gid> <octal
permissions
> [<@|$|*>
<command>]

 

它是属于mdev的一个配置文件,而mdev以前就讲过了,它最主要的作用是管制/dev目录底下的配备节点

(PS:
之前创建字符设备节点用的class_device_create(),其实是和device_create作用差不离)

 

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])
{
       char **envp;
       char *buffer;
       char *scratch;
       int i = 0;
       ... ...

       /* 通过KOBJ_ADD获取字符串"add",所以action_string="add"  */
       action_string = action_to_string(action);              // action=KOBJ_ADD


       /* environment index */
       envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);      //分配一个环境变量索引值

       /* environment values */
    buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);     //分配一个环境变量缓冲值      

/* event environemnt for helper process only */
/*设置环境变量*/
       envp[i++] = "HOME=/";
       envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
       scratch = buffer;
       envp [i++] = scratch;
       scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;  //"ACTION= add"
       envp [i++] = scratch;
       scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
       envp [i++] = scratch;
       scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
       ... ...
       /*调用应用程序,比如mdev*/
       if (uevent_helper[0]) {
            char *argv [3];
              argv [0] = uevent_helper;       // uevent_helper[]= "/sbin/hotplug";
              argv [1] = (char *)subsystem;
              argv [2] = NULL;
              call_usermodehelper (argv[0], argv, envp, 0);        //调用应用程序,根据传入的环境变量参数来创建设备节点
       }
}

2.1而/etc/mdev.conf又是何许?

正则表明式,来发布哪一个配备 ,正则表达式讲解链接:https://deerchao.net/tutorials/regex/regex.htm

故而当我们插上u盘,自动创建了/dev/sda1时,mdev便会进去/etc/mdev.conf配置文件,然后实施mount
/dev/
命令,即可自行装载U盘,正如图所示:


充裕以下一句:

 

 

device regex:

$ACTION==”add”   :意味着注册设备节点,否则就是裁撤设备节点

5.接下来便来拔取mdev.conf,完成u盘自动装载

从地方的代码和注释来看,最后通过*argv[],
*envp[]五个字符串数组里面存的环境变量参数来创立设备节点的

4.接下来,便来看看哪些使用mdev.conf, 
参考busybox-1.7.0/docs/mdev.txt文档

*  
始建设备节点之后 和 删除设备节点从前 执行命令  

而取出u盘时,同样自动umount
 /mnt来卸载

输入 insmod
key.ko,打印了以下语句:


 :  再一次匹配两回或更频仍

 

而想利用方面的sda1设施节点,读写多少时,还索要选取mount /dev/sda1
 /mnt,来挂载u盘才行,会显示煞是麻烦,正如图所示:

采用方法如下所示:

 

正如图,进入busybox目录,然后输入make
menuconfig,发现我们早就布署过了该选项了

安顿文件格式:

$ Run before
removing the device.

 管理 7

 

 管理 8

* Run both after
creating and before removing the device.

 

owner       (uid,gid:注册设备节点时,就会被chown命令调用,来改变设备的属主,默许都填0即可)

the format:

当系统中有机关注册设备节点的时候,mdev就会调用/etc/mdev.conf五次,
该公文可以兑现与设备节点相关的事,比如自动装载usb,打印创设的设备节点信息等

 

int mdev_main(int argc, char **argv)
{
... ...
action = getenv("ACTION");           //获取传进来的执行参数,它等于“add”,则表示创建设备节点
env_path = getenv("DEVPATH");      //获取设备的路径“/class/sixth_dev/buttons”
sprintf(temp, "/sys%s", env_path);   //指定temp (真正设备路径)为“/sys/class/sixth_dev/buttons”

if (!strcmp(action, "remove"))           //卸载设备节点
                    make_device(temp, 1);

else if (!strcmp(action, "add")) {       //创建设备节点
                     make_device(temp, 0);
 ... ... 
}

 管理 9

<device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]  

各样参数代表的意思如下:  

 

以八进制表示的权力值,会被chmod命令调用,来改变设备的拜访权限,默许填660即可

大概就是:

vi /etc/mdev.conf

gid: 

octal permissions:

 

1.当大家每一回插入u盘后,都会自动创键U盘的装置节点/dev/sda%d

那是因为其中调用了device_create()达成的, busybox的mdev机制就会基于程序设备号等信息,在/dev下开创立备节点,正如图所示:

3.2
device_create()->device_register()->device_add()->kobject_uevent_env()函数如下所示:

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注