版权归原作者所有,如有侵权,请联系我们

[科普中国]-网络进程标识符

科学百科
原创
科学百科为用户提供权威科普内容,打造知识科普阵地
收藏

概述

系统给每个进程定义了一个唯一标识该进程的非负正数,称作进程标识符。进程标识符可以简单的表示为主进程表中的一个索引。当某一进程终止后,其标识符可以重新用作另一进程的标识符。不过,在任何时刻,一个标识符所代表的进程是唯一的。系统把标识符 0 和 1 保留给系统的两个重要进程。进程 0 是调度进程,它按一定的原则把处理机分配给进程使用。进程 1 是初始化进程,它是程序/sbin/init 的执行。进程 1 是 UNIX 系统那其它进程的祖先,并且是进程结构的最终控制者1。

进程标识符进程标识符属性每个进程都有6个重要的ID:进程ID、父进程ID、有效用户ID、有效组ID、实际用户ID和实际组ID。这6个标识符保存在进程的PCB中。 另外因为有时需要用到这些ID,系统提供了获取这些ID的函数1。

#include #include pid_t getpid(void); //返回值:调⽤进程的进程IDpid_t getppid(void); //返回值:调⽤进程的⽗进程IDuid_t getuid(void); //返回值:调⽤进程的实际⽤户IDuid_t geteuid(void); //返回值:调⽤进程的有效⽤户IDgid_t getgid(void); //返回值:调⽤进程的实际组IDgid_t getegid(void); //返回值:调⽤进程的有效组ID若成功执行,返回相应ID,执行失败则返回-1;
另外这里面进程ID和父进程ID不能更改,其他都可以更改。

进程的有效标识符实际⽤户ID和实际⽤户组ID:每个进程都有一个实际用户标识符和一个实际组标识符,它们永远是启动该进程之用户的用户标识符和组标识符。也就是登录用户的uid和gid,比如我的Linux以lazyboy登录,在Linux运行的所有命令的实际用户ID都是lazyboy的uid,实际用户组ID都是lazyboy的gid。(可以用id命令查)

有效⽤户ID和有效⽤户组ID:进程的有效用户标识符和有效组标识符也许更重要些,它们被用来确定一个用户能否访问某个确定的文件(该进程的访问权限)。在通常情况下,它们与实际用户标识符和实际组标识符是一致的。但是,一个进程或其祖先进程可以设置程序文件的置用户标识符权限或置组标识符权限。这样,当通过 exec 调用执行该程序时,其进程的有效用户标识符就取自该文件的文件主的有效用户标识符,而不是启动该进程的用户的有效用户标识符1。
另外,还有两个系统调用可以用来设置进程的有效用户标识符和有效组标识符,它们的使用格式如下:

#include #include uid newuid;pid newgid;int status;/* 设定进程的有效用户标识符 */status=setuid(newuid);/* 设定进程的有效组标识符 */status=getgid(newgid);如果你不是超级用户,那么你只能把进程的有效用户标识符和有效组标识符设置成实际用户和实际组标识符。超级用户的话就可以随意的设置有限标识符。

Linux进程标识符每个进程都有一个实际用户标识符和一个实际组标识符,它们永远是启动该进程之用户的用户标识符和组标识符。

进程的有效用户标识符和有效组标识符也许更重要些,它们被用来确定一个用户能否访问某个确定的文件。在通常情况下,它们与实际用户标识符和实际组标识符是一致的1。

有几个系统调用可以用来得到进程的用户标识符和组标识符,详见下列程序2:

/* 取进程的实际用户标识符 */ uid=getuid(); /* 取进程的有效用户标识符 */ euid=geteuid(); /* 取进程的实际组标识符 */ gid=getgid(); /* 取进程的有效组标识符 */ egid=getegid();另外,还有两个系统调用可以用来设置进程的有效用户标识符和有效组标识符,它们的使用格式如下:

/* 设定进程的有效用户标识符 */ status=setuid(newuid); /* 设定进程的有效组标识符 */ status=setgid(newid)通过这两个系统调用,进程可以改变自己的标识符,进而改变自己的权限(因为Linux中权限是通过标识符来判断的)。比如一个root 建立的进程可以用这种方法放弃一部分的root 权限而只保留工作所需的权限。这样可以提高系统的安全性。程序如下2:

#include #include #include #include #include int main(int argc,char* argv[]) { int fd; fd = fork(); switch(fd){ case -1:{ perror("fork"); exit(-1); } break; case 0: { //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置用户id为1000,改变用户权限 setuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //root用户才能查看shadow文件,测试权限是否改变 execlp("cat","cat","/etc/shadow",NULL); } break; default: { //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //root用户才能查看shadow文件 execlp("cat","cat","/etc/shadow",NULL); } break; } return 0; } 在root权限下运行结果:

[root@embedclub file]# ./a.out uid=0,euid=0 uid=1000,euid=1000 cat: /etc/shadow: 权限不够 uid=0,euid=0 root:$$MG0tTs8yWCPoKMrG$tVrZQKQ6IK52ucaSGfHIMKdVHB7zP.rpqD5GO/1w07eYQj0Jgue9S/UijUtyYYQa9Irm2vwj7r.DwaY.5IXIp0:15195:0:99999:7::: bin:*:14789:0:99999:7:::需要注意的是,一旦root 进程通过这种方式放弃了root 特权,将无法再通过setuid()调用的方式重新获得root权,因为一个非root 标识符的进程是无法设定root 标识符的。这时可以使用Linux的另外两个系统调用seteuid()和setegid()。其调用方式和前两个完全相同。但是它们是根据进程程序文件的标识符来判断设定的。因此,一个root 的程序文件在任何时候都可以将自己重新seteuid()为root1。

root下运行结果:

[root@embedclub file]# ./a.out uid=0,euid=0 uid=1000,euid=1000 setuid to root: Operation not permitted以上例子说明不是超级用户所引用的进程,只能把它的有效用户表示符和有效组标识符重新设置成其实际用户标识符和实际组标识符。超级用户所引用的进程就可以自由进行其有效用户标识符和有效组标识符的设置。

以上使用setuid()永久改变权限,现在使用seteuid()暂时改变用户权限2:

#include #include #include #include #include int main(int argc,char* argv[]){ //开始进程用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置有效用户id为1000,暂时改变用户权限 seteuid(1000); //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); //设置有效用户id为0,改变用户权限为自己 if(-1 == setuid(0)){ perror("setuid to root"); exit(-1); } //改变后的用户id和有效用户id printf("uid=%d,euid=%d\n",getuid(),geteuid()); return 0; } 运行结果:

#./a.outuid=0,euid=0uid=0,euid=1000uid=0,euid=0 [root@embedclub file]# ./a.out uid=0,euid=0 uid=0,euid=1000 uid=0,euid=0