进程与程序:
程序就是存储在磁盘上包含可执行指令、数据的静态实体。
进程就被操作系统读取加载到内存中的正在执行的程序。
进程的分类:
守护进程:由系统在开机时通过启动脚本激活的,总处理活动状态,一般运行在后台维护系统的正常运行,进程号为1的init。
批处理进程:系统会每隔一段时间集中处理该类进程的相关指令,会有定的延时,但避免的状态切换的耗时因此执行效率高。
交互进程:在执行需要用户输入一些数据否则无法继续执行。
Linux/UNIX系统查看进程:
简单形式:ps 以简略方式显示当用户当前终端有控制权的进程。
详细显示:ps aux
a 所有用户的当前终端有控制权的进程
u 显示详细信息
x 包含无控制权进程
进程的信息列表:
USER 进程属主
PID 进程IP
%CPU CPU使用率
%MEM 内存使用率
VSZ 使用虚拟内存的大小(KB)
RSS 使用物理内存的大小(KB)
TTY 终端设备号,?表示无终端控制
STAT 进程的状态
O 就绪态,所有准备工作都已经完成,等待被系统执行。
R 运行状态的进程,Linux系统中没有就绪态,就绪态也用R表示。
S 可被唤醒的睡眠,当收到系统中断、获取到资源、收到信号都可以抟进程唤醒转入运行状态。
D 不可以唤醒的睡眠,只能被系统调用wake_up唤醒。
T 暂停,收到信号SIGSTOP进程将转为暂停状态,收到SIGCONT信号转为进行状态。
Z 僵尸状态,进程已经结束运行,但其父进程还没有回收它的资源。
s 会话首进程
l 多线程进程
< 高优先级进程
N 低优先级进程
+ 前台进程
START 进程启动时间
TIME 进程的运行时间
COMMAND 启动进程的程序
父进程与子进程:
1、一个进程可以创建另一个进程,创建者叫父进程,被创建者叫子进程。
2、父进程启动子进程后,子进程会在操作系统的高度下与父进程同时执行。
3、子进程先于父进程结束时,会向父进程发送SIGCHLD信号,父进程收到SIGCHLD信号后就应该去回收子进程的相关资源,如果父进程没有及时回收子进程将变成僵尸进程。
4、父进程先子进程结束,子进程就会变成孤儿进程,同时会被init进程收养,即成为init的子进程,而init进程也叫孤独院。
进程标识符:
1、每个进程都会有一个以非负数表示的唯一的标识,也就是进程ID,相当于进程的身份证号。
2、进程在任何时候都是唯一的,但可以重用,当一个进程结束时,它的进程ID就可以被其它进程使用(延迟重用)。
pid_t getpid(void);
功能:获取当前进程的进程ID。
pid_t getppid(void);
功能:获取当前进程的父进程ID。
fork创建进程:
pid_t fork(void);
功能:创建一个子进程
返回值:
失败返回一次 -1
成功返回两次,子进程返回0,父进程返回子进程ID。
1、通过fork创建的子进程会拷贝父进程的代码段、全局数据段、静态数据段、堆、栈、IO流缓冲区、文件表。
2、进程创建完成后,父子进程各自独立运行,先后顺序不确定。
3、当进程的总数超出系统限制,fork将创建失败。
4、通过fork创建的子进程会继承父进程的信号处理方式。
vfork创建进程:
pid_t vfork(void);
功能:创建子进程
返回值:
失败返回一次 -1
成功返回两次,子进程返回0,父进程返回子进程ID。
区别:
vfok创建的子进程不会复制父进程的代码段资源,而是通过exec系列函数直接加载一个可执行文件启动子进程。
子进程创建成功前,子进行暂时借用父进程的相关资源来加载子进程,而此时的父是阻塞状态,只有子进程创建成功后,父进程才继续运行。
通过vfork创建的子进程,不会继承父进程的信号处理方式。
int execl(const char *path, const char *arg, ...);
path:可执行文件的路径
arg:命令行参数,至少一个,且以NULL结尾。
int execlp(const char *file, const char *arg, ...);
file:可以执行文件名,该文件必须存储在PATH环境变量的路径下。
arg:命令行参数,至少一个,且以NULL结尾。
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
argv:存储命令行参数的字符串数组,最后一个必须是NULL。
system创建进程:
int system(const char *command);
功能:加载一个可执行程序创建子进程
返回值:成功返回0,失败返回-1。
区别:子进程执行时,父进程阻塞,子进程执行结束后父进程才继续。
进程的正常退出:
1、从main函数return
2、调用标准C函数exit
无论在任何时候的任何位置调用此函数的进程会立即结束。
void exit(int status);
功能:结束当前进程,一旦执行就不会返回。
status:进程的结束状态码,与main函数中的返回值具有同样的意义,反映了进程是如何结束的,父进程调用相关函数可以获取status的你8位,常用的参数:EXIT_SUCCESS,EXIT_FAILURE。
进程在结束之前会执行atexit/on_exit注册的函数,然后再调用_exit/_Exit函数。
int on_exit(void (*function)(int , void *), void *arg);
int atexit(void (*function)(void));
功能:向内存注册一个函数,进程结束时自动执行被注册的函数。
void (*function)(int , void *)
int:exit函数的参数或者return的返回值
void*:是on_exit注册时的第二个参数
3、调用_exit/_Exit函数
void _exit(int status);
功能:Linux系统提供的进程结束函数
void _Exit(int status);
功能:标准库提供的等价于_exit的进程结束函数,目的是为了提高代码的兼容性。
1、status可以是exit函数的参数,也可以是用户提供的,父进程调用相关函数可以获取status的你8位。
2、冲刷所有文件的缓冲区,关闭所有文件。
3、把子进程托付给init进程收养。
4、向父进程发送SIGCHLD信号。
4、进程的最后一个线程执行了返回语句。
5、进程的最后一个线程调用了pthread_exit函数。
进程的异常中止:
1、进程接收到某些信号(他杀)。
2、调用abort函数(自杀)。
3、最后一个线程对取"消请"求做出响应。
回收子进程:
pid_t wait(int *status);
功能:等待所有子进程结束,回收子进程,如果所有子进程都在运行,父进程阻塞。
返回值:
如果没有子进程则返回错误ECHILD。
只要有一个子进程结束,则返回子进程的进程ID和结束状态码。
pid_t waitpid(pid_t pid, int *status, int options);
功能:等待指定的子进程结束,回收子进程。
pid:
< -1 等待进程组号为abs(pid)的进程结束。
= -1 等待任意子进程结束
= 0 等待组进程ID为当前进程ID的进程结束。
> 0 等待进程ID为pid的进程结束。
options:
WNOHANG 如果没有子进程则立即返回。
WUNTRACED 子进程停止立即返回。
WCONTINUED 子进程由暂停状态转为继续状态也返回。
返回值:
如果没有子进程则返回错误ECHILD。
等待的进程结束,则返回进程ID和结束状态码。
解析status的宏:
WIFEXITED(status) 检查进程是否正常退出。
WEXITSTATUS(status) 获取status的低八位,但只有WIFEXITED(status)结果为真时才有意义。
WIFSIGNALED(status) 检查进程是否被信号中止。
WTERMSIG(status) 获取导致进程中的信号,WIFSIGNALED(status)结果为真时才有意义。
WCOREDUMP(status) 检查进程是否由于核心转储结束(段错误),WIFSIGNALED(status)结果为真时才有意义。
WIFSTOPPED(status) 检查进程是否处理暂停状态,WIFSIGNALED(status)结果为真时才有意义。
WSTOPSIG(status) 获取导入进程暂停的信号,WIFSTOPPED(status)结果为真才有意义。
WIFCONTINUED(status) 当进程由暂停转换为继续运行进返回真。
当父进程收到子进程的SIGCHLD信号时,就应该调用wait/waitpid函数回收子进程。
如果子进程已经结束,在调用wait/waitpid函数之前子进程处于僵尸状态,调用后子进程会立即消息。
如果不关心子进程的结束状态,status的参数可以为NULL。