Cgroup简介-概述

10 3 月, 2013 by edsionte 无评论 »

Cgroup(Control Groups)是这样一种机制:它以分组的形式对进程使用系统资源的行为进行管理和控制。也就是说,用户通过cgroup对所有进程进行分组,再对该分组整体进行资源的分配和控制。

1 Cgroup的结构

cgroup中的每个分组称为进程组,它包含多个进程。最初情况下,系统内的所有进程形成一个进程组(根进程组),根据系统对资源的需求,这个根进程组将被进一步细分为子进程组,子进程组内的进程是根进程组内进程的子集。而这些子进程组很有可能继续被进一步细分,最终,系统内所有的进程组形成一颗具有层次等级(hierarchy)关系的进程组树。如下图:

cgroup_tree

由于进程组可以被进一步划分,因此一个进程可能处于多个进程组中,但这些进程组必然不处于同一层级中。

另外,如果某个进程组内的进程创建了子进程,那么该子进程默认与父进程处于同一进程组中。也就是说,cgroup对改进程组的资源控制同样作用于子进程。

2 subsystem

cgroup是一种对进程资源管理和控制的统一框架,它提供的是一种机制(mechanism),而具体的策略(policy)是通过子系统(subsystem)来完成的,子系统是cgroup对进程组进行资源控制的具体行为。机制和策略是Linux操作系统中一种经典的设计思想,所谓机制就是“我要提供哪种功能”,而策略则是“我要怎样来实现这种功能”。

cgroup中每个子系统都代表一种类型的资源,具体如下:

1) cpu子系统:该子系统为每个进程组设置一个使用CPU的权重值,以此来管理进程对cpu的访问。

2) cpuset子系统:对于多核cpu,该子系统可以设置进程组只能在指定的核上运行,并且还可以设置进程组在指定的内存节点上申请内存。

3) cpuacct子系统:该子系统只用于生成当前进程组内的进程对cpu的使用报告。

4) memory子系统:该子系统提供了以页面为单位对内存的访问,比如对进程组设置内存使用上限等,同时可以生成内存资源报告

5) blkio子系统:该子系统用于限制每个块设备的输入输出。首先,与CPU子系统类似,该系统通过为每个进程组设置权重来控制块设备对其的I/O时间;其次,该子系统也可以限制进程组的I/O带宽以及IOPS。

6) devices子系统:通过该子系统可以限制进程组对设备的访问,即该允许或禁止进程组对某设备的访问。

7) freezer子系统:该子系统可以使得进程组中的所有进程挂起。

8) net-cls子系统:该子系统提供对网络带宽的访问限制,比如对发送带宽和接收带宽进程限制。

如果要实现子系统对所属进程组的资源控制,那么就要实现该子系统对应的钩子函数。这个关系与虚拟文件系统类似,VFS提供统一的用户接口,而具体的文件操作则通过文件系统(比如ext3)对钩子函数的实现。具体关系如下图:

cgroup_struct

由图可以看出,cgroup在用户态提供统一的用户接口,而每个子系统对资源的控制功能则通过其钩子函数实现。这样使得cgroup在上层是一个统一的框架,而下层则可以实现多种资源的控制。每个子系统的钩子函数如下:

 
struct cgroup_subsys {
        struct cgroup_subsys_state *(*css_alloc)(struct cgroup *cgrp);
        int (*css_online)(struct cgroup *cgrp);
        void (*css_offline)(struct cgroup *cgrp);
        void (*css_free)(struct cgroup *cgrp);

        int (*can_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset);
        void (*cancel_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset);
        void (*attach)(struct cgroup *cgrp, struct cgroup_taskset *tset);
        void (*fork)(struct task_struct *task);
        void (*exit)(struct cgroup *cgrp, struct cgroup *old_cgrp,
                     struct task_struct *task);
        void (*bind)(struct cgroup *root);
        …… ……
}

3 cgroup文件系统

cgroup在Linux内核中是以文件系统的形式存在的,不过cgroup对应的这种文件系统与proc文件系统类似,都是只存在于内存中的“虚拟”文件系统。既然如此,就可以通过mount命令创建一个cgroup实例。

$ sudo mount -t cgroup -o memory memory_cgroup /dev/cgroup/

即在/dev/cgroup/下创建了一个memory子系统。接下来就可以通过:

$ cat /proc/filesystems | grep cgroup

可看到系统有了cgroup类型的文件系统。

当创建了一个cgroup实例后,对应的挂载点下会有一些文件,这些文件是用户与cgroup进行交互的接口。由于cgroup位于VFS层之下,因此用户可以通过统一的文件操作接口去读取或设置子系统的参数,当然也可以直接使用echo或者cat等命令。

参考:

1.Linux内核文档:

https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt

Linux下CPU的利用率

3 3 月, 2013 by edsionte 无评论 »

CPU利用率是对系统进行性能分析的重要因素,本文将说明CPU时间的组成以及利用率的计算方法。

内核中的时间

具体说明CPU的各种时间之前,先说明内核中几个重要的时间概念。

HZ是系统时钟在一秒内固定发出时钟中断的次数。HZ在编译内核前是可以进行配置的,因此通过下述命令就可以查看当前系统的时钟中断频率:

cat /boot/config-`uname -r` | grep CONFIG_HZ

tick为系统时钟每“滴答”一次的时间,其值为(1/HZ)秒。也就是连续两次时钟中断之间的时间间隔。

jiffies用来计算自系统启动以来tick的次数,也就是说系统时钟每产生一次时钟中断,该变量的值就增加一次。

CPU时间

CPU的工作时间由三部分组成:用户态时间、系统态时间和空闲态时间。具体的组成为;

CPU时间=User time+Nice time+System time+Hardirq time+Softirq time+Waiting time+Idle time+Steal time

空闲态时间只包含了idle time,而用户态时间和系统态时间则由多个部分组成,详析介绍如下。

用户态时间

用户态时间包括用户时间和nice时间。

用户时间(user time)指的是CPU在用户态执行进程的时间。nice时间(nice time)是指系统花费在调整进程优先级上的时间。

内核态时间

内核态时间包括系统时间、软中断时间和硬中断时间。

系统时间(system time)表示CPU在内核运行的时间,如果一个CPU的系统时间占有率高,则说明该系统中某个子系统产生了瓶颈。

软中断时间和硬中断时间分别对应系统在处理软硬中断时候所花费的CPU时间。

此外,waiting time是指CPU在等待I/O完成时所花费的时间。而steal time指的是当前CPU等待另外虚拟的CPU处理完毕时话费的时间。

CPU利用率

CPU利用率可以通过top等命令来查看,它们的计算方法如下:

%us =(User time + Nice time)/CPU时间*100%

%sy=(System time + Hardirq time +Softirq time)/

%id=(Idle time)/CPU时间*100%

%ni=(Nice time)/CPU时间*100%

%wa=(Waiting time)/CPU时间*100%

%hi=(Hardirq time)/CPU时间*100%

%si=(Softirq time)/CPU时间*100%

%st=(Steal time)/CPU时间*100%

查看方法

通过top、iostat和vmstat等命令均可以查看上述CPU的利用率。这些命令的数据来源均来自与/proc/stat文件,不过该文件中的时间是以tick为单位的。

Linux下访问文件的基本模式

5 12 月, 2012 by edsionte 1 comment »

访问文件的操作主要是指读文件和写文件,下文简单说明内核中几种常见的访问文件的方式。

普通模式

读写系统调用的默认方式。以读系统调用为例,默认情况下读函数以阻塞的形式访问数据,并且使用了内核的页高速缓存机制。而写函数则直接将修改后的数据写入页高速缓存就返回。此时O_SYNC和O_DIRECT两个标志均被置0。

同步模式

同步模式主要是指进程将阻塞到数据请求完成为止,但是读操作默认情况下即为阻塞方式,因此该模式主要针对写函数,此时O_SYNC标志被置1,写函数并不是将页高速缓存中的数据修改后就立马返回,而是直到相应数据被写入磁盘后才返回。

直接I/O模式

该模式下的读写操作并不会使用内核中的页高速缓存机制,而是在用户地址空间和磁盘之间直接进行数据传送。此时O_DIRECT标志被置1。

异步模式

异步模式需要使用特定的系统调用来完成,比如aio_read和aio_write。异步模式是指进程在发出数据请求后并不需要以阻塞的方式等待数据,而是立即返回继续执行其他操作,数据的请求工作在后台自动完成。

内存映射模式

内存映射方式和传统意义上的读写系统调用不同,它将磁盘上的文件映射到进程用户空间的一块虚拟内存中,这样对该文件的操作就可以转化为对内存的操作。通过mmap()就可以实现内存映射。

文件操作函数在VFS层的实现

23 11 月, 2012 by edsionte 2 comments »

虚拟文件系统(Virtual Filesystem Switch,VFS)为各种文件系统提供了一个通用的接口,它使得上层进程在进行与文件系统相关的操作时可以使用同一组系统调用,但是系统调用在内核中可以根据不同的文件系统执行不同的操作。

与文件相关的基本操作函数有:open、read、write和close,本文将结合内核源代码分析这些函数在虚拟文件系统中的实现。

1.open()的实现

open系统调用的作用是打开或创建一个文件,并且返回该文件的文件描述符。在内核中,open系统调用主要完成的工作是为此次打开的文件创建file对象,该对象在fd_array数组中的索引值为返回用户空间的文件描述符。

open系统调用对应的系统调用服务例程为sys_open,不过目前内核已经统一使用SYSCALL_DEFINEn这种方式对系统调用服务例程进行定义。在open系统调用服务例程中又直接调用了do_sys_open函数,它是打开动作的主体函数。

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
        char *tmp = getname(filename);
        int fd = PTR_ERR(tmp);

        if (!IS_ERR(tmp)) {
                fd = get_unused_fd_flags(flags);
                if (fd >= 0) {
                        struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
                        if (IS_ERR(f)) {
                                put_unused_fd(fd);
                                fd = PTR_ERR(f);
                        } else {
                                fsnotify_open(f->f_path.dentry);
                                fd_install(fd, f);
                        }
                }
                putname(tmp);
        }
        return fd;
}

用户进程使用open打开文件时将传递文件路径filename,因此该函数第一步先通过getname函数从用户空间读取文件路径到内核空间,暂存到tmp。通过get_unused_fd_flags函数在当前进程的fd_array数据中找到一个何时的位置,并返回其索引。

接下来通过do_filp_open函数执行打开文件的核心操作:根据系统调用中的标志参数flags和访问模式mode设置相应的局部变量以便后续使用;根据要打开文件的路径tmp寻找其inode节点,如果该inode节点不存在并且设置了O_CREATE标志则在磁盘上创建一个新的磁盘索引节点;分配一个新的文件对象,并根据系统调用传递的标志和访问模式设置文件对象的f_flags和f_mode字段;使用索引节点的i_fop字段初始化文件对象的f_op字段;将该文件对象插入到超级块指向的打开文件链表中;如果文件对象操作函数集中的open函数被定义则调用它;最后返回这个文件对象;

如果这个文件对象创建成功,则通过fd_install函数将该文件对象赋值到fd_array数组的第fd个元素中。

2.read()的实现

读文件系统调用read()的作用是根据文件描述符fd读取指定长度size的数据到缓冲区buf中。该系统调用的实现涉及了内核中对I/O进行处理的各个层次,但是对于VFS层来说实现方法比较清晰。

SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
        struct file *file;
        ssize_t ret = -EBADF;
        int fput_needed;
        
        file = fget_light(fd, &fput_needed);
        if (file) {
                loff_t pos = file_pos_read(file);
                ret = vfs_read(file, buf, count, &pos);
                file_pos_write(file, pos);
                fput_light(file, fput_needed);
        }

        return ret;
}

在read系统调用对应的服务例程中,首先使用fget_light函数通过fd获取对应的文件对象;再通过file_pos_read函数获取读文件的起始偏移量,即文件对象的f_pos字段的值;接着通过vfs_read函数进行读操作;通过file_pos_write函数更新文件当前的偏移量;通过fput_light函数释放文件对象;最终返回vfs_read函数的返回值ret,该值则为实际读取数据的长度。

read系统服务例程中最核心的函数即为vfs_read,它的主要工作是选择一个具体的读操作函数。如果当前文件对象操作函数集中的read钩子函数(file->f_op->read)被实现(通常在驱动程序中实现),则调用它。否则使用内核默认的读函数do_sys_read。

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
        ssize_t ret;

        if (!(file->f_mode & FMODE_READ))
                return -EBADF;
        if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
                return -EINVAL;
        if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
                return -EFAULT;

        ret = rw_verify_area(READ, file, pos, count);
        if (ret >= 0) {
                count = ret;
                if (file->f_op->read)
                        ret = file->f_op->read(file, buf, count, pos);
                else
                        ret = do_sync_read(file, buf, count, pos);
                if (ret > 0) {
                        fsnotify_access(file->f_path.dentry);
                        add_rchar(current, ret);
                }
                inc_syscr(current);
        }

        return ret;
}

事实上,do_sys_read函数在内部调用钩子函数aio_read(file->f_op->aio_read),该钩子函数一般指向内核实现的通用读函数generic_file_aio_read。这个通用函数已经不属于我们本文所述的VFS层的实现范畴。

3.write函数的实现

write系统调用在VFS层的实现流程与read类似,只不过在出现read的地方将其相应的置换为write。

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
                size_t, count)
{
        struct file *file;
        ssize_t ret = -EBADF;
        int fput_needed;

        file = fget_light(fd, &fput_needed);
        if (file) {
                loff_t pos = file_pos_read(file);
                ret = vfs_write(file, buf, count, &pos);
                file_pos_write(file, pos);
                fput_light(file, fput_needed);
        }

        return ret;
}

当然最终实现写文件操作的函数也是file->f_op->write或者内核中通用的写操作generic_file_aio_write。

4.close()的实现

close系统调用对应的服务例程中,它首先通过fd在文件对象数组中获取文件对象,接着则将fd处的文件对象清空。接下来的大部分工作都通过filp_close函数完成,它主要的工作是调用flush钩子函数将页高速缓存中的数据全部写回磁盘,释放该文件上的所有锁,通过fput函数释放该文件对象。最后返回0或者一个错误码。

页缓存概述

18 10 月, 2012 by edsionte 无评论 »

页缓存是Linux内核一种重要的磁盘高速缓存,它通过软件机制实现。但页缓存和硬件cache的原理基本相同,将容量大而低速设备中的部分数据存放到容量小而快速的设备中,这样速度快的设备将作为低速设备的缓存,当访问低速设备中的数据时,可以直接从缓存中获取数据而不需再访问低速设备,从而节省了整体的访问时间。

页缓存以页为大小进行数据缓存,它将磁盘中最常用和最重要的数据存放到部分物理内存中,使得系统访问块设备时可以直接从主存中获取块设备数据,而不需从磁盘中获取数据。

在大多数情况下,内核在读写磁盘时都会使用页缓存。内核在读文件时,首先在已有的页缓存中查找所读取的数据是否已经存在。如果该页缓存不存在,则一个新的页将被添加到高速缓存中,然后用从磁盘读取的数据填充它。如果当前物理内存足够空闲,那么该页将长期保留在高速缓存中,使得其他进程再使用该页中的数据时不再访问磁盘。写操作与读操作时类似,直接在页缓存中修改数据,但是页缓存中修改的数据(该页此时被称为Dirty Page)并不是马上就被写入磁盘,而是延迟几秒钟,以防止进程对该页缓存中的数据再次修改。

页缓存的设计需求

页缓存至少需要满足以下两种需求。首先,它必须可以快速定位含有给定数据的特定页。其次,由于页高速缓存中的数据来源不同,比如普通文件、块设备等,内核必须根据不同的数据来源来选择对页缓存的适当操作。

内核通过抽象出address_space数据结构来满足上述两种设计需求。

address_space结构

address_space结构是页高速缓存机制中的核心数据结构,该结构并不是对某一个页高速缓存进行描述,而是以页高速缓存的所有者(owner)为单位,对其所拥有的缓存进行抽象描述。页高速缓存中每个页包含的数据肯定属于某个文件,该文件对应的inode对象就称为页高速缓存的所有者。

页缓存与文件系统和内存管理都有联系。每个inode结构中都嵌套一个address_space结构,即inode字段中的i_data;同时inode中还有i_maping字段指向所嵌套address_spaces结构。而address_space结构通过host字段反指向页高速缓存的所有者。页缓存的本质就是一个物理页框,因此每个页描述符中通过mmaping和index两个字段与高速缓存进行关联。mmaping指向页缓存所有者中的address_space对象。index表示以页大小为单位的偏移量,该偏移量表示页框内数据在磁盘文件中的偏移量。

address_space结构中的i_mmap字段指向一个radix优先搜索树。该树将一个文件所有者中的所有页缓存组织在一起,这样可以快速搜索到指定的页缓存。内核中关于radix树有一套标准的使用方法,它不与特定的数据联系(与内核双联表类似),这样使得使用范围更加灵活。具体操作如下:

radix_tree_lookup():在radix树中对指定节点进行查找;

radix_tree_insert():在radix树中插入新节点;

radix_tree_delete():在radix树中删除指定节点;

此外,该结构中的a_ops字段指向address_space_operations结构,该结构是一个钩子函数集,它表明了对所有者的页进行操作的标准方法。比如writepage钩子函数表示将页中的数据写入到磁盘中,readpage表示从磁盘文件中读数据到页中。通常,这些钩子函数将页缓存的所有者(inode)和访问物理设备的低级驱动程序关联起来。该函数集使得内核在上层使用统一的接口与页缓存进行交互,而底层则根据页缓存中数据的来源具体实现。

通过上面的描述,可以看到address_space结构中的优先搜索树和钩子函数集解决了页高速缓存的两个主要设计需求。

内核对页缓存的操作函数

内核对页缓存的基本操作包含了在一个页缓存所形成的radix树中查找,增加和删除一个页缓存。基于radix的基本操作函数,页高速缓存的处理函数如下:

page_cache_alloc():分配一个新的页缓存;

find_get_page():在页高速缓存中查找指定页;

add_to_page_cache():把一个新页添加到页高速缓存;

remove_from_page_cache():将指定页从页高速缓存中移除;

read_cache_page():确保指定页在页高速缓存中包含最新的数据;

参考:

1. 深入理解Linux内核 第三版

2.深入Linux内核架构

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