进程的基本操作
概念
程序运行的一个实例,其占有一定的空间。
查询某一进程当前情况
ps aux | grep 进程名
终止进程
kill -9 pid; //pid指需要终止的进程pid
创建
pid_t fork();
该函数有两个返回值,对于子进程其返回的是0,对于父进程,其返回的是子进程的pid。
fork函数一般在使用时都要用if语句进行分流,示例如下:
pid_t pid=fork();
if(pid==0)
{
//子进程
}
else
{
//父进程
}
我们用一段代码来证明fork函数的返回值情况。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid=fork();
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
}
else
{
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
}
return 0;
}
注:pid_t getpid(); 获取当前进程pid;
其结果如下:
根据结果显示fork返回给父进程的值正是子进程的pid。
退出
常见的退出方式有以下两种,exit函数是一种强制退出方式,return是一种常见的退出方式,其在效果上等同于exit函数。使用方法就不做赘述,与主进程中调用方式一样。
- void exit(int status);
- return;
其实还有一种终止进程方式,就是调用system函数执行kill -9 命令终止进程,子进程自己终止自己我把它称为自杀,父进程终止子进程称为他杀,当然用子进程杀掉父进程也是可以的。代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
pid_t pid=fork();
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
}
else
{
char str[15]={0};
sprintf(str,"kill -9 %d",pid);
system(str);
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
while(1);
}
return 0;
}
该方法可以杀掉子进程,但同样无法解决僵尸进程问题。
两种特殊的进程状态
僵尸进程:僵尸进程指子进程已退出但父进程未退出,此时子进程就会成为僵尸进程,即Z状态。僵尸进程的出现是因为子进程退出后没有释放资源造成的,故而僵尸进程会导致内存泄漏。如例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
pid_t pid=fork();
pid_t ppid=0;
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
}
else
{
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
while(1);//父进程进入死循环
}
return 0;
}
结果如下:
由图可知,子进程成为僵尸进程
孤儿进程: 父进程退出但子进程未退出,子进程就会成为孤儿进程。如例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
pid_t pid=fork();
pid_t ppid=0;
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
while(1);//子进程进入死循环
}
else
{
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
}
return 0;
}
结果如下:
由结果可知,子进程仍在运行,这是kill -9 杀掉进行。
进程等待
概念:
指子进程退出后,父进程对子进程的占用资源进行回收。故而进程等待可以避免僵尸进程的出现。
进程等待的两种方式
1、pid_t wait(int*status); //等待任一进程结束
返回值
成功返回被等待进程pid,失败返回-1
参数
status指进程退出状态,不关心则可置为NULL
2、pid_ t waitpid(pid_t pid, int *status, int options);
返回值
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
如例:
1、pid_t wait(int*status);方式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
pid_t pid=fork();
pid_t ppid=0;
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
}
else
{
wait(NULL);//等待子进程
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
while(1);//父进程进入死循环
}
return 0;
}
2、pid_ t waitpid(pid_t pid, int status, int options);方式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i=0;
pid_t pid=fork();
pid_t ppid=0;
if(pid==0)
{
printf("我是子进程,我的pid=%d\n",getpid());
}
else
{
int status;
waitpid(pid,&status,0);//等待子进程
printf("当前进程id=%d\n",pid);
printf("我是父进程,我的pid=%d\n",getpid());
while(1);//父进程进入死循环
}
return 0;
}
无论哪种方式,发现都没有出现僵尸进程
进程替换
概念
创建子进程成功后,其实这个子进程和其父进程具有相同的资源和代码,也就是说子进程将父进程的代码拷贝了一份为己有,父进程拥有的资源子进程也拥有,进程替换就是说将子进程原本的资源全部替换为你想要执行的可执行文件的资源,更通俗的说就是子进程的代码与你想要执行的可执行文件的代码相同,故而子进程就不具备与父进程相同资源的属性。
进程替换需要用到exec函数,其一共有6个,这里我们只说明execlp函数,该函数的参数较为麻烦,在此我将他写的更直白一些。
execlp("路径","路径","参数列表(可用NULL)")
”路径“ 就是说你想要替换的可执行文件的路径,要用绝对路径
第二个参数与第一个参数相同
第三个参数置为NULL即可
如例:
这是要用作替换的程序,我将其编译后生成名为out的可执行文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
printf("已被替换\n");
return 0;
}
要执行的程序
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid=fork();
if(pid==0)
{ execlp("/home/kungege/stady/123/111","/home/kungege/stady/out",NULL);
printf("我是子进程\n");
}
else
{
printf("我是父进程\n");
}
wait(NULL);
return 0;
}
其结果或许还会执行子进程中的 printf(“我是子进程\n”);,然而结果是
因为子进程当中的代码与父进程完全相同,一旦进程替换,子进程的代码就会变为替换的可执行文件的代码,故而printf(“我是子进程\n”); 不会执行