linux系统编程之进程(一):进程基本概述

一、什么是进程

从用户的角度来看进程是程序的一次执行过程。 从操作系统的核心来看,进程是操作系统分配的内存、CPU时间片等资源的基本单位。 进程是资源分配的最小单位。 每一个进程都有自己独立的地址空间与执行状态。 像UNIX这样的多任务操作系统能够让许多程序同时运行,每一个运行着的程序就构成了一个进程。

二、进程数据结构

进程的静态描述:由三部分组成:PCB、有关程序段和该程序段对其进行操作的数据结构集。 进程控制块:用于描述进程情况及控制进程运行所需的全部信息。 代码段:是进程中能被进程调度程序在CPU上执行的程序代码段。 数据段:一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行后产生的中间或最终数据

堆栈段:程序运行时需要在这里做数据运算,存储临时数据,开辟函数栈等。在Linux下,栈是高地址往低地址增长的。

三、进程与程序

进程是动态的,程序是静态的。 进程的生命周期是相对短暂的,而程序是永久的。 进程数据结构PCB。 一个进程只能对应一个程序,一个程序可以对应多个进程。

四、进程状态变迁

运行状态(TASK_RUNNING) 可中断睡眠状态(TASK_INTERRUPTIBLE) 不可中断睡眠状态(TASK_UNINTERRUPTIBLE) 暂停状态(TASK_STOPPED) 僵死状态(TASK_ZOMBIE)

In computer operating systems terminology, a sleeping process can either be interruptible (woken via signals) or uninterruptible (woken explicitly). An uninterruptible sleep state is a sleep state that cannot handle a signal (such as waiting for disk or network IO (input/output)).  When the process is sleeping uninterruptibly, the signal will be noticed when the process returns from the system call or trap.  A process which ends up in “D” state for any measurable length of time is trapped in the midst of a system call (usually an I/O operation on a device — thus the initial in the ps output).  Such a process cannot be killed — it would risk leaving the kernel in an inconsistent state, leading to a panic. In general you can consider this to be a bug in the device driver that the process is accessing.

五、进程控制块

进程描述信息

进程标识符用于唯一的标识一个进程。

进程控制信息

进程当前状态 进程优先级 程序开始地址 各种计时信息 通信信息

资源信息

占用内存大小及管理用数据结构指针 交换区相关信息 I/O设备号、缓冲、设备相关的数结构 文件系统相关指针

现场保护信息

寄存器 PC 程序状态字PSW 栈指针

六、进程标识

每个进程都会分配到一个独一无二的数字编号,我们称之为“进程标识”(process identifier),或者就直接叫它PID. 是一个正整数,取值范围从2到32768(2.6内核以下) 当一个进程被启动时,它会顺序挑选下一个未使用的编号数字做为自己的PID 数字1一般为特殊进程init保留的

七、进程的创建

不同的操作系统所提供的进程创建原语的名称和格式不尽相同,但执行创建进程原语后,操作系统所做的工作却大致相同,都包括以下几点: 给新创建的进程分配一个内部标识,在内核中建立进程结构。 复制父进程的环境 为进程分配资源, 包括进程映像所需要的所有元素(程序、数据、用户栈等), 复制父进程地址空间的内容到该进程地址空间中。 置该进程的状态为就绪,插入就绪队列。

八、进程的销毁

进程终止时操作系统做以下工作: 关闭软中断:因为进程即将终止而不再处理任何信号; 回收资源:释放进程分配的所有资源,如关闭所有已打开文件,释放进程相应的数据结构等; 写记帐信息:将进程在运行过程中所产生的记帐数据(其中包括进程运行时的各种统计信息)记录到一个全局记帐文件中; 置该进程为僵死状态:向父进程发送子进程死的信号(SIGCHLD),将终止信息status送到指定的存储单元中; 转进程调度:因为此时CPU已经被释放,需要由进程调度进行CPU再分配。

九、对于一个进程来说,相关联的ID有下面几个:

ID

作用

实际用户ID

实际上我们是谁

实际组ID

有效用户ID

以什么权限运行

有效组ID

保存的设置用户ID

由exec函数保存

保存的设置组ID

#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
//r for real,e for effective
int setreuid(uid_t ruid,uid_t euid);
int setregid(gid_t rgid,gid_t egid);
int seteuid(uid_t uid);
int setegid(gid_t gid);

关于保存的设置ID判断条件是_POSIX_SAVED_IDS/_SC_SAVED_IDS.

通常来说有效uid和gid等同于实际uid和gid. 但是对于一些特殊程序比如需要修改passwd,那么程序执行时必须以另外一种用户权限启动,所以区分了这两个概念。比如我们调用passwd修改密码,ruid和rgid是我们自己,而euid和egid则是root(/etc/passwd属主是root).为了查看文件是否设置了SUID和SGID,我们可以使用S_ISUID和S_ISGID & st_mode。

这里有必要说说保存设置用户ID的作用(保存设置组ID同)。假设我们编写一个程序aaa, 运行者是simba, 然后aaa的owner是root并且设置了SUID特殊权限位。 当我们exec这个aaa程序的话,我们ruid=simba, euid=root. 那么如果进行 seteuid(simba) 操作的话, 修改有效用户id为simba是允许的,因为ruid就是simba. 这样ruid=simba,euid=simba. 这样就造成了一个问题,如果我们此时想seteuid(root), 系统如何验证呢? 系统不可能再去读取一次文件系统,所以要求内核本身就保存一个设置用户id.可以知道设置用户id 通常保存的内容就是第一次exec文件使用的euid.

对于setuid(uid)行为是这样的:

  • 如果是超级用户进程的话,那么ruid=uid,euid=uid,saved_id=uid.
  • 如果不是超级用户进程的话,如果uid==ruid或者是saved_id的话,那么euid=uid.
  • 出错那么返回-1并且errno=EPERM.

id

exec但是SUID关闭

exec而且SUID打开

超级用户进程调用setuid(uid)

非特权用户调用setuid(uid)

ruid

不变

不变

uid

不变

euid

不变

文件owner uid

uid

uid

saved_id

euid

euid

uid

不变

对于setreuid不是很了解,对于seteuid来说和setuid差别不大,只不过超级用户进程调用seteuid(uid)只是修改euid=uid.

十、终止进程的5种方法

从main函数return返回 调用exit(C库函数) 调用_exit(系统调用) 调用abort(产生SIGABRT信号,异常终止) 由信号终止(如ctrl+c 产生的 SIGINT信号)

需要注意的是main函数返回会调用exit;exit会调用_exit; exit会调用fflush,但_exit不会调用fflush。

atexit可以注册终止处理程序,ANSI C规定最多可以注册32个终止处理程序。终止处理程序的调用与注册次序相反

int atexit(void (*function)(void));

参考:《APUE》

http://dirlt.com/#sec-1

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏更流畅、简洁的软件开发方式

我的数据访问函数库的源代码(一)—— 共用部分

/* 2008 4 25 更新 */ 我的数据访问函数库的源码。整个类有1400多行,原先就是分开来写的,现在更新后还是分开来发一下吧。 第一部分:内部成员...

1729
来自专栏北京马哥教育

Linux磁盘监控工具说明

1143
来自专栏沃趣科技

mysqldump与innobackupex备份过程你知多少(三)

相关阅读: mysqldump与innobackupex备份过程你知多少(二) mysqldump与innobackupex备份过程你知多少(一) mysqld...

43013
来自专栏从ORACLE起航,领略精彩的IT技术。

Oracle RAC环境下定位并杀掉最终阻塞的会话

3236
来自专栏大内老A

WCF 技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[下篇]

[续《上篇》]TransactionFlow选项通过TransactionFlowAttribute这个操作契约写入绑定上下文,由事务绑定创建的事务信道获取该选...

1886
来自专栏散尽浮华

定时备份脚本分享(网站数据和数据库数据)

备份是我们运维人员最基本的日常工作,做好备份是稳定运维的一个重要环节。下面分享两个使用过的简单备份脚本: 1)网站数据备份 将网站数据/var/www/vhos...

3258
来自专栏WindCoder

《Linux内核分析》之分析fork函数对应的系统调用处理过程

xref: /linux-3.18.6/include/linux/sched.h

471
来自专栏三丰SanFeng

Linux进程间通信(四) - 共享内存

共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用...

2075
来自专栏散尽浮华

mysql数据库误删除后的数据恢复操作说明

在日常运维工作中,对于mysql数据库的备份是至关重要的!数据库对于网站的重要性使得我们对mysql数据的管理不容有失! 然后,是人总难免会犯错误,说不定哪天大...

24711
来自专栏皮振伟的专栏

​[linux][process]进程crash类问题处理方法

前言: 进程crash一般比较讨厌,尤其是segmentation fault,所谓的“踩内存”,是最讨厌的。 分析: 1,status 进程的状态,一般使...

2928

扫码关注云+社区