前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于进程使用资源的限制(基于linux1.2.13)

关于进程使用资源的限制(基于linux1.2.13)

作者头像
theanarkh
发布2023-10-30 15:50:13
1280
发布2023-10-30 15:50:13
举报
文章被收录于专栏:原创分享原创分享

如今的操作系统都是支持多任务、多用户的,计算机的资源是各个用户和任务共享的。操作系统通过setrlimit系统调用提供控制资源使用的方法。该函数的实现在各版本的内核里不尽相同,现在也支持了更多的能力,本文通过1.2.13的内核大致分析资源使用限制的一些原理。 首先在PCB中加了一个字段记录了限制信息。

代码语言:javascript
复制
struct rlimit rlim[RLIM_NLIMITS]; 
代码语言:javascript
复制
#define RLIMIT_CPU    0       /* CPU time in ms */
#define RLIMIT_FSIZE    1       /* Maximum filesize */
#define RLIMIT_DATA    2       /* max data size */
#define RLIMIT_STACK    3       /* max stack size */
#define RLIMIT_CORE    4       /* max core file size */
#define RLIMIT_RSS    5       /* max resident set size */
#define RLIMIT_NPROC    6       /* max number of processes */
#define RLIMIT_NOFILE    7       /* max number of open files *

rlimit的定义如下

代码语言:javascript
复制
struct rlimit {
    // 当前的限制
    long    rlim_cur;
    // 最大的限制,即调整值不能大于max
    long    rlim_max;
};

看完数据结构我们再看一下修改这个数据结构的函数。

代码语言:javascript
复制
asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim)
{
    struct rlimit new_rlim, *old_rlim;
    int err;

    if (resource >= RLIM_NLIMITS)
        return -EINVAL;
    err = verify_area(VERIFY_READ, rlim, sizeof(*rlim));
    if (err)
        return err;
    memcpy_fromfs(&new_rlim, rlim, sizeof(*rlim));
    old_rlim = current->rlim + resource;
    // 超级用户可以随便修改阈值,非超级用户修改值的时候不能大于之前设置的最大值
    if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
         (new_rlim.rlim_max > old_rlim->rlim_max)) &&
        !suser())
        return -EPERM;
    // RLIMIT_NOFILE代表进程能打开的文件大小,这个是操作系统本身的限制(NR_OPEN),无法突破
    if (resource == RLIMIT_NOFILE) {
        if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN)
            return -EPERM;
    }
    *old_rlim = new_rlim;
    return 0;
}

看完资源限制的表示和设置方法,我们来看看各种限制的实现。

1 RLIMIT_CPU

RLIMIT_CPU代表某个进程使用CPU的时间限制,包括用户态的时间和内核态的时间。当进程的CPU使用时间达到rlim_cur的值的时候,他会收到SIGXCPU信号,这个信号默认的处理是终止进程,但是用户可以设置处理该信号的处理函数,防止进程退出。这样进程可以继续执行,往后的每5秒该进程都会受到SIGXCPU 信号,直到CPU时间达到rlim_max的值,进程会收到SIGKILL信号。然后被终止。我们看看代码的实现。

代码语言:javascript
复制
if (
    (current->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) &&
    (((current->stime + current->utime) % HZ) == 0)
) {
        psecs = (current->stime + current->utime) / HZ;
        // 达到软限制,发送信号
        if (psecs == current->rlim[RLIMIT_CPU].rlim_cur)
            send_sig(SIGXCPU, current, 1);
        // 超过了软限制,每5秒再次发送SIGXCPU信号
        else if ((psecs > current->rlim[RLIMIT_CPU].rlim_cur) &&
                ((psecs - current->rlim[RLIMIT_CPU].rlim_cur) % 5) == 0)
            send_sig(SIGXCPU, current, 1);
    }
代码语言:javascript
复制
// 达到硬限制,发送SIGKILL终止进程
if ((current->rlim[RLIMIT_CPU].rlim_max != RLIM_INFINITY) &&
  (((current->stime + current->utime) / HZ) >= current->rlim[RLIMIT_CPU].rlim_max)
)
    send_sig(SIGKILL, current, 1);

2 RLIMIT_FSIZE

RLIMIT_FSIZE代表进程创建文件大小的限制。当进程创建文件的时候,会触发这个判断,如果达到了阈值。会返回-EFBIG错误码(文档注释说还会收到SIGXFSZ 信号,但是这个版本的内核没有实现)。

3 RLIMIT_DATA

RLIMIT_DATA代表数据使用空间的限制,包括数据段,bss段和堆。因为数据段和bss段在编译的时候已经确认大小,只有堆可以修改大小。所以在修改堆大小的时候会触发这个校验。brk系统调用可以修改堆的大小。

4 RLIMIT_STACK

RLIMIT_STACK代表栈的大小限制。比如我们在栈上访问了一个还没有映射到物理内存的虚拟地址,然后触发缺页中断,正常来说系统会映射一块物理内存到该虚拟地址,但是如果达到了阈值。则进程会收到SIGSEGV信号。

5 RLIMIT_RSS,

进程驻留内存的页数的大小限制

6 RLIMIT_NPROC

RLIMIT_NPROC代表当前进程所属的真实id对应的用户所能创建的最大进程数(线程)。触发校验的时机在fork。

代码语言:javascript
复制
    this_user_tasks = 0;
    free_task = -EAGAIN;
    i = NR_TASKS;
    // 遍历所有进程,找出uid和当前进程进程的uid一样的
    while (--i > 0) {
        if (!task[i]) {
            free_task = i;
            tasks_free++;
            continue;
        }
        if (task[i]->uid == current->uid)
            this_user_tasks++;
    }
    // 达到阈值
    if (this_user_tasks > current->rlim[RLIMIT_NPROC].rlim_cur)
        if (current->uid)
            return -EAGAIN;

7 RLIMIT_NOFILE

RLIMIT_NOFILE代表一个进程能打开文件个数的最大值。触发校验的时机是打开一个文件的时候。

代码语言:javascript
复制
int do_open(const char * filename,int flags,int mode)
{
    struct inode * inode;
    struct file * f;
    int flag,error,fd;
    // 找到一个可用的文件描述符,值小于NR_OPEN和RLIMIT_NOFILE(初始化的值大于NR_OPEN,表示用户没有设置过)。
    for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++)
        // 还没被使用则找到可用的
        if (!current->files->fd[fd])
            break;
    // 找不到可用的
    if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur)
        return -EMFILE;
    ...
}

这个版本的内核实现的限制控制不多,现代版本复杂了很多。今天就先分析到这。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 RLIMIT_CPU
  • 2 RLIMIT_FSIZE
  • 3 RLIMIT_DATA
  • 4 RLIMIT_STACK
  • 5 RLIMIT_RSS,
  • 6 RLIMIT_NPROC
  • 7 RLIMIT_NOFILE
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档