Linux内核中通过文件描述符获取绝对路径

19 3 月, 2014 by edsionte 无评论 »

背景

在Linux内核中,已知一个进程的pid和其打开文件的文件描述符fd,如何获取该文件的绝对路径?基本思路是先获取该文件在内核中的file结构体,再通过d_path()获取到整个文件的绝对路径。

方法一

如果理解了进程和文件系统数据结构之间的关系,那么这种方法可以采用。基本的方法如下:

1.通过进程pid获取进程描述符task_struct;

2.通过task_struct获取该进程打开文件结构files_struct,从而获取文件描述符表;

3.以fd为索引在文件描述符表中获取对应文件的结构体file;

4.通过file获取对应path结构,该结构封装当前文件对应的dentry和挂载点;

5.通过内核函数d_path()获取该文件的绝对路径;

通过进程pid获取进程描述符demo:

struct task_struct *get_proc(pid_t pid)
{
	struct pid *pid_struct = NULL;
	struct task_struct *mytask = NULL;

	pid_struct = find_get_pid(pid);
	if (!pid_struct)
		return NULL;
	mytask = pid_task(pid_struct, PIDTYPE_PID);
	return mytask;
}

通过fd以及d_path()获取绝对路径demo:

int get_path(struct task_struct *mytask, int fd)
{
        struct file *myfile = NULL;
        struct files_struct *files = NULL;
        char path[100] = {'\0'};
        char *ppath = path;

        files = mytask->files;
        if (!files) {
                printk("files is null..\n");
                return -1;
        }
        myfile = files->fdt->fd[fd];
        if (!myfile) {
                printk("myfile is null..\n");
                return -1;
        }
        ppath = d_path(&(myfile->f_path), ppath, 100);

        printk("path:%s\n", ppath);
        return 0;
}

从上面的代码可以看出,从fd到file结构的获取均通过各个数据结构之间的指向关系获取。

方法二

与方法一的思路相同,但是可以直接使用内核提供的函数fget()进行fd到file的获取。这种方法使用比较简单,程序更加安全,不过就是少了对数据结构关系的思考过程。其实也可以将fget()函数的实现过程作为参考,欣赏内核中代码实现的严谨性。

 

Linux内核文件系统挂载分析

25 2 月, 2014 by edsionte 无评论 »

本文将针对内核版本3.2.0中的mount系统调用实现过程进行简单说明。

1.数据结构

下面将对文件系统挂载过程中涉及到的两个主要数据结构vfsmount和path进行节本说明。

1.1 struct vfsmount

每个挂载在内核目录树中的文件系统都将对应一个vfsmount结构,下面将对该结构中的部分字段进行说明。假设设备/dev/sdc为ntfs文件系统,现需要将其挂载在文件系统为ext3的/home/edsionte/work下。因此,/home/edsionte/work可以被称为ntfs文件系统的挂载点,并且称ntfs文件系统与ext3文件系统形成父子文件文件系统关系。同时ntfs也可称为源文件系统,而ext3也可称为目的文件系统。

struct list_head mnt_hash;

内核将系统内所有已挂载的文件系统通过散列表的形式进行组织,每个vfsmount将处于其对应哈希值的冲突链表当中。mnt_hash字段则为具体冲突链表的元素。

struct list_head mnt_mounts;

如果当前文件系统下挂载了其他的子文件系统,那么这些子文件系统将通过自身vfsmount中的mnt_child字段组成一个链表,该链表头为父文件系统中的mnt_mounts字段。

struct list_head mnt_child;

当前文件系统将通过该字段与其他父文件系统下的子文件系统组成一个链表。

struct vfsmount *mnt_parent;

该字段指向父文件系统对应的vfsmount结构。即指向ext3文件系统对应的vfsmount结构。

struct dentry *mnt_mountpoint;

该字段表示源文件系统在目的文件系统中挂载点对应的dentry结构。/home/edsionte/work为挂载点,则该字段指向目录项work。

struct dentry *mnt_root;

指向当前文件系统的根目录项。对于源文件系统ntfs来说,根目录项相对为/,但在整个系统目录树中,根目录项为work。

struct super_block *mnt_sb;

每个文件系统都将对应一个super_block结构,该字段指向/dev/sdc设备上文件系统对应的超级块。

struct list_head mnt_list;

所有处于一个名字空间的文件系统通过mnt_list字段链接在一起,而该链表的表头为该名字空间结构中的list字段。

struct mnt_namespace *mnt_ns;

该字段表示当前vfsmount所对应的名字空间结构。

1.2 struct path

path结构由vfsmount结构和dentry结构组成。该结构在挂载文件系统时表示目的文件系统的vfsmount结构和挂载点dentry。

2.函数调用关系图

do_mount

3.实现

3.1 mount系统调用服务例程

mount()系统调用服务例程为:

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, char __user *, type, unsigned long, flags, void __user *, data)

其内部实现主要是将用户态的参数依次复制到内核态,接着调用内核函数do_mount()。

3.2 do_mount()

该函数内部首先通过kern_path()获取目的文件系统的path结构,即挂载点目录项以及目的文件系统的vfsmount结构;接着,通过检查flags对挂载操作进行不同目的的分发。这里我们只讨论最普通的情形,即将一个文件系统挂载在一个新的挂载点中,这种情况调用do_new_mount()。

3.3 do_new_mount()

这个函数描述的是挂载一个新文件系统最普遍的情形,主要包括以下几点:

1.文件系统类型、操纵权限检查等;

2.通过do_kern_mount()获取源文件系统的vfsmount结构;

3.通过do_add_mount()将源文件系统增加到目的文件系统中;

3.4 do_add_mount()

1.flags参数合法性检查;

2.检查指定的目的文件系统是否为当前文件系统。如果是,则失败;

3.检查源文件系统的根inode是否为链接文件。如果是,则失败;

4.通过graft_tree()将源文件系统装载到目的文件系统中。其内部graft又封装了attach_recursive_mnt();

3.5 attach_recursive_mnt()

该函数的主要作用是设置父子文件系统的映射关系。具体操作为:

1.通过mnt_set_mountpoint()将子vfsmount中的mnt_parent指向父vfsmount,将子vfsmount的mnt_mountpoint指向位于父文件系统中的挂载点dentry;

2.通过commit_tree()将子文件系统添加到内核的文件系统哈希表中,并将子文件系统添加到父文件系统对应的子文件系统链表中;

3.6 commit_tree()

1.将当前文件系统的名字空间设置为父名字空间,父vfsmount通过当前vfsmount中的mnt_parent获取;再将其连接到父名字空间链表中。

2.将当前vfsmount加入到对应哈希值的冲突链表当中,哈希值通过hash()计算。其中,mnt_hash作为链表元素。

3.将当前vfsmount加入到父vfsmount对应的子文件系统链表mnt_mounts中。其中,mnt_child作为链表元素。

从整个挂载的处理流程上看,挂载的本质就是将源文件系统的vfsmount结构连接到目的文件系统对应的vfsmount结构中,即具体涉及到两个vfsmount中字段的指向问题。两个vfsmount具体父子等级关系,这也对应着内核中目录树的父子等级关系。

参考资料:

1.深入理解Linux内核:http://book.douban.com/subject/2287506/;

2.深入Linux内核架构:http://book.douban.com/subject/4843567/;

3.Linux内核探秘:http://book.douban.com/subject/25817503/;

CFS中的虚拟运行时间

28 4 月, 2013 by edsionte 2 comments »

一直对CFS(Completely Fair Scheduling,完全公平调度)中的虚拟运行时间(vruntime)不太理解,最近在看cgroup中的cpu子系统算是搞清楚了它是怎么回事。

先简单说一下CFS调度算法的思想:理想状态下每个进程都能获得相同的时间片,并且同时运行在CPU上,但实际上一个CPU同一时刻运行的进程只能有一个。也就是说,当一个进程占用CPU时,其他进程就必须等待。CFS为了实现公平,必须惩罚当前正在运行的进程,以使那些正在等待的进程下次被调度。

具体实现时,CFS通过每个进程的虚拟运行时间(vruntime)来衡量哪个进程最值得被调度。CFS中的就绪队列是一棵以vruntime为键值的红黑树,虚拟时间越小的进程越靠近整个红黑树的最左端。因此,调度器每次选择位于红黑树最左端的那个进程,该进程的vruntime最小。

虚拟运行时间是通过进程的实际运行时间和进程的权重(weight)计算出来的。在CFS调度器中,将进程优先级这个概念弱化,而是强调进程的权重。一个进程的权重越大,则说明这个进程更需要运行,因此它的虚拟运行时间就越小,这样被调度的机会就越大。

那么,在用户态进程的优先级nice值与CFS调度器中的权重又有什么关系?在内核中通过prio_to_weight数组进行nice值和权重的转换。

static const int prio_to_weight[40] = {
 /* -20 */     88761,     71755,     56483,     46273,     36291,
 /* -15 */     29154,     23254,     18705,     14949,     11916,
 /* -10 */      9548,      7620,      6100,      4904,      3906,
 /*  -5 */      3121,      2501,      1991,      1586,      1277,
 /*   0 */      1024,       820,       655,       526,       423,
 /*   5 */       335,       272,       215,       172,       137,
 /*  10 */       110,        87,        70,        56,        45,
 /*  15 */        36,        29,        23,        18,        15,
};

而在内核中,进程的虚拟运行时间是自进程诞生以来进行累加的,每个时钟周期内一个进程的虚拟运行时间是通过下面的方法计算的:

一次调度间隔的虚拟运行时间=实际运行时间*(NICE_0_LOAD/权重)

其中,NICE_0_LOAD是nice为0时的权重。也就是说,nice值为0的进程实际运行时间和虚拟运行时间相同。通过这个公式可以看到,权重越大的进程获得的虚拟运行时间越小,那么它将被调度器所调度的机会就越大。

Cgroup简介-资源控制的实现

25 4 月, 2013 by edsionte 无评论 »

Cgroup自身通过文件系统的形式在内核中实现,通过对子系统配置文件的读写即可完成对进程组资源的控制。不过,cgroup对各种资源的实际控制则分布到整个内核代码中。下面从CPU、内存和I/O三个方面说明Cgroup对资源的控制过程。

1 CPU控制

Cgroup对进程组使用CPU的限制是通过cpu和cpuset两个子系统来完成的。cpu子系统主要限制进程的时间片大小,cpuset子系统可为进程指定cpu和内存节点。

1) cpu子系统

cpu子系统主要分布在内核的调度系统中,通过该子系统中的cpu.shares文件可对进程组设置权重。

根据CFS的原理,一个进程的权重越大,那么它的被调度的可能性就越大。那么进程组的权重如何在CFS中体现?CFS将进程和进程组视为同一个调度体,并用sched_entity结构来表示,每个结构中包含该调度体的权重以及虚拟运行时间等。

因此,用户可通过CPU子系统中的cpu.shares文件来控制进程组对CPU的使用。

2) cpuset子系统

通过cpuset子系统中的cpuset.cpus和cpuset.mems可对进程组设定可访问的CPU和内存节点。内核使用cpuset结构来描述cpuset子系统的属性信息,其中该结构中的cpus_allowed和mems_allowed两个字段分别保存上述两个文件的值。同时,进程描述符中也有cpus_allowed和mems_allowed两个字段,其值与cpuset结构保持同步。

cpuset限制进程所能访问的CPU主要通过两方面。首先体现在进程的创建,如果父进程新建子进程时没有指定CLONE_STOPPED标志,则父进程将调用wake_up_new_task()将子进程状态设置为TASK_RUNNING,并将其加入就绪队列。为子进程选择就绪队列的具体工作则由select_task_rq()完成,其内部实现将涉及CPU的检查操作,即在cpus_allowed所指定的cpu范围内为当前进程分配CPU。

其次,当调度器在调度一个进程时,也要通过select_task_rq()进行同样的对比选择。这样就可以保证cgroup实例中的进程只在cpus_allowed所限定的cpu中运行。

cpuset进程所能访问内存节点的限制表现在物理内存的分配过程中。Linux内核将物理内存在逻辑上分为node、zone和page,内核通过alloc_pages()来实现物理内存的分配工作。alloc_pages()的主要工作是在所有物理内存中选择node,再在当前node中选择zone,最终在zone中分配一个物理页框。其中,在选择node的过程中会进行mems_allowed的判断过程。

2 内存控制

cgroup对内存的控制通过memory子系统完成,其控制作用主要体现在对内存使用量的限制,同时为当前cgroup生成一份内存使用情况报告。

在具体实现的过程中,cgroup通过内核中的resource counter机制实现内存的限制。resource counter相当于一个通用的资源计数器,在内核中通过res_counter结构来描述。该结构可用于记录某类资源的当前使用量、最大使用量以及上限等信息。具体描述如下:

struct res_counter {
    /*
     * the current resource consumption level
     */
    unsigned long long usage;
    /*
     * the maximal value of the usage from the counter creation
     */
    unsigned long long max_usage;
    /*
     * the limit that usage cannot exceed
     */
    unsigned long long limit;
    /*
     * the limit that usage can be exceed
     */
    unsigned long long soft_limit;
    /*
     * the number of unsuccessful attempts to consume the resource
     */
    unsigned long long failcnt;
    /*
     * the lock to protect all of the above.
     * the routines below consider this to be IRQ-safe
     */
    spinlock_t lock;    /*
     * Parent counter, used for hierarchial resource accounting
     */
    struct res_counter *parent;
};

内核使用mem_cgroup结构描述cgroup中内存的使用信息,其内部嵌入了res_counter结构。因此res_counter中的每个字段则表示对内存使用量的记录。用户态下memory子系统所导出的配置文件与该结构中的字段互相对应,比如mem.limit_in_bytes表示当前cgroup可使用内存的最大上线,该文件与res_counter结构中的limit字段对应。也就是说,当用户在用户态向mem.limit_in_bytes文件写入值后,则res_counter中的limit字段相应更新。

内核对res_counter进行操作时有三个基本函数:res_counter_init()对res_counter进行初始化;当分配资源时,res_counter_charge()记录资源的使用量,并且该函数还会检查使用量是否超过了上限,并且记录当前资源使用量的最大值;当资源被释放时,res_counter_uncharge()则减少该资源的使用量。

cgroup对内存资源的限制主要是将上述三个函数分布到内存的分配单元中,比如,系统发生缺页异常时,由于页表项未分配而申请内存时,由于页缓存而分配内存等。

3 块I/O控制

Cgroup中通过blkio子系统完成对块设备I/O的控制。具体的控制主要通过blkio.weight文件在用户态设定当前进程组访问块I/O的权重,也就是控制进程组占有I/O的时间。

blkio子系统对块I/O的控制代码主要分布在I/O调度算法中,目前内核中默认的调度算法为CFQ(完全公平队列),该算法与进程调度算法CFS比较类似。

Cgroup简介-子系统与层级

24 3 月, 2013 by edsionte 无评论 »

Cgroup是一种资源控制机制,它将操作系统中的所有进程以组为单位划分,所有进程组以层级结构进行组织。cgroup为每个进程组都指定一组访问资源的行为,这些行为限制了该组进程对资源的访问。

1基本概念

子系统(subsystem)实际上是cgroup对进程组进行资源控制的具体体现。子系统具有多种类型,每个类型的子系统都代表一种系统资源,比如CPU、memory等。当创建一个cgroup实例时,必须至少指定一种子系统。也就是说,这个新建的进程组在访问子系统对应的系统资源时就有了一些限制。具体的限制项与子系统的类型有关。

Cgroup中进程组的层级关系与Linux中进程的层级关系比较类似。在Linux操作系统中,一个进程通过fork()系统调用创建了一个子进程,这两个进程之间存在父子这样的等级关系,并且子进程可以继承父进程的一些资源。系统中所有的进程形成一个树形的等级关系,每个进程都唯一的位于进程树中的某一个位置。

对于cgroup来说,cgroup实例之间也是具体级别关系的,但是它们层级关系是为了更细粒度的对进程组进行资源控制。同时,子cgroup会继承父cgroup的对资源的控制属性。

2子系统与层级的关系

如果要创建一个cgroup实例,通过下面的命令即可完成:

mount –t cgroup –o cpu,memory cpu_memory /cgroup/cpu_memory

可以看到,通过Linux中挂载文件系统的方法就可以创建cgroup实例。其中-t选项指定文件系统类型为cgroup类型,-o指定本次创建的cgroup实例与cpu和momory子系统(或资源)关联,cpu_momory指定了当前cgroup实例在整个cgroup树中所处的层级名称,最后的路径为文件系统挂载点。关于cgroup文件系统本文稍后说明。

图1是一个系统中的cgroup结构图,它包含两个层级,即第一层级cpu_mem和第二层级cpuset_net。接下来以该图为例说明cgroup子系统和层级之间的设置规则。

cgroup_example

图1

1. 系统中第一个被创建的cgroup被称为root cgroup,该cgroup的成员包含系统中所有的进程。

如图1所示,root cgroup位于cpu_mem层,它包含了系统中的所有进程;root cgroup又被分为cgroup1和cgroup2两个子cgroup,它们位于第二层级cpuset_net中。

2. 一个子系统只能位于一个层级中。

如图1中所示,cpu子系统位于第一层级cpu_mem中,那么这个子系统将不能再位于第二层级中。不过这种设置规则并不影响子cgroup对cpu子系统的使用,因为所有的子cgroup都将继承root cgroup所属的cpu子系统。

3. 每个层级中可以关联多个子系统。

图1中第一层级cpu_mem关联了cpu和memory两个子系统。

4. 一个进程可以位于不同层级的cgroup中。

由于root cgroup包含了系统中所有的进程,因此cgroup2中的进程P也位于root cgroup中。从资源控制角度来说,进程P所在的进程组在访问cpu、memory和net时会受到资源限制。

5. 一个进程创建了子进程后,该子进程默认为父进程所在cgroup的成员。

子进程被创建后,继承父进程的cgroup,但是后续可根据需求将子进程移动到其他cgroup中。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key