进程
进程概念
触发 任何一个事件时,系统都会将他定义成为一个进程,并且给予这个进程一个 ID ,称为 PID,同时依据启发这个进程的用户与相关属性关系,给予这个 PID 一组有效的权限设定。
并行和并发
并发的概念是:
并发,在一个时间段内, 是在同一个cpu上, 同时运行多个程序。
如:若将CPU的1S的时间分成1000个时间片,每个进程执行完一个时间片必须无条件让出CPU的使用权,这样1S中就可以执行1000个进程。
并行性指两个或两个以上的程序在同一时刻发生(需要有多颗)。
PCB
每个进程都在内核中都有一个进程控制块来维护进程相关的信息,linux内核的进程控制块是task_stacut结构体
-
进程ID。系统中每个进程都有唯一的ID,在C语言中用pid_t标识
-
进程的状态,有就绪、运行、挂起、终止等状态
-
进程切换的时候恢复寄存器
-
描述虚拟地址空间
-
控制终端的信息
-
当前工作目录
-
umask掩码
-
文件描述表
-
信号相关信息
-
用户ID和用户组ID
-
会话和进程组
-
进程可使用的资源上限
进程状态
处于就绪态的进程,有执行资格,但是没有CPU时间片
处于挂起态的进程即没有执行资格,也没有CPU时间片
处于挂起态的进程不可以直接回到运行态,必须回到就绪态
处于就绪态可以回到运行态
运行状态(TASK_RUNNING)
可中断睡眠状态(TASK_INTERRUPTIBLE)
不可中断睡眠状态(TASK_UNINTERRUPTIBLE)
暂停状态(TASK_STOPPED)
僵死状态(TASK_ZOMBIE)
创建进程
fork
创建子进程
pid_t fork(void);
调用成功后返回子进程的PID,子进程返回0;
如果失败,则返回-1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
printf("begin fork for pid = %d\n",getpid());
//pid_t fork(void);
pid_t pid = fork();
if(pid<0) //检查fork失败
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("parent pid = %d fpid = %d\n",getpid(),getppid());
sleep(5);
return 1;
}
else if(pid==0)
{
printf("child pid = %d fpid = %d\n",getpid(),getppid());
}
printf("fork ends for pid = %d\n",getpid());
return 0;
}
}
父子进程不可以共享全局变量
进程间的全局变量遵循:写时复制,读时共享;
vfork
vfork创建完成子进程程必须立即执行_exit或者exec函数;
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>
#include <sys/times.h>
#include <wait.h>
#include <sys/stat.h>
#include <fcntl.h>
//vfork函数
int main(int argc,char* args[])
{
int ret = 0;
int i = 10;
pid_t pid = vfork();
printf("pid == %d\n",getpid());
if(pid == -1)
{
perror("fork error");
return -1;
}
if(pid>0)
{
i++;
printf("parent pid == %d\n",getpid());
printf("i = %d\n",i);
}
if(pid==0)
{ char *const argv[] = {"ls","-l",NULL};
ret = execve("/bin/ls",argv,NULL); //拉起ls函数
printf("ret = %d\n",ret);
exit(0);
}
return 0;
}
ps
常用命令:
ps -ajx
ps -aux
pf -ef | grep init
kill
kill -l
#获取所有的信号
kill -9 xxx
kill -sigstop xxx
命令执行
execl
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
执行execl函数后,子进程的代码段被命令的代码段替换;子进程的地址空间不会变化,子进程PID也不会改变
execlp
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
pid_t pid = fork();
if(pid<0) //检查fork失败
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("parent pid = %d fpid = %d\n",getpid(),getppid());
}
else if(pid==0)
{
printf("child pid = %d fpid = %d\n",getpid(),getppid());
//execl("./hello","hello","hello","hello","hello",NULL);
//execlp("./hello","hello","-l",NULL);
execlp("ls","ls","-l",NULL);
//execl("/bin/ls","ls","-l",NULL);
perror("execl error");
}
return 0;
}
进程回收
当一个进程退出后,进程能够回收自己的用户区的资源,但是不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数对子进程进行回收,避免造成系统资源的浪费;
孤儿进程
孤儿进程的父进程已经死掉,而子进程还存活,这个进程就成为了孤儿进程;操作系统为了保证每个进程都有一个父进程,孤儿进程会被init进程领养,由init进程对孤儿进程进行回收;
模拟孤儿进程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc,char* args[])
{
pid_t pid = fork();
if(pid<0) //检查fork失败
{
perror("fork error");
return -1;
}
else if(pid>0)
{
exit(0); //父进程先死
printf("parent pid = %d fpid = %d\n",getpid(),getppid());
}
else if(pid==0)
{
printf("child pid = %d fpid = %d\n",getpid(),getppid());
sleep(20);
printf("child pid = %d fpid = %d\n",getpid(),getppid());
}
}
僵尸进程
子进程先退出,父进程在等待子进程退出,这个时候子进程变成了僵尸进程;
僵尸进程并不能被kill杀死,但是我们可以杀死父进程来消灭僵尸进程,原因是杀死父进程后,僵尸进程就会被init进程回收
wait
阻塞线程等待子进程退出;回收残留线程;
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc,char* args[])
{
pid_t pid = fork();
pid_t w;
int status;
if(pid<0) //检查fork失败
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("parent pid = %d fpid = %d\n",getpid(),getppid());
w=wait(&status);
printf("wpid = %d\n",w);
if(WIFEXITED(status))//WIFEXITED宏的释义: wait if exit ed
{
printf("子进程返回信息码:%d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("子进程信号中断返回信息码:%d\n",WTERMSIG(status));
}
else if(WIFSTOPPED(status))
{
printf("子进程暂停返回信息码:%d\n",WSTOPSIG(status));
}
else
{
printf("其他退出信息!\n");
}
}
else if(pid==0)
{
printf("child pid = %d fpid = %d\n",getpid(),getppid());
abort();
}
return 0;
}
systemk1t@systemk1t-virtual-machi
waitpid
pid_t waitpid(pid_t pid, int *status, int options);
参数pid:
pid = -1 等待任意子进程。与wait等效。
pid > 0 等待指定的子进程
pid = 0 等待进程组ID与目前进程相同的任何子进程,也就是说任何和调用
waitpid()函数的进程在同一个进程组的进程。
参数options:
设置为WNOHANG,函数非阻塞,设置为0,函数阻塞。
返回值:
如果返回值大于0:回收的子进程的PID
如果返回值等于0:若options取值为WNOHANG,则标识子进程还存在
如果返回值等于-1:标识没有子进程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <wait.h>
int main(int argc,char* args[])
{
pid_t pid = fork();
pid_t w;
int status;
if(pid<0) //检查fork失败
{
perror("fork error");
return -1;
}
else if(pid>0)
{
printf("parent pid = %d fpid = %d\n",getpid(),getppid());
w = waitpid(-1,&status,WNOHANG);
printf("wpid = %d\n",w);
}
else if(pid==0)
{
printf("child pid = %d fpid = %d\n",getpid(),getppid());
//abort();
//return 9;
//exit(0);
sleep(5);
}
return 0;
}
status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充
如果传递NULL,表示不关心子进程的退出状态信息,
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)
正常退出
status & 0x7f, 如果是0的话,那就表示正常退出
0xff是0111111,status 按位与0x7f,表示取出status的低7位
异常退出
status & 0x7f, 不为0的话,那就表示异常退出
判断子进程正常/异常退出的时候:
status & 0x7f(status按位与0x7f)的时候,按位与7位就好,如果按位与的结果是0,那就表示子进程正常退出,按位与的结果不是0,那就表明子进程异常退出
阻塞式等待&非阻塞式等待
1.阻塞式等待
子进程先执行,而且父进程会等待子进程执行结束,再去执行自己
2.非阻塞式等待
子进程和父进程谁先执行不确定,父进程如果先执行的话,父进程会一边等待
子进程而另一边又做自己的事情,即就是等待子进程和执行自己的事情是同步
进行的
status参数:
WIFEXITED(status) //非0 进程正常退出
WEXITSTATUS(status) //获取进程退出状态
WIFSIGNALED(status) //非0 进程异常终止
WTERMSIG(status) //取得进程终止的信号编号
abort
- 立即终止当前进程,产生异常程序终止
- 进程终止时不会销毁任何对象
exit
- 正常终止进程,并进行清理!