进程控制
创建进程
fork
//创建子进程
pid_t fork(void);
//成功:父进程返回子进程的PID,子进程返回0;失败:返回-1,设置errno值
父子进程谁先抢到CPU时间片谁先执行
父子进程间不能共享全局变量,虽然打印出来的地址一样,因为是虚拟地址
如果父子进程只是对全局变量做读操作,则父子进程在内存中只有一份,属于共享
如果父子进程中的任何一个进程对该变量做修改操作,会在内存中拷贝一个副本,然后在这个副本上进行修改,修改完成以后映射回去
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
printf("before fork,pid:[%d]\n",getpid());
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("father:pid==[%d]\n",getpid());
sleep(1);
}
else if(pid==0)
{
printf("child:pid==[%d]\n",getpid());
}
printf("after fork,pid:[%d]\n",getpid());
return 0;
}
ps和kill
ps aux | grep "xxx"
ps ajx | grep "xxx"
//-a:(all)当前系统所有用户的进程
//-u:查看进程所有者及其他一些信息
//-x:显示没有控制终端的进程 -- 不能与用户进行交互的进程【输入、输出】
//-j: 列出与作业控制相关的信息
kill -l
//查看系统有哪些信号
kill -9 pid
//杀死某个线程
getpid和getppid
//得到当前进程的PID
pid_t getpid(void);
//得到当前进程的父进程的PID
pid_t getppid(void);
循环创建n个子进程
//循环创建n个子进程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int i=0;
for(i=0;i<3;i++)
{
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("father:pid==[%d]\n",getpid());
sleep(1);
}
else if(pid==0)
{
printf("第%d个子进程,child:pid==[%d]\n",i+1,getpid());
break;
}
}
return 0;
}
exec函数族
有的时候需要在一个进程里面执行其他的命令或者是用户自定义的应用程序,此时就用到了exec函数族当中的函数
使用方法一般都是在父进程里面调用fork创建处子进程,然后在子进程里面调用exec函数
execl
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
//path: 要执行的程序的绝对路径
//变参arg: 要执行的程序的需要的参数
//arg:占位,通常写应用程序的名字
//arg后面的: 命令的参数
//参数写完之后: NULL
//若是成功,则不返回,不会再执行exec函数后面的代码;若是失败,会执行execl后面的代码,可以用perror打印错误原因
//execl函数一般执行自己写的程序
execlp
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
//file: 执行命令的名字, 根据PATH环境变量来搜索该命令
//arg:占位
//arg后面的: 命令的参数
//参数写完之后: NULL
//若是成功,则不返回,不会再执行exec函数后面的代码;若是失败,会执行exec后面的代码,可以用perror打印错误原因
//execlp函数一般是执行系统自带的程序或者是命令
//父子进程间能否共享全局变量
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
int main()
{
//创建一个子进程
pid_t pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("father\n");
sleep(2);
}
else if(pid==0)
{
printf("child\n");
execl("./test","test","hello","dd",NULL);
perror("execl error");
}
return 0;s
}
进程回收
当一个进程退出之后,进程能够回收自己的用户区的资源,但是不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数完成对子进程的回收,避免造成系统资源的浪费
孤儿进程
若子进程的父进程已经死掉,而子进程还存活着,这个进程就成了孤儿进程
为了保证每个进程都有一个父进程,孤儿进程会被init进程领养,init进程成为了孤儿进程的养父进程,当孤儿进程退出之后,由init进程完成对孤儿进程的回收
僵尸进程
若子进程死了,父进程还活着, 但是父进程没有调用wait或waitpid函数完成对子进程的回收,则该子进程就成了僵尸进程
如何解决僵尸进程
- 由于僵尸进程是一个已经死亡的进程,所以不能使用kill命令将其杀死
- 通过杀死其父进程的方法可以消除僵尸进程。杀死其父进程后,这个僵尸进程会被init进程领养,由init进程完成对僵尸进程的回收
进程回收函数
wait
pid_t wait(int *status);
//阻塞并等待子进程退出
//回收子进程残留资源
//获取子进程结束状态(退出原因)
//成功:清理掉的子进程ID;失败:-1 (没有子进程)
//status参数:子进程的退出状态 -- 传出参数
//WIFEXITED(status):为非0 → 进程正常结束
//WEXITSTATUS(status):获取进程退出状态
//WIFSIGNALED(status):为非0 → 进程异常终止
//WTERMSIG(status):取得进程终止的信号编号
//父进程调用wait函数完成对子进程的回收
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/wait.h>
int main()
{
pid_t pid=fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("father:pid=[%d],fpid=[%d]\n",getpid(),getppid());
int status;
pid_t wpid=wait(&status);
printf("wpid==[%d]\n",wpid);
if(WIFEXITED(status))//正常退出
{
printf("child normal exit,status==[%d]\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))//被信号杀死
{
printf("child killed by signal,signo==[%d]\n",WTERMSIG(status));
}
}
else if(pid==0)
{
printf("child:pid=[%d],fpid=[%d]\n",getpid(),getppid());
sleep(5);
return 9;
}
return 0;
}
waitpid
pid_t waitpid(pid_t pid, int *status, in options);
//同wait函数
//pid:
//pid = -1 等待任一子进程。与wait等效
//pid > 0 等待其进程PID与pid相等的子进程。
//pid = 0 等待进程组ID与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程
//pid < -1 等待其组ID等于pid的绝对值的任一子进程。(适用于子进程在其他组的情况)
//status: 子进程的退出状态,用法同wait函数
//options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞
//函数返回值
//>0:返回回收掉的子进程ID;
//-1:无子进程
//=0:参3为WNOHANG,且子进程正在运行