日期:2014-05-16  浏览次数:20777 次

进程(二)Linux下进程具体属性

进程数据结构(进程描述符)

直接查看下源码(这里是0.11版本的内核)中的文件/include/linux/sched.h,Linux的进程控制块为一个由结构task_struct所定义的数据结构,这个结构就在上面的sched.h中。这个文件中有一行代码:
extern struct task_struct *task[NR_TASKS];
为记录指向各PCB的指针,指针数组定义于/kernel/sched.c中,原定义为
 struct task_struct * task[NR_TASKS] = {&(init_task.task), };
NR_TASKS为最多可以同时运行的进程的数目,在sched.h中定义(#define NR_TASKS 64)。
task_struct结构的内容如下(为了写注释有改动,像unsigned long start_code,end_code,end_data,brk,start_stack;会展开成多行写):

struct task_struct {
/* these are hardcoded - don't touch */
        long state;     /* -1 unrunnable, 0 runnable(就绪), >0 stopped */
        long counter;   //任务运行时间片
        long priority;  
        long signal;    //信号。每个比特代表一种信号。
        struct sigaction sigaction[32];
        long blocked;   /* bitmap of masked signals 进程信号屏蔽码*/
/* various fields */
        int exit_code;  //退出码,父进程会取得
         unsigned long start_code;  //代码段地址
        unsigned long end_code;    //代码长度(字节数)
        unsigned long end_data;    //代码长度+数据长度(字节数)
        unsigned long brk;    //总长度
        unsigned long start_stack; //堆栈段地址
        long pid,father,pgrp,session,leader; //进程、父进程、进程组、会话、会话首进程号
        unsigned short uid,euid,suid;  //用户id、有效用户id、保存的用户id
        unsigned short gid,egid,sgid;  //组id、有效组id,保存的组id
        long alarm;                    //闹钟定时
        long utime,stime,cutime,cstime,start_time;  //用户态(系统态)运行时间、子进程用户态(系统太)
        unsigned short used_math;      //是否使用了协处理器
/* file system info */
        int tty;                //进程使用tty的子设备号,-1标识没有使用
        unsigned short umask;   //文件创建屏蔽字
        struct m_inode * pwd;   //当前工作目录i节点结构
        struct m_inode * root;  //根节点i节点结构
        struct m_inode * executable;  //执行文件i节点结构
        unsigned long close_on_exec;  //执行时关闭文件句柄位图标志
        struct file * filp[NR_OPEN];  //进程使用的文件表结构
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
        struct desc_struct ldt[3];    //本任务的局部表描述符。0-空,1-代码段cs,2-数据段和堆栈段ds&ss
/* tss for this task */
        struct tss_struct tss;        //进程的任务状态段信息结构
};

 

进程部分属性说明

1.进程ID和父进程ID
2.实际用户ID、实际组ID、有效用户ID、有效组ID、保存的设置用户ID、保存的设置组ID、附加组、文件模式创建屏蔽字
3.进程组ID、会话组ID、控制终端
4.tms_utime、tms_stime、tms_cutime、tms_ustime
5.信号屏蔽字、信号集、闹钟
6.文件锁(记录锁)
7.环境
8.运行状态

一、进程ID
每个进程都是有ID的,线程也有,但是系统中的进程ID号是有限的,而且可能同时运行的进程的数目也是有限制的,所以进程ID号也是一种资源。僵尸是进程在进程结束后父进程没有及时的回收终止子进程的有关信息情况下产生的,这些僵尸进程会占用进程ID。

 

二、实际用户ID、实际组ID、有效用户ID、有效组ID、保存的设置用户ID、保存的设置组ID、附加组ID
这里分三个问题来讲:

1.权限问题一直是很复杂的。先简单介绍下几个概念:
1)实际用户ID和实际组ID
这两个ID标识我们究竟是谁,也就是以什么用户登录的,在一个登录会话间这些值并不改变。
2)有效用户ID和有效组ID
这两个ID决定了我们文件的访问权限,下面会具体说明这个ID的作用
3)保存的设置用户ID和保存的设置组ID
其实为有效用户ID和有效组ID的副本
4)文件的访问权限
文件的访问权限分为三类,所有者,组,其他,每类都有读、写、可执行三项。
可以用 setuid() setgid() 来设置实际用户(组)和有效用户(组)ID
可以用 chmod()改变文件的权限,chown改变文件的所有者和所有组

2.进程每次打开、创建或者删除一个文件的时候,内核就进行文件访问权限测试,测试分四步:
1)若进程的有效用户ID是0(root),则允许访问。
2)若进程的有效用户ID等于文件的所有者ID(也就是进程拥有此文件),:那么若所有者适当的访问权限被设置,则允许访问,否则拒绝访问。
3)若进程的有效组或进程的附加组ID之一等于文件的组ID,那么若所有者适当的访问权限被设置,则允许访问,否则拒绝访问。
4)若其他用户适当的访问权限被设置,则允许访问,否则拒绝
按照顺序执行四步,如果进程拥有此文件(2),则按照用户访问权限批注或拒绝该进程对文件的访问,不查看组访问权限。下面也类似,也就是说并不是一定四步都执行完,可能从第二步开始就break了。

3.关于文件模式创建屏蔽字(umask)
每个进程创建一个新的文件或者目录的时候,都需要一个文件模式创建屏蔽字,这个屏蔽字是默认的创建的文件或者目录的权限。系统的大多数用户并不处理,通常在登录的时候由shell的启动文件设置一次,然后从不改变。可用umask命令直接查看,也可以直接用umask 777直接更改。注意,这里是屏蔽(u意义),如果设置成777,那么一个目录的权限为d---------。

一般而言,在设计程序时候,我们总是试图使用最小特权(least privilege)模型。依照此模型,我们的程序应该具有完成给定任务所需的最小特权,这减少了安全性受到损害的可能性。

 

三、进程组ID、会话组ID、控制终端
在网上搜了下,好多也都是apue上的概念,我理解,进程组,会话组的概念出现是为了linux方便管理的,主要是为了作业控制的。最常见的就是在终端上发送一个中断信号(crtl+c),这个中断信号会发送到控制终端的前台进程组。简单说,进程组是一个或者多个进程的集合,而会话(session)是一个或者多个进程组的集合。理解这两个概念有以下几点:

1)进程组中