名词
-
进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
-
线程:是程序执行流的最小单元。调度
-
进程环境:
- 其main函数是如何调用;
- 命令行参数是如何传递给新程序的;
- 典型的存储空间布局是什么样式;
- 如何分配另外的存储空间;
- 进程如何使用环境变量;
- 进程的各种终止方式
-
进程控制
- 新建进程;
- 执行程序;
- 进程终止;
- 实际、有效和保存的用户ID和组ID的控制
方法摘要
函数 | 说明 | 成功 | 失败 |
---|---|---|---|
进程管理 | |||
main | C程序执行 | ||
fork | 创建一个新进程 | 子进程-->0,父进程-->子进程ID(>0) | -1 |
wait | 获取子进程的终止状态 | 进程ID | 0或-1 |
waitpid | 获取子进程的终止状态 | 进程ID | 0或-1 |
execl、execv、execle、execlp、execvp、fexecve | 启动新的进程 | ||
exit | 程序退出 | ||
_Exit | 程序退出 | ||
_exit | 程序退出 | ||
atexit | 终止处理程序注册 | 0 | 非0 |
内存分配 | |||
malloc | 存储空间分配 | 非空指针 | 非0 |
calloc | 存储空间分配 | 非空指针 | 非0 |
ralloc | 存储空间分配 | 非空指针 | 非0 |
free | 释放存储空间 | ||
环境变量 | |||
getenv | 获取环境值 | name关联的value的指针,若未找到,返回NULL | |
putenv | 设置变量 | 0 | 非0 |
setenv | 设置变量 | 0 | 非0 |
putenv | 设置变量 | 0 | -1 |
getrlimit | 获取资源限制 | 0 | 非0 |
setrlimit | 设置资源限制 | 0 | 非0 |
进程信息 | |||
getpid | 函数调用进程的进程ID | ||
getppid | 函数调用进程的父进程ID | ||
getuid | 函数调用进程的实际用户ID | ||
getgid | 函数调用进程的实际组户ID | ||
getegid | 函数调用进程的有效组户ID | ||
setuid | 设置用户 | 0 | -1 |
setgid | 设置组id | 0 | -1 |
setgid | 设置组id | 0 | -1 |
nice | 设置权限值 | nice=ZERO | -1 |
nice | 设置权限值 | nice=ZERO | -1 |
setpriority | 设置权限值 | 0 | -1 |
getpriority | 获取权限值 | 返回-NZERO~NZERO-1之间的值 | -1 |
times | 设置用户时间 | 返回流逝的墙上时钟时间 | -1 |
跳转 | |||
setjmp | 设置跳转点 | 直接调用:0;longjmp:非0 | |
longjmp | 跳转 |
名称解释
-
环境表 :每个程序接受一张环境表(环境都是静态数据)。
- 一个字符指针数组, 每个指针包涵一个以null结束的C字符串的地址。
- 环境变量字符串形式: name=value
- 启动函数
int main(int argc ,char *argv[] ,char *envp[])
'envp':数组指针
变量 | 说明 |
---|---|
COLUMNS | 终端宽度 |
DATEMASK | getdate(3)模版文件路径名 |
HOME | hone起始目录 |
LANG | 本地名 |
LC_ALL | 本地名 |
LC_COLLATE | 本地排序名 |
LC_CTYPE | 本地字符分类名 |
LC_MESSAGES | 本地消息名 |
LC_MONETARY | 本地货币编辑名 |
LC_NUMERIC | 本地数字编辑名 |
LC_TIME | 本地日期/时间格式名 |
LINES | 终端高度 |
LOGNAME | 登录名 |
MSGVERB | fmtmsg(3)处理的消息组成部分 |
NLSPATH | 消息类模块序列 |
PATH | 搜索可执行文件的路径前缀列表 |
PWD | 当前工作目录的绝对路径名 |
SHELL | 用户首选的shell名 |
TERM | 终端类型 |
TMPDIR | 在其中创建临时文件的目录路径名 |
TZ | 时区信息 |
- C程序的存储空间布局
- size: 查看正文段、数据段、bss段的长度
size /bin/sh
text data bss dec hex filename
902580 35984 22920 961484 eabcc /bin/sh
-
共享库:
- 使可执行文件中不再需要包含公用的库函数,只需要在所有进程都可引用的存储区中保存这种库例程的一个副本。
- 程序第一次执行或这第一次调用某个库函数时,用动态连接方法将程序共享库函数相链接。
- 减少了可执行文件的长度,增加了一些运行时时间开销。
- 可用库函数的新版本代替老版无需对该库的程序重新链接编辑。
-
进程标识:每个进程都有一个非负整数表示的唯一进程ID。
- PID=0:调度进程(交换进程)。
- PID=1:init进程。字自举过程结束时,由内核调用(/sbin/init)。读取与系统有关的初始化文件(/etc/rc*、/etc/inittab、/etc/init。d)。init进程决不会终止,是一个用户进程,以超级用户特权运行。是所有孤儿进程的父进程
-
进程终止:
- 从main返回
- 调用exit
- 调用_exit或_Exit
- 最后一个线程从其它启动历程返回
- 最后一个线程调用 pthread_exit
-
进程异常终止:
- 调用abort;
- 接受到一个信号
- 最后一个线程对取消请求做响应
-
父进程先终止 :
- 子进程的父进程都改为init进程。一个进程终止时,内核逐个检测所有活动进程,判断是否有终止进程的子进程。如果有,则子进程的父进程ID更改为1(init进程ID)。
- init进程只要有一个进程终止,init就会调用一个wait函数取得其终止状态。
-
竞争条件(race condition) :
- 为了避免竞争条件和轮询,在多个进程之间需要有某种形式的信号发送和接受方法:uninx信号机制,进程间通信 (IPC)
-
登录 :
- 终端登录:/etc/ttys
函数
main : C程序开始执行
int main(int argc, char *argv[]);
-
参数:
- argc : 命令行参数的数目
- argv : 参数各个指针所构成的数组
-
特点:
-
在调用main前线调用一个特殊的启动例程,可执行程序文件将 启动例程 指定为程序的起始地址--这是由编译器设置的,而连接编辑器由C编译器调用。启动例程从内核取得命令行参数和环境变量值,然后为按上述方式调用启动main函数做好安排。
-
命令行参数。
- argv[0] :可执行文件名
- argv[1] :第一个参数
- argv[2] :第二个参数
-
exit、_Exit、_exit :退出
#include <stdlib.h> 'ISO C'
void exit(int status);
void _Exit(int status);
#include <unistd.h> 'POSIX'
void _exit(it status);
//用法
exit(0); = return(0);
-
参数:
- status : 终止状态
-
特点:
- 将退出状态(exit status)作为参数传送给函数。
- 父进程先终止
atexit :终止处理程序
#include <stdlib.h>
int atexit(void (*func)(void));
-- '成功:0;出错:非0'
-
参数:
- (*func) :函数地址
-
特点:
- 终止处理程序的顺序与它们登记(注册)时的顺序相反。
- 登记多次,调用多次。
存储空间分配
#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj,size_t size);
void *ralloc(size_t *ptr, size_t newsize);
-- '成功:非空指针;出错:NULL'
void *free(void *ptr);
-
特点:
- malloc:分配指定字节数的存储区。
- calloc:分配指定长度的对象存储空间存储空间,每个bit都初始化为0。
- ralloc:增加或减少分配区的长度。
- free:释放ptr指向的存储空间。
- 大多数实现分配的存储空间比所要求的要稍大,记录管理信息:分配块的长度、指向下一个分配块的指针。
环境变量:getenv、putenv、setenv、unsetenv
#incLude <stdlib.h>
char *getenv(const char *name);
--'指向与name关联的value的指针;若未找到,返回NULL'
-
特点:
- ISC O没有定义任何环境变量。
#include <stdlib.h>
int putenv(char *str);
-- '成功:0;失败:非0’'
int setenv(const char *name, const char *value ,int rewrite);
int putenv(const char *str);
-- '成功:0;出错:-1'
-
参数:
- putenv的str: name=value,删除已存在。
- setenv的rewrite:
- rewrite=非0 :删除已存在的。
- rewrite=0 :不操作。
- str: name=value
-
特点
- value :自动通过malloc 变更。
setjmp、longjmp:跳转
#inlude <setjmp.h>
int setjmp(jmp_buf env);
--'直接调用:0;longjmp:非0'
int longjmp(jmp_buf env, int val)
-
参数:
-
setjmp:
- jmp_buf:存放在调用longjmp时能用来恢复栈状态的所有信息。因为需要另一个函数中引用env变量,通常将env变量定义为全局变量。
-
longjmp:
- val:将成为从setjmp处返回的值。
-
getrlimit、setrlimit:资源限制(一般都是大小)
#include <sys/resources.h>
int getrlimit(int resource, struct rlimit *rlpth);
int setrlimit(int resource, const struct rlimit *rlpth);
-- '成功:0;出错:非0'
//rlimit 结构
struct rlimit {
rlim_t rlim_cur; /*软限制:现在真正使用的限制*/
rlim_t rlim_max; /*硬现在:rlim_cur的最大值*/
}
-
参数:
- resouce :
现在 | 说明 | 超过影响 |
---|---|---|
RLIMIT_AS | 进程总得可用存储空间的最大长度(字节) | 影响到sbrk、mmap函数 |
RLIMIT_CORE | core文件的最大字节数 | 若其值为0则阻止创建core文件。 |
RLIMIT_CPU | CPU时间的最大值(秒) | 当超过次软限制时,向进程发送SIGXCPU信号 |
RLIMIT_DATA | 数据端的最大字节长度。data+bss | |
RLIMIT_FSIZE | 创建文件的最大字节长度 | 超过此软限制,则向该进程发送SIGXFSZ信号 |
RLIMIT_MEMLOCK | 一个进程使用mlock(2)能够锁定在存储空间中的最大字节长度 | |
RLIMIT_MSGQUEUE | 进程为POSIX消息队列分配的最大存储字节 | |
RLIMIT_NOFILE | 打开的最多文件数 | 影响sysconf函数在参数_SC_OPEN_MAX中的返回值 |
RLIMIT_NPROC | 最大子进程数 | 影响sysconf函数在参数_SC_CHILD_MAX中的返回值 |
RLIMIT_NPTS | 伪终端的最大数量 | |
RLIMIT_RSS | 最大驻内存集字节长度(resident set size in bytes,RSS)。 | |
RLIMIT_SBSIZE | 在任一给定时刻,一个用户可占用的套接字缓冲区的最大长度 | |
RLIMIT_SIGPENDINGE | 一个进程可排队的信号最大数量 | 影响sigqueue函数 |
RLIMIT_STACK | 栈的最大字节数 | |
RLIMIT_SWAP | 用户可消耗的交换空间的最大字节数 | |
RLIMIT_VMEM | RLIMIT_AS |
进程信息:
#include <unistd.h>
pid_t getpid(void);
-- '函数调用进程的进程ID'
pid_t getppid(void);
-- '函数调用进程的父进程ID'
uid_t getuid(void);
-- '函数调用进程的实际用户ID'
uid_t geteuid(void);
-- '函数调用进程的有效用户ID'
uid_t getgid(void);
-- '函数调用进程的实际组户ID'
uid_t getegid(void);
-- '函数调用进程的有效组户ID'
-
特点:
- 实际用户ID:
- 有效用户ID:
fork : 创建一个新进程
#inlcude <unistd.h>
pid_t fork(void);
-- '成功:子进程-->0,父进程-->子进程ID(>0);出错:-1'
-
特点:
- 执行成功:子进程-->0,父进程-->子进程ID(>0)
- 父子进程共享正文段(.text).
- 子进程单独获得(创建) 父进程很多信息副本:
- 数据空间
- 堆和栈
- 文件描述符
- 实际用户ID,实际组ID,有效用户ID,有效组ID
- 附属组ID
- 进程组ID
- 会话ID
- 控制终端
- 设置用户ID标志和设置组ID标志
- 当前工作目录
- 跟目录
- 文件模式创建屏蔽字
- 信号屏蔽和安排
- 对任一打开文件描述符的执行时关闭(close-on-exec)标志
- 环境
- 存储映像
- 资源限制
- 父子进程的区别:
- fork的返回值不同
- 进程ID不同
- 子进程的 tms_utime、tms_stime、tms_cutime和tms_ustime的值设置为0
- 子进程不继承父进程设置的文件锁。
- 子进程的为处理闹钟被清除
- 子进程的未处理信号集设置为空集
- fork之后经常跟随exec。很多实现并不执行一个父进程数据段、栈和堆的完全副本,作为代替使用了写时复制(Copy-On-Write,COW)
- 写时复制:将程数据段、栈和堆由父子进程共享,内核将它们的访问权限改变为只读。如果父子进程修改这些区域,内核只为修改区域的那块内存制作一个副本(虚拟存储的以“页”)
- 用法:
- 一个父进程希望复制自己,使父子进程同时执行不同的代码。网络服务器进程很常见--父进程等待客户的端的服务请求。当这种请求到达,父进程调用fork,是子进程处理次请求。
- 一个进程要执行一个不同的成(shell的常见请求)。子进程fork返回后立即调用exec。
vfork:复制一个新进程
-
特点:
- 子进程立即exec一个新的进程,vfork不会复制子进程的地址空间。(shell的基本操作)
wait、waitpid:获取子进程的终止状态
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);
--'成功:进程ID;出错:0或-1'
-
参数:
- waitpid:
参数 | 说明 |
---|---|
pid | |
pid-1 | 等待任一子进程。这种情况下:waitpid与wait等效 |
pid>0 | 等待进程ID与pid相等的子进程 |
pid0 | 等待组ID等于调用进程组ID的任一子进程 |
pid<-1 | 等待组ID等于pid绝对值的任一子进程 |
options | |
WCONTINUED | 若实现支持作业控制,那么pid指定的任一子进程在停止后又继续,但其状态尚未报告,则返回状态状态 |
WNOHANG | pid指定的子进程并不是立即可用,则waitpid不阻塞,次数返回值为0 |
WUNTRACED | 若实现支持作业控制,那么pid指定的任一子进程在停止后,但其状态尚未报告,则返回状态状态 |
-
特点:
- 所有子进程都在运行,则阻塞。
- 子进程终止,立即返回子进程的终止状态。
- 没有任何子进程,则立即出错返回。
- waitpid:有选项,调用者不阻塞。
waitid : 返回终止状态
#include <sys/wait.h>
int waitid(idtype_t idtype,id_t id, siginfo_t *infop, int options);
-- '成功:0;出错:-1'
-
特点:
- Single UNIX specification 标准。
exec:创建新的子进程
#include <unistd.h>
int execl(const char *pathname, const char *arg0,.../* (char *)0 */);
int execv(const char *pathname, const char *argv[]);
int execle(const char *pathname, const char *arg0,.../* (char *)0, char *const envp[] */);
int execve(const char *pathname, const char *argv[], char *const envp[] );
int execlp(const char *pathname, const char *arg0,.../* (char *)0 */);
int execvp(const char *pathname, const char *argv[]);
int fexecve(int fd, const char *pathname, const char *argv[]);
- 特点
- 子进程单独获得(创建) 父进程很多信息副本:
- 进程ID和父进程ID
- 实际用户ID,实际组ID
- 附属组ID
- 进程组ID
- 会话ID
- 控制终端
- 闹钟尚余留的时间
- 当前工作目录
- 根目录
- 文件模式创建屏蔽字
- 信号屏蔽和安排
- 未处理信号
- 资源限制
- nice值
- tms_utime、tms_stime、tms_cutime以及tms_cstime值
- 子进程单独获得(创建) 父进程很多信息副本:
setuid、setgid:设置用户id和组id
#include <unistd.h>
int setuid(uid_t uid);
int setgid(git_t gid);
-- '成功:0;出错:-1'
system函数:执行脚本命令
#include <stdlib.h>
int system(const char *cmdstring);
getlogin:用户标识
#include <unistd.h>
char *getlogin(void);
--'成功:返回指向登录名字符串的指针;出错:NULL'
nice、getproiorty、setpriority:权限值
#include <unistd.h>
int nice(int incr);
--'成功:nice=ZERO;出错:-1'
- 参数:
- incr :
- 最大最小值不能超过系统给定范围。输入超过,则直接设置最大最小值。
- 出错:errno!=0
- incr :
#include <sys/resource.h>
int setpriority(int which,id_t who, int value);
-- '成功:0;出错:-1'
int getpriority(int which,id_t who);
-- '成功:返回-NZERO~NZERO-1之间的值;出错:-1'
-
参数:
- which:
- PRIO_PROCESS:表示进程。
- PRIO_PGRP:表示进程组。
- PRIO_USER: 表示用户ID。
- which:
进程时间
#include <sys/times.h>
clock_t times(struct tms *buf);
--'成功:返回流逝的墙上时钟时间;出错:-1'
//tms结构
struct tms {
clock_t tms_utime; '用户的CPU时间'
clock_t tms_stime; '系统的CPU时间'
clock_t tms_cutime; '终止的子进程用户的CPU时间'
clock_t tms_cstime; '终止的子进程用户的CPU时间'
}