Linux进程
进程相关概念、创建进程fork()、进程退出exit()、等待函数wait()、exec族、system函数、popen函数
一、进程相关概念
问1. 什么是程序,什么是进程,有什么区别?
程序是静态的概念,gcc xxx.c –o pro,磁盘中生成pro文件。
进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程。
问2. 如何查看系统中有哪些进程?
a. ps -aux|grep +(程序名)
b. top指令查看,类似于任务管理器。
问3. 什么是进程标识符?
每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证。
特别地:
Pid=0: 称为交换进程(swapper)作用—进程调度
Pid=1:init进程作用—系统初始化。
获取pid值:
调用getpid函数获取自身的进程标识符。
getppid获取父进程的进程标识符。
问4. 什么叫父进程,什么叫子进程?
进程A创建了进程B。那么A叫做父进程,B叫做子进程,
问5. C程序的存储空间是如何分配?
正文段:代码段
初始化的数据:数据段
未初始化的数据:bss段通常是用来存放程序中未初始化的全局变量和静态变量
堆:malloc等函数申请临时空间
栈:函数调用、局部变量
二、创建进程
fork头文件和函数
// 头文件
#include <unistd.h>
#include <sys/types.h>
//函数
pid_t fork(void);
//返回:success 返回两次 返回值为0 代表当前进程是子进程
// 返回值为非负数 代表当前进程是父进程,返回值为子进程ID
pid_t 实质是 int 被定义在 #include<sys/types.h>
fork创建子进程的目的
-
一个父进程复制自己,父、子进程同时执行不同代码段。父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork。使得子进程处理这种请求,父进程继续下一请求。
-
一个进程执行不同的程序,这是shell常见的情况,这种情况下fork返回立即调用exec。
fork写时拷贝
Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。
也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候。
vfork与fork的区别
区别一:vfork直接使用父进程储存空间,不拷贝。
区别二:vfork保证子进程先运行,当子进程exit后,父进程运行。
区别一举例说明:当子进程修改变量a=10时,那么父进程中的a也改成了10。
三、进程退出
正常退出
- Main函数调用return
- 进程调用exit(),标准C语言库
- 进程调用_exit()或者_Exit(),属于系统调用
- 进程最后一个线程返回
- 最后一个线程调用pthread_exit
异常退出
- 调用abort函数
- 当进程收到某些信号时,如ctrl+C
- 最后一个线程对取消(cancellation)请求做出响应
正常线程退出使用pthread_exit(),进程退出调用exit()。不论时正常还是异常退出状态下,子进程需要传参父进程,使得父进程知道子进程运行状态,将运行状态status传递给wait或者waitpid参数。
下面展示一些 exit代码片
。
// exit头文件
#include <stdlib.h>
//exit主函数
void exit(int status);
//_exit头文件
#include <unistd.h>
//_exit主函数
void _exit(int status);
//_Exit头文件
#include <stdlib.h>
//Exit主函数
void Exit(int status);
四、进程等待
进程等待函数的意义:父进程等待子进程退出,并收集子进程的退出状态。如果子进程的退出状态不被收集,变成僵尸进程(zombie)。
下面展示一些 wait()、waitpid()头文件和主函数
。
// 头文件
#include <sys/types.h>
#include <sys/wait.h>
//主函数
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
//返回
// success 返回 子进程ID ; error 返回 -1
下面展示一些 参数解释
。
status:是一个整型数指针
非空:子进程退出状态放在它所指向的地址中。
空:不关心退出状态。
一般想要获取status的返回值,所用函数为WEXITSTATUS(status)。举例说明,如果检测子进程exit(3),可以得到WEXITSTATUS(status)=3,用法如下:
wait(status);
printf(“father process, status = %d\n”,WEXITSTATUS(status));
pid_t pid的取值
pid==-1,等待任一子进程,此时和wait()等效。
pid>0,等待与pid值一致的子进程。
pid==0,等到其组ID和调用进程的组ID相等的任一子进程。
pid<-1,等到其组ID和pid绝对值相等的任一子进程。
options的取值可以取0,或者取下列组合:
WCONTINUED:pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态。
WNOHANG:常用,waitpid将不阻塞如果指定的pid并未结束。
WUNTRACED:如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
孤儿进程
含义:父进程先结束,子进程后结束。子进程就变成了孤儿进程。
Linux处理方法:Linux避免存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。
**pr_exit(status)**打印status的数值
下面展示一些 pr_exit()代码段
。
// pr_exit()函数
void pr_exit(int status)
{
if(WIFEXITED(status)){
printf("normal termination, exit status = %d\n",WEXITSTATUS(status));
}elseif (WIFESIGNALED(status)){
printf("abnormal termination, exit status = %d\n",WTERMSIG(status));
}
}
五、exec族
功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
// 头文件
#include <unistd.h>
//主函数
extern char **environ;
int execl<