1、介绍linux
当linux系统中的一个进程运行起来的时候,老是要访问系统的资源,访问文件或者向其余的进程发送信号。系统是否容许其进行这些操做?系统是根据什么来判断该进程的权限?这些问题是和进程信任状(process credentials)相关。shell
process credentials包括一系列的ID,以下:编程
一、real user ID 和 real group ID网络
二、effective user ID 和 effective group IDsession
三、saved set-user-ID 和 saved set-group-ID数据结构
四、file-system user ID 和 file-system group ID多线程
五、supplementary group IDsapp
2、什么是real user ID 和 real group IDide
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是继承自其父进程。函数
3、什么是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。
4、什么是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,这里再也不赘述。
5、什么是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来攻击。解决这个问题有两个思路:
一、 不修改effective ID,引入file-system user ID(file-system group ID)
二、 修改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。
6、什么是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来进行进程是否有访问某些资源权限的断定。
1、概述
本文主要描述在linux kernel中如何标识一个或者一组和进程(线程)相关的实体,包括:
一、进程ID(线程组ID)
二、线程ID
三、进程组ID
四、Session ID
须要强调的是本文focus在identification,不少展开的内容会有一系列文档描述。
2、什么是进程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数据结构。
3、什么是线程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不该该假设这一点。
4、什么是进程组ID
每一个进程属于一个进程组,每一个进程组有一个Leader进程,也就是进程ID等于进程组ID的那个进程。进程组有生命周期,它的生命周期开始于进程组leader建立进程组,结束于进程组内的最后一个进程离开进程组(多是进程退出, 或加入其余进程组)。进程组概念的提出主要是因为:
一、和job control相关,关于job control,后续会专门的文档详细描述。
二、 Signal能够发送给进程组的每个进程(job control也使用这个特性。例如job control signal会被送给job(进程组)中的每个进程)
三、进程同步的时候,父进程能够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的位置来看,进程组应该是和信号处理相关的,后续会专门的文档详细描述。
5、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应该也是和信号处理相关的,后续会详细描述。