一、介绍

当linux系统中的一个进程运行起来的时候,总是要访问系统的资源,访问文件或者向其他的进程发送信号。系统是否允许其进行这些操作?系统是根据什么来判断该进程的权限?这些问题是和进程信任状(process credentials)相关。

process credentials包括一系列的ID,如下:

1、real user ID 和 real group ID

2、effective user ID 和 effective group ID

3、saved set-user-ID 和 saved set-group-ID

4、file-system user ID 和 file-system group ID

5、supplementary group IDs

二、什么是real user ID 和 real group ID

real user ID 和 real group ID标识了该进程属于哪一个用户(哪一个组)。Swapper和init进程的real user ID 和 real group ID都被设定为root(ID=0),用户登陆后,其对应的shell进程的real user ID 和 real group ID会被设定为登录用户。这是login进程调用setuid函数设定的。在fork进程的时候,子进程的credentials是继承自其父进程。

三、什么是effective user ID 和 effective group ID

就如同其命名,真正去检查一个进程是否有权限进行某些动作(例如访问IPC对象、通过系统调用请求内核服务等)的是effective user ID 和 effective group ID。一般而言,effective user ID(effective group ID)是和real user ID(real group ID)一样的,但是如果可执行文件设定了Set-User-ID(Set-Group-ID),那么在进程创建的时候,其effective user ID 和 effective group ID则分别等于该可执行文件的user ID。

四、什么是saved set-user-ID 和 saved set-group-ID

Linux kernel中的task_struct中定义了这些ID如下:

uid_t uid,euid,suid,fsuid;

gid_t gid,egid,sgid,fsgid;

这里的suid(sgid)表示了saved set-user-ID(saved set-group-ID),这里的变量命名非常不友好,表面看起来是save了real ID,但实际上是save了effective ID。为何要save effective ID?其实这是和一个准则相关的:一个进程应该以尽可能小的权限运行。大部分的嵌入式软件工程师都会忽略这一点,因为嵌入式系统基本不是多用户的,开发者都是用root登录进入系统,全部掌管一切。此外,saved set-user-ID(saved set-group-ID)是和Set-User-ID(Set-Group-ID)相关的。例如张三启动一个owner是root的可执行程序,并且该程序设定了Set-User-ID bit,那么,当该程序执行的时候,real user ID是张三,effective user ID是root,saved set-user-ID由于是copy自effective user ID,因此也是root。该进程并不是总是需要root权限,因此,基于进程应该以尽可能小的权限运行的准则,在不需root权限的时候可以通过系统调用修改effective user ID为张三(对于unprivileged的用户,effective user ID只能在real user ID和saved set-user-ID之间切换)。而在需要root权限的时候,可以通过系统调用修改effective user ID回root。正因为如此,进程才需要一个saved set-user-ID来保存原始的effective user ID。

同样的道理适用于saved set-group-ID,这里不再赘述。

五、什么是file-system user ID 和 file-system group ID

这个ID是linux特有的,传统的unix并没有这个ID。对于传统的unix,访问文件、发送signal,打开IPC的object等等的权限都是依据effective ID判断。对于linux,其余的权限仍然依据effective ID判断,但是对于文件的访问则使用file-system user ID 和 file-system group ID(当然,需要配合supplementary group IDs)。

在linux kernel中,file-system user ID(file-system group ID)都是跟随effective user ID(effective group ID)。例如,如果owner是root的可执行文件如果设定了Set-User-ID bit,那么,当该程序执行的时候, effective user ID是root,file-system user ID也跟随effective user ID被设定为root。如果通过系统调用修改effective user ID,file-system user ID也会随之修改。这样就保证了linux的权限判断和传统的unix是一样的。不一样的地方在于linux提供了两个特别的系统调用setfsuid() 和setfsgid()来设定进程的file-system user ID和file-system group ID。

为何linux要引入file-system user ID 和 file-system group ID?这是和NFS (Network File System)相关的。考虑下面的场景:运行NFS server进程的A主机开放其文件系统,运行NFS client进程的B主机可以通过mount NFS file system象访问本地文件那样访问A主机的文件。在这样的场景下,NFS client进程访问A主机上的文件当然应该应用NFS client的effective ID,但是由于是网络文件系统,实际访问文件的是NFS server进程。如果修改NFS server的effective ID的话,用户空间的进程可以通过向NFS server发送signal来攻击。解决这个问题有两个思路:

1、  不修改effective ID,引入file-system user ID(file-system group ID)

2、  修改effective ID,改变信号发送的机制。也就是当A进程发送信号给B进程,该操作是否允许不再和B进程的effective ID相关。

在linux kernel的早期版本采用了方案一,但是2.0之后的kernel版本采用了方案二。因此,file-system user ID 和 file-system group ID应该是被废弃的,但是,为了软件的兼容性,linux kernel仍然保留了这两个file-system ID。

六、什么是supplementary group IDs

当一个用户登录后,loggin程序(其user ID是root)会根据/etc/passwd(还有/etc/shadow)中的信息来校验密码,并设定该登录用户的shell进程的user ID和第一个group ID。由于一个用户ID可能属于多个group,通过/etc/group文件,loggin程序可以知道该用户还属于哪些group,并设定该登录用户的shell进程的supplementary group IDs。

当用户通过shell创建新的进程的时候,子进程的supplementary group IDs是继承自其父进程。supplementary group IDs会配合file-system ID和effective ID来进行进程是否有访问某些资源权限的判定。

一、概述

本文主要描述在linux kernel中如何标识一个或者一组和进程(线程)相关的实体,包括:

1、进程ID(线程组ID)

2、线程ID

3、进程组ID

4、Session ID

需要强调的是本文focus在identification,很多展开的内容会有一系列文档描述。

二、什么是进程ID(线程组ID)

一般而言,我们都会定义进程是一个正在执行的程序或者是一个程序的运行实例。程序是一个静态的概念,是存储在磁盘上的二进制可执行文件,包括程序代码和数据(正文段、数据段等)。当程序运行起来成为进程的时候,单纯的程序代码则不能清楚的描述进程,它还需要若干数据结构来描述程序的执行状态(硬件上下文和软件上下文)以及拥有的资源(如地址空间、打开的文件描述符等)。从内核的角度看,进程是一个和系统资源(CPU time、memory等)分配相关的实体。

在POSIX标准中,系统定义了getpid函数来获取一个进程的process ID。在linux kernel中,定义如下:

asmlinkage long sys_getpid(void)

{

return current->tgid;

}

从字面上看task_struct中的tgid是thread group ID,也就是线程组ID,在linux kernel中,进程是有一个或者多个thread组成(POSIX规定多个thread要共享一个进程ID)。对于linux kernel,每一个thread分配一个task_struct(其中有一个pid的标识),这和大部分的kernel处理不一样,其他的kernel针对一个进程分配一个task_struct,在该task_struct中嵌入了属于该进程的各个线程的数据。正因为如此,linux kernel创建一个线程组的概念来映射到POSIX中的进程概念。

多线程的进程中第一个线程(主线程,group leader)的pid等于tgid,之后,该线程组中的线程都有自己的pid,但是共享tgid,也就是传统意义上的进程ID。task_struct有一个group_leader成员,指向该task的thread group leader,对于group leader,该成员指向自己的task_struct数据结构。

三、什么是线程ID

线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不独立拥有系统资源,它是与同属一个进程的其它线程共享进程所拥有的全部资源(如地址空间、文件描述符和信号处理)。这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量等资源。进程是资源管理的最小单元,而线程是程序执行的最小单元。除了共享的进程资源,进程中的各个线程也属于自己的资源,具体包括:stack、PC counter和CPU寄存器。

每个线程应该有自己的ID,就是线程ID,在linux kernel中,每一个thread分配一个task_struct,该结构中的pid成员就是线程ID。在POSIX标准中,定义了pthread_self来获取线程ID,linux kernel采用了gettid的系统调用来获取调用者的线程ID。在linux kernel中,定义如下:

asmlinkage long sys_gettid(void)

{

return current->pid;

}

POSIX规定线程ID在所属进程中是唯一的,不过在linux kernel的实现中,thread ID是全系统唯一的,当然,考虑到可移植性,Application software不应该假设这一点。

四、什么是进程组ID

每个进程属于一个进程组,每个进程组有一个Leader进程,也就是进程ID等于进程组ID的那个进程。进程组有生命周期,它的生命周期开始于进程组leader创建进程组,结束于进程组内的最后一个进程离开进程组(可能是进程退出, 或加入其他进程组)。进程组概念的提出主要是由于:

1、和job control相关,关于job control,后续会专门的文档详细描述。

2、 Signal可以发送给进程组的每一个进程(job control也使用这个特性。例如job control signal会被送给job(进程组)中的每一个进程)

3、进程同步的时候,父进程可以wait for进程组中的任何一个进程

在POSIX标准中,系统定义了getpgid函数来获取一个进程的process group ID。在linux kernel中,定义如下:

asmlinkage long sys_getpgid(pid_t pid)

{

if (!pid) {

//如果pid等于0,那么需要获取当前进程的进程组ID

return process_group(current);

}

else {

//否则,获取pid标识的那个进程对应的进程组ID

int retval;

struct task_struct *p;

read_lock(&tasklist_lock);

p = find_task_by_pid(pid);

retval = -ESRCH;

if (p) {

//是否有权利获取其他进程的进程组ID

retval = security_task_getpgid(p);

if (!retval)

retval = process_group(p);

}

read_unlock(&tasklist_lock);

return retval;

}

}

static inline pid_t process_group(struct task_struct *tsk)

{

return tsk->signal->pgrp;

}

task_struct中的signal中的pgrp成员标识了进程组ID。从pgrp的位置来看,进程组应该是和信号处理相关的,后续会专门的文档详细描述。

五、Session ID

和进程属于进程组类似,每个进程组都属于一个session,每个session有一个Leader进程,也就是创建session的那个进程,session leader的ID就等于该session的ID。Session概念的提出和用户登录以及终端编程相关,后续会专门的文档详细描述。

在POSIX标准中,系统定义了getsid函数来获取session leader进程的process group ID。如果指定的PID不是session leader,将返回错误,但是在linux kernel中没有完全遵守这个规定,getsid函数总是能返回指定PID的session ID,无论该PID指向的进程是否是session leader,具体定义如下:

asmlinkage long sys_getsid(pid_t pid)

{

if (!pid) {

//如果pid等于0,那么需要获取当前进程的进程组ID

return process_session(current);

}

else {

//否则,获取pid标识的那个进程对应的进程组ID

int retval;

struct task_struct *p;

read_lock(&tasklist_lock);

p = find_task_by_pid(pid);

retval = -ESRCH;

if (p) {

retval = security_task_getsid(p);

if (!retval)

retval = process_session(p);

}

read_unlock(&tasklist_lock);

return retval;

}

}

static inline pid_t process_session(struct task_struct *tsk)

{

return signal_session(tsk->signal);

}

static inline pid_t signal_session(struct signal_struct *sig)

{

return sig->__session;

}

task_struct中的signal中的__session成员标识了进程组ID。从__session的位置来看,session应该也是和信号处理相关的,后续会详细描述。

Linux进程概述的更多相关文章

  1. Linux进程控制(一)

    1. Linux进程概述 进程是一个程序一次执行的过程,它和程序有本质区别.程序是静态的,它是一些保存在磁盘上的指令的有序集合:而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建.调度 ...

  2. Linux进程实践(1) --Linux进程编程概述

    进程 VS. 程序 什么是程序? 程序是完成特定任务的一系列指令集合. 什么是进程? [1]从用户的角度来看:进程是程序的一次执行过程 [2]从操作系统的核心来看:进程是操作系统分配的内存.CPU时间 ...

  3. Linux进程调度器概述--Linux进程的管理与调度(十五)

    调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为两个不同的部分, 其中一个涉及调度策略, 另外一个涉及上下文切换. 1 背景知识 1.1 什么是调度器 ...

  4. linux进程编程:子进程创建及执行函数简介

    linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system();    下面分别做详细介绍.(1)fork()    函数定 ...

  5. Linux性能及调优指南(翻译)之Linux进程管理

    本文为IBM RedBook的Linux Performanceand Tuning Guidelines的1.1节的翻译原文地址:http://www.redbooks.ibm.com/redpap ...

  6. Linux进程优先级的处理--Linux进程的管理与调度(二十二)

    1. linux优先级的表示 1.1 优先级的内核表示 linux优先级概述 在用户空间通过nice命令设置进程的静态优先级, 这在内部会调用nice系统调用, 进程的nice值在-20~+19之间. ...

  7. Linux学习笔记之三————Linux命令概述

    一.引言 很多人可能在电视或电影中看到过类似的场景,黑客面对一个黑色的屏幕,上面飘着密密麻麻的字符,梆梆一顿敲,就完成了窃取资料的任务. Linux 刚出世时没有什么图形界面,所有的操作全靠命令完成, ...

  8. Linux进程ID号--Linux进程的管理与调度(三)【转】

    Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一. 该数据结 ...

  9. 《Linux 性能及调优指南》1.1 Linux进程管理

    https://blog.csdn.net/ljianhui/article/details/46718835 本文为IBM RedBook的Linux Performanceand Tuning G ...

随机推荐

  1. Math Number 数值类 包装类 数学计算 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. javascript+JQuery实现返回顶部功能

    很多网站上都有返回顶部的效果,本文阐述如何使用jquery实现返回顶部按钮. 首先需要在顶部添加如下html元素: <p id="back-to-top"><a ...

  3. 【HowTo ML】分类问题-&gt;神经网络入门

    非线性分类器(Non-linear hypotheses) 为什么使用非线性分类器 我们举几个栗子: 假如我们有一个数据空间如左上角坐标系所看到的,那么我们要的模型须要如右边公式所看到的的预測函数. ...

  4. 笔记本wifi热点设置好后,手机连上但不能上网问题

    这个问题我遇到过,我的原因是因为电脑上装有安全防护软件360的原因 解决方法是:打开360-->找到功能大全中的流量防火墙-->打开局域网防护-->关闭局域网隐身功能,立刻解决了这个 ...

  5. 在项目代码中载入cocostudio导出的动画并循环播放

    须要在代码中引入#include "cocostudio/CocoStudio.h" using namespace cocostudio; ArmatureDataManager ...

  6. 浅谈Java两种并发类型——计算密集型与IO密集型

    转载:https://blog.csdn.net/u013070853/article/details/49304099 核心是可以分别独立运行程序指令的计算单元.线程是操作系统能够进行运算调度的最小 ...

  7. Android 之布局(二)

    3.TableLayout(表格布局) 像表格一样布局,通常情况下,TableLayout有多个TableRow组成,每个TableRow就是一行. <?xml version="1. ...

  8. JSTL标签引入(web基础学习笔记十八)

    一.JSTL包下载和引入 1.0.简介 JSTL全名为JavaServer Pages Standard Tag Library 1.1.下载包 下载地址:http://archive.apache. ...

  9. oracl 、mysql在线查看文档

    Oracle .mysql在线开发文档: http://www.runoob.com/sql/sql-union.html

  10. Oracle 删除表中记录 如何释放表及表空间大小

    1.查看一个表所占的空间大小:SELECT bytes/1024/1024 ||'MB' TABLE_SIZE ,u.* FROM USER_SEGMENTS U WHERE U.SEGMENT_NA ...