一、定义和理解
狭义定义:进程是正在运行的程序的实例。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。
进程的概念主要有两点:
第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域和堆栈区域。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。
进程的属性
进程标志符:进程ID,内核分配,该标志符非负,范围0~32767
父进程和父进程ID(PPID)
启动进程的用户ID(UID)和所归属的组(GID);
进程的有效用户ID和有效组ID
进程的进程组ID:一个进程可以属于某一个进程组。
进程的会话ID:每一个进程都属于惟一的会话。
进程状态:状态分为运行R、休眠S、阻塞Z;
进程执行的优先级;
进程标志符:进程ID,内核分配,该标志符非负,范围0~32767
父进程和父进程ID(PPID)
启动进程的用户ID(UID)和所归属的组(GID);
进程的有效用户ID和有效组ID
进程的进程组ID:一个进程可以属于某一个进程组。
进程的会话ID:每一个进程都属于惟一的会话。
进程状态:状态分为运行R、休眠S、阻塞Z;
进程执行的优先级;
进程所连接的终端名;
进程资源占用:比如占用资源大小(内存、CPU占用量);
进程资源占用:比如占用资源大小(内存、CPU占用量);
二、进程的相关操作
(1)获取进程属性和更改属性
获取:
getpid(void) 获取当前进程ID
getppid(void) 获取当前进程的父进程ID
getpgrp(void) 获取当前进程的进程组ID
getpgid(pid_t pid) 获取进程组ID
getpid(void) 获取当前进程ID
getppid(void) 获取当前进程的父进程ID
getpgrp(void) 获取当前进程的进程组ID
getpgid(pid_t pid) 获取进程组ID
getuid(void) 获取当前进程的用户ID
geteuid(void) 获取当前进程的有效用户ID
getgid(void) 获取当前进程的用户组ID
getegid(void) 获取当前进程的有效用户组ID
getsid(pid_t pid) 获取当前进程的会话ID
geteuid(void) 获取当前进程的有效用户ID
getgid(void) 获取当前进程的用户组ID
getegid(void) 获取当前进程的有效用户组ID
getsid(pid_t pid) 获取当前进程的会话ID
设置:
int setpgid(pid_t pid,pid_t pgid)
setpgid()将参数pid 指定进程所属的组ID设为参数pgid 指定的组识别码。如果参数pid 为0,则会用来设置目前进程的组ID,如果参数pgid为0,则会以
目前进程的进程ID来取代。
int setpgrp(void)
setpgrp()将目前进程所属的组ID设为目前进程的进程ID。此函数相当于调用setpgid(0,0)。
pid_t setsid(void)
setpgid()将参数pid 指定进程所属的组ID设为参数pgid 指定的组识别码。如果参数pid 为0,则会用来设置目前进程的组ID,如果参数pgid为0,则会以
目前进程的进程ID来取代。
int setpgrp(void)
setpgrp()将目前进程所属的组ID设为目前进程的进程ID。此函数相当于调用setpgid(0,0)。
pid_t setsid(void)
setsid函数,调用该函数的进程将作为新会话的领导者创建一个新的会话,会话和调用进程的进程组ID,将被设置为调用进程的进程ID,并返回该进程ID
int setuid(uid_t uid) 设置进程的用户ID
int setreuid(uid_t ruid, uid_t euid) 将进程的实际用户ID设置为ruid,有效用户ID设置为euid
int seteuid(uid_t uid) 设置进程的有效用户ID为uid
int setgid(gid_t gid) 设置进程的组ID
int setregid(gid_t rgid, gid_t egid) 将进程的实际组ID设置为rgid,有效组ID设置为egid
int setegid(gid_t gid) 设置进程的有效组ID
int setuid(uid_t uid) 设置进程的用户ID
int setreuid(uid_t ruid, uid_t euid) 将进程的实际用户ID设置为ruid,有效用户ID设置为euid
int seteuid(uid_t uid) 设置进程的有效用户ID为uid
int setgid(gid_t gid) 设置进程的组ID
int setregid(gid_t rgid, gid_t egid) 将进程的实际组ID设置为rgid,有效组ID设置为egid
int setegid(gid_t gid) 设置进程的有效组ID
进程优先级: linux系统为多进程同时运行,Linux采用了时间片轮转的进程调度方式。进程的优先级定义了进程被调度的优先顺序,优先级的数值越低,其优先级就越高。
Linux用nice系统调用来修改进程的优先级,默认情况下,进程的优先级为0,系统允许的优先级的 范围为:-20~2
int nice(int inc)
nice()用来改变进程的进程执行优先顺序。参数inc数值越大则优先顺序排在越后面,即表示进程执行会越慢。 只有超级用户才能使用负的inc值,代表优先顺序排在前面,进程执行会较快。返回值 如果执行成功则返回0,否则返回-1,失败原因存于errno中。错误代码 EPERM 一般用户企图转用负的参数inc值改变进程优先顺序。 nice系统调用只能用于修改进程自身的优先级。
setpriority(设置程序进程执行优先权)
Linux用nice系统调用来修改进程的优先级,默认情况下,进程的优先级为0,系统允许的优先级的 范围为:-20~2
int nice(int inc)
nice()用来改变进程的进程执行优先顺序。参数inc数值越大则优先顺序排在越后面,即表示进程执行会越慢。 只有超级用户才能使用负的inc值,代表优先顺序排在前面,进程执行会较快。返回值 如果执行成功则返回0,否则返回-1,失败原因存于errno中。错误代码 EPERM 一般用户企图转用负的参数inc值改变进程优先顺序。 nice系统调用只能用于修改进程自身的优先级。
setpriority(设置程序进程执行优先权)
定义函数 int setpriority(int which,int who, int prio);
函数说明 setpriority()可用来设置进程、进程组和用户的进程执行优先权。参数which有三种数值,
函数说明 setpriority()可用来设置进程、进程组和用户的进程执行优先权。参数which有三种数值,
参数 who则依which值有不同定义
which who 代表的意义
PRIO_PROCESS who为进程ID
PRIO_PGRP who为进程的组ID
PRIO_USER who为用户ID
参数prio介于-20 至20 之间。代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。
此优先权默认是0,而只有超级用户(root)允许降低此值。返回值 执行成功则返回0,如果有错误发生返回值则为-1,错误原因存于errno。 ESRCH 参数which或who 可能有错,而找不到符合的进程EINVAL 参数which值错误。EPERM 权限不够,无法完成设置 EACCES
一般用户无法降低优先权
PRIO_PGRP who为进程的组ID
PRIO_USER who为用户ID
参数prio介于-20 至20 之间。代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。
此优先权默认是0,而只有超级用户(root)允许降低此值。返回值 执行成功则返回0,如果有错误发生返回值则为-1,错误原因存于errno。 ESRCH 参数which或who 可能有错,而找不到符合的进程EINVAL 参数which值错误。EPERM 权限不够,无法完成设置 EACCES
一般用户无法降低优先权
int getpriority(int which,int who);
函数说明 getpriority()可用来取得进程、进程组和用户的进程执行优先权。 参数 which有三种数值,参数who 则依which值有不同定义
which who 代表的意义
函数说明 getpriority()可用来取得进程、进程组和用户的进程执行优先权。 参数 which有三种数值,参数who 则依which值有不同定义
which who 代表的意义
PRIO_PROCESS who 为进程ID
PRIO_PGRP who 为进程的组ID
PRIO_USER who 为用户ID
此函数返回的数值介于-20 至20之间,代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。 返回值 返回进程执行优先权,如有错误发生返回值则为-1 且错误原因存于errno。附加说明 由于返回值有可能是-1,因此要同时检查errno是否存有错误原因。最好在调用次函数前先清除errno变量。错误代码 ESRCH 参数which或who 可能有错,而找不到符合的进程。EINVAL 参数which 值错误。
PRIO_PGRP who 为进程的组ID
PRIO_USER who 为用户ID
此函数返回的数值介于-20 至20之间,代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。 返回值 返回进程执行优先权,如有错误发生返回值则为-1 且错误原因存于errno。附加说明 由于返回值有可能是-1,因此要同时检查errno是否存有错误原因。最好在调用次函数前先清除errno变量。错误代码 ESRCH 参数which或who 可能有错,而找不到符合的进程。EINVAL 参数which 值错误。
(2)创建进程
1、int fork( void );
返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。
fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。
注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。
函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。
fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。
注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。
深入理解可以参见:linux中fork()函数详解http://os.chinaunix.net/a2012/0203/1306/000001306508.shtml
2、int vfork( void );
返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
vfork与fork大致相同,区别如下:
1) fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
2) fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制
1) fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
2) fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制
(3)exec函数族
在fork后的子进程中使用exec函数族,可以装入和运行其它程序(子进程替换原有进程,和父进程做不同的事)。fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被 exec 执行的进程的PID不会改变(和调用exec的进程的PID一样)。
(4)进程等待
1、pid_t wait (int * status);
函数说明:wait()函数的工作过程是:先判断子进程是否存在,即是否成功创建了一个子进程。如果创建失败,则会直接退出并提示相关错误信息,并返回-1;如果创建成功,wait()将父进程挂起,直到子进程结束,并返回子进程结束时的状态和PID。
函数说明:wait()函数的工作过程是:先判断子进程是否存在,即是否成功创建了一个子进程。如果创建失败,则会直接退出并提示相关错误信息,并返回-1;如果创建成功,wait()将父进程挂起,直到子进程结束,并返回子进程结束时的状态和PID。
如果有子进程,退出时的结束状态(status)有以下两种:
1)子进程正常结束:如调用exit(0)。
2)信号引起子进程结束:如调用kill(pid_t pid, int sig);
如果不在意结束状态值,则参数status 可以设成NULL。
如果不在意结束状态值,则参数status 可以设成NULL。
2、 pid_t waitpid(pid_t pid,int * status,int options);
返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。
函数说明: waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status 可以设成NULL。参数pid 为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid 的子进程。
参数option 可以为0 或下面的OR 组合:
WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。
WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。
返回值:如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno 中。
函数说明: waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status 可以设成NULL。参数pid 为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid 的子进程。
参数option 可以为0 或下面的OR 组合:
WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。
WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。
(5)进程通信
现在linux使用的进程间通信方式:
1、管道(pipe)和有名管道(FIFO)
1、管道(pipe)和有名管道(FIFO)
1)管道(pipe)的介绍
A.管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
B.只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
C.单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
D.数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
2)管道的创建
创建一个无名管道可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。
系统调用:pipe();
原型:int pipe(int fd[2]);
返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:
errno=EMFILE(没有空亲的文件描述符)
EMFILE(系统文件表已满)
EFAULT(fd数组无效)
注意:fd[0]用于读取管道,fd[1]用于写入管道。
3)有名管道的介绍
无名管道,由于没有名字,只能用于亲缘关系的进程间通信.。为了克服这个缺点,提出了有名管道(FIFO)。
FIFO不同于无名管道之处在于它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据。值的注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。
注意:有名管道的名字存在于文件系统中,内容存放在内存中
4)有名管道创建
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
返回:若成功则为0,若出错返回-1
一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close,read,write,unlink等)都可用于FIFO。当打开一个FIFO时,非阻塞标(O_NONBLOCK)产生下列影响:
A、在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。
B、如果指一了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。
FIFO相关出错信息:EACCES(无存取权限)EEXIST(指定文件不存在)
ENAMETOOLONG(路径名太长)
ENOENT(包含的目录不存在)
ENOSPC(文件系统余空间不足)
ENOTDIR(文件路径无效)
EROFS(指定的文件存在于只读文件系统中)
管道是基于文件描述符的通信方式。(所以会用到关于文件操作的一些函数)
可详细参见下面大牛的博客:
(5)进程退出
http://blog.csdn.net/muge0913/article/details/7317580
本文参考内容:
6、http://blog.csdn.net/muge0913/article/details/7317580