说多线程编程之前先说一下什么是进程,进程和程序的区别就是,程序是内存中的代码,进程是运行的程序。
内存有详细的属性表:
USER:进程的创建者。
PID:进程ID
%CPU:CPU占用率
%MEM:内存使用率
VSZ:占用虚拟内存大小
RSS:占用物理内存大小
TTY:终端设备号,?表示无终端控制的进程
STAT:进程的状态
O 就绪状态,等待被调用
R 运行状态,Linux系统没有O状态,就绪态也就是运行态。
S 可被唤醒的睡眠状态,当系统中断、获得到资源、收到信号都可以反它唤醒然后转入运行状态。
D 不唤醒的睡眠状态,只能被wake_up系统调用唤醒。
T 暂停,收SIGSTOP(19)信号
X 死亡状态
Z 僵尸,已经停止,但父进程没有回收它的相关资源。
s 会话首进程
< 高优先级
N 低优先级
l 多线程化的进程
+ 在前台进程组中
START:进程启动时间
TIME:进程运行了多长时间
COMMAND:启动进程的指令(可执行文件的名字)
这些都是进程的属性,了解即可。
进程:
进程分为父进程,子进程,孤儿进程,僵尸进程。
A进程创建lB进程,那么A进程就是B进程的父进程,B进程就是A进程的子进程。
除了init以外所有进程都有父进程,init是所有进程的父进程或祖宗进程,是进程祖宗。为了便于理解,这里我引入了祖宗进程的概念:父进程的父进程以及之后的都叫祖宗进程。例如A进程创建了B、C进程,B进程创建了D进程,D进程创建了E进程,那么A、B都是E的祖宗进程。
父进程先结束的子进程叫孤儿进程,由祖宗init收养。
进程结束之后产生的尸体(占用的资源)由父进程收尸,如果父进程没有收尸(回收资源)那么就称这个尸体(占用的资源)为僵尸进程。大部分僵尸进程在关机之后会自动回收所以关机能解决90%的问题。
每一个进程都有一个以非负整数表示的唯一编号,即PID(进程id)。
进程ID在任何时候都是唯一的,当可以重用,当进程结束时它的ID可以给新的进程用,采用的规则是延迟重用。例如创建了进程2345,那么在2345被回收前2345这个进程id将无法使用。
pid_t getpid(void);这个函数可以查看当前进程的id,pid_t getppid(void);这个函数可以查看当前进程的父进程id,这两个函数存在于头文件#include <sys/types.h>和
#include <unistd.h>中
然后再来介绍一下fork(void);和vfork(void);这两个函数他们都存在于头文件#include <unistd.h>中
两个函数的作用都是创建一个子进程
区别:
fork:
1、父子进程各自运行,先后顺序不确定,父子进程谁先返回不确定。(某些操作系统可以让子进程先运行)
2、子进程是父副本:子进程被创建时会拷贝父进程的data、bss、heap、stack、缓冲区等,唯独代码段不拷贝,父子进程共享一个代码段。
vfork:
1、子进程先返回,父进程等待,临时,需要子进程真正创建出来,然后父进程才返回。
2、vfork无法单独创建出一个子进程,需要与exec函数配合。
3、vfork不会复制父进程的相应数据内存,而是进行替换。
能和这两个函数配合使用的就是exec系列函数
最后就是进程的退出与回收:
1、从main函数中return
2、调用标准库函数exit
1、EXIT_SUCCESS/EXIT_FATLURE,约定好的进程的退出状态码,把参数的低8位返回给父进程。
2、进程退出前会调用通过atexit/on_exit函数注册的函数。
3、冲刷处理打开状态的标准IO流的缓冲区,并关闭文件。
4、exit函数不会有返回,它之后的代码不会被执行。
5、该函数的底层调用了系统的exit_group函数。
3、调用_Exit/_exit函数退出进程
#include <unistd.h>
void _exit(int status); // 系统
#include <stdlib.h>
void _Exit(int status); // 标准库这两个函数功能完全相同。
6、进程退出前会关闭处于打开状态的文件描述符。
7、把所有的子进程托付给init进程(PID为的进程,也叫孤儿院),这个行为叫托孤。
8、向父进程发送SIGCHLD信号。
4、进程的最后一个线程执行的返回语句。
5、进程的最后一个线程调用了pthread_exit函数。
当然进程也会异常退出,大部分是产生bug(自杀)或者外力强制停止(他杀)。
自杀的主要原因是接收到了SIGABRT信号这里的信号包括段错误,error,这些常见的bug。他杀就比较好理解,最简单就是linux下的KILL命令,应用最多的就是打开任务管理器去关闭一些关不掉的程序。还有一小部分就是程序到最后接收到了“取消”这个命令。
最后收尸的话就比较简单本质上就是调用 pid_t wait(int *status)、pid_t waitpid(pid_t pid, int *status, int options)这两个函数,也没什么好说的。