目录
1. 进程创建
- 进程创建:pid_t fork(void); 创建一个进程,父子进程数据独有,代码共享(因为代码段是只读的)。
-
- 写时拷贝技术:子进程复制了父进程,一开始与父进程指向同一块物理内存;因此看起来父子进程完全相同;但是进程之间具有独立性;意味着当这块物理内存中数据即将发生改变时会重新给子进程开辟物理内存,将数据拷贝过来,因为子进程应该有自己的数据。
- 进程创建采用写时拷贝技术,目的是为了提高进程的创建效率。
子进程创建pcb的时候,就会创建页表,虚拟地址空间......,然后从父进程pcb中去拷贝这些数据过来。
- vfork创建的子进程,父子进程是可以同时运行的。
- pid_t vfork(void); 创建一个子进程,并且阻塞父进程,直到子进程exit退出或者程序替换之后,父进程才会运行。
- vfork创建子进程效率比较高,因为vfork创建子进程之后,父子进程共用同一个虚拟地址空间。共用同一个虚拟地址空间,意味着共用代码段,数据段,这样如果父子进程同时运行会造成栈混乱,因此必须子进程先运行,父进程阻塞,直到子进程退出,所有函数出栈,父进程才运行。
- vfork创建的子进程不能使用main中return退出,因为main中return退出,会释放进程资源。
- 早期使用vfork是因为vfork创建子进程效率高,但是fork实现了写时拷贝之后,创建效率更高了,并且使用起来更加灵活,因此vfork现在已经很少使用。
为什么创建一个子进程? 大多数情况下不是为了让子进程干跟自己一样的活,而是让子进程去调度另一个程序运行。
- 创建子进程的流程:在内核中调用clone接口创建pcb,从父进程pcb中拷贝数据过来->写时拷贝技术。
2. 进程终止
- 进程终止:退出一个进程。
- main函数中return 退出进程时会刷新缓冲区。
- void exit(int status); 库函数 退出调用进程,将staus作为返回值返回给父进程。
- void _exit(int status);系统调用接口 退出调用进程,将status返回给父进程。
库函数的系统调用接口的关系:库函数封装了系统调用接口,系统调用接口不太好用。
- exit/return退出时都会刷新缓冲区;_exit退出的时候直接释放资源,不刷新缓冲区。
- exit和return区别:return之后在main函数中的时候才会退出进程,而exit是在任意位置调用都会退出进程。
以上3中进程退出方式,都属于进程的正常退出,会根据返回值向父进程表示为什么退出。但是进程退出也有可能是异常退出-> 程序崩溃,程序没有运行完毕突然因为某种错误退出了。
3. 进程等待
- 进程等待:父进程等待子进程退出,获取子进程的返回值,避免产生僵尸进程。
- pid_t wait(int *status);
阻塞等待任意一个子进程的退出,获取子进程的返回值放到status指向的空间中,并且释放子进程资源;返回退出的子进程pid。
阻塞:为了完成某个功能发起一个调用,若当前不具备完成功能的条件,则调用不返回一直等待。
非阻塞:为了完成某个功能发起一个调用,若当前不具备完成功能的条件,则立即报错返回;非阻塞操作通常需要循环操作。
非阻塞操作相较于阻塞操作,对CPU利用更加充分,因为不再一直等待了,但是必须循环进行操作。
- pid_t waitpid(pid_t pid,int *status, int options);
pid:若pid==-1, 则表示等待任意一个子进程的退出,若pid>0, 则表示等待指定的子进程退出。
status:输出型参数,传入一个int空间的首地址,获取退出子进程的返回值。
options:选项参数,0表示默认阻塞等待;WNOHANG将waitpid设置为非阻塞,没有子进程已经退出的话就立即报错返回。
返回值:若等待到子进程退出则返回子进程的pid;若有子进程,但是没有退出则返回0;出错返回-1。
wait(status) == waitpid(-1, status, 0).
wait和waitpid并不是只处理刚退出的进程,而是对已经退出的进程进行处理(不管什么时候退出的)。
int status --> status 这个变量有四个字节的空间,向函数传入地址
int *status---> 定义了一个指针变量,有8个字节空间,存放的是另一块空间的地址
子进程的退出返回值,实际只有一个字节的存储空间,也就意味着返回值最好不要大于255;这个1个字节的返回值,在status四个字节中存放的时候是存放在低16位中的高8位中,retval = status>>8 && 0xff.
4. 程序替换