1.进程:
1.1什么是进程:
我们首先需要分清程序与进程的概念。程序是一种静态的概念,是指磁盘中生成的文件,而进程是指程序一次运行活动,简言之程序跑起来,系统中就多了一个进程。
1.2如何查看系统中有那些进程
第一种方式:使用ps指令查看,实际使用中,我们通常配合grep来查找程序中是否存在某一个进程。
第二种方式使用top指令查看
1.3什么是进程标识符
每一个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证。
pid=0;//成为交换进程,作用进程调度
pid=1;//init进程,系统初始化
我们通过getpid函数获取自身的进程标识符,getppid获取父进程的标识符。
1.4父子进程
进程A创建了进程B 那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。
1.5C语言在存储空间内是如何分配的
2.进程创建fork函数
2.1函数原型
我们通过man手册查询fork函数的原型如下:
pid_t fork(void);
根据man手册我们知道fork函数调用成功,返回两次,返回值为0,代表当前进程是子进程,返回值非负数,代表当前进程为父进程。调用失败,返回-1.
2.2使用fork函数的目的
1.在类似网络服务进程中———父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,使得子进程处理此请求。父进程则继续等待下一个服务请求到达。具体例子代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
pid_t pid;
int data;
while(1){
printf("please input a data\n");
scanf("%d ",&data);
if(data ==1){ //如果data=1则使用fork
pid=fork();
if(pid>0)
{
} //父进程什么都不做
else if(pid==0)
{ //子进程循环打印,并且输出进程号
while(1){
printf("do net request,pid =%d\n",getpid());
sleep(3);
}
}
}
else{
printf("wait ,do nothing\n");
}
}
return 0;
}
2.一个进程要执行一个不同的程序即子进程从fork返回后立即调用exec
3.进程创建函数vfork函数
3.1区别
与fork相比vfork直接使用父进程存储空间,不拷贝:vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。具体例程。
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
pid_t pid;
int cnt =0;
pid=vfork();
if(pid>0)
{
while(1)
{
printf("cnt=%d\n",cnt);
printf("this is father print,pid =%d\n",getpid());
sleep(1);
}
}
else if (pid ==0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt==3){
exit(0);
}
}
}
return 0;
}
通过运行结果我们可以看出,调用vfork后先调用子进程,等待exit退出后父进程继续运行。
4.进程退出
4.1正常退出
1.main函数调用return
2.进程调用exit(),
3.进程调用_exit()或者_Exit(),属于系统调用
(最后一个线程返回,则代表这进程退出,最后一个线程退出调用pthread_exit)
4.2异常退出
1.调用abort
2.当进程收到某些信号时,如ctrl+c
3.最后一个线程对取消(cancellation)请求做出响应
4.3三个中止函数
exit,_exit,_Exit作用是,将其退出状态作为参数传送给函数。函数原型
5.等待子进程退出
5.1目的
判断子进程任务是否完成,是否正常退出,是否被异常杀死,并且收集退出状态反馈给父进程使得父进程可以正常运行。
5.2父进程等待子进程退出并收集子进程的退出状态
调用wait和waitpid会返回这几种状态。注意子进程退出状态不被收集,就会变成僵尸进程。例子
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
pid_t pid;
int cnt =0;
pid=vfork();
if(pid>0)
{
while(1)
{
printf("cnt=%d\n",cnt);
printf("this is father print,pid =%d\n",getpid());
sleep(1);
}
}
else if (pid ==0){
while(1){
printf("this is child print,child pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt==3){
exit(0);
}
}
}
return 0;
}
在这段代码中我们就没有收集子进程的退出码造成了子进程成了僵尸进程。
在这张图中我们可以看出,有一个进程号为4348的进程为僵尸进程。
5.3wait函数
函数原型
#include <sys/wait.h>
pid_t wait(int *_Nullable wstatus);
pid_t waitpid(pid_t pid, int *_Nullable wstatus, int options);
wstatus是一个整数型指针,如果为非空指针,则子进程退出状态放在它所指向的地址中。
空指针则不关心退出状态。如需要知道退出状态,需要以下几个宏进行解析
wait和waitpid区别:waitpid有个选项,可以使调用者不阻塞。