文章目录 查看进程相关命令 进程相关函数 孤儿进程 僵尸进程 进程回收 进程通信(IPC)
查看进程相关命令
ps -ef: System V 风格查询所有的进程信息,-e 参数表示显示所有进程,-f 表示使用全格式输出,包含更多的列信息。输出是静态的,即显示的是执行命令那一刻的快照。 ps aux/ajx:BSD 风格查询所有进程信息其中 aux 是一个快捷方式,等同于 -a -u -x,a 表示所有用户的所有进程,j 添加了作业控制相关信息,而 x 显示没有控制终端的进程,u则显示进程的详细信息。 top:实时监视进程信息 kill:并非杀死进程,而是给进程发送一个信号 kill -l:列出所有信号 kill -9:杀死进程 kill -9 进程ID(PID)
进程相关函数
每个进程都由唯一进程号来标识,其类型为pid_t(int整形),当某个进程终止后回收该进程号,可以分配给其他进程使用。 除init进程外,任何进程都是由另一个进程创建的,该进程为父进程,对应的进程号为父进程号(PPID),被创建的进程为其子进程。 进程组是一个或者多个进程的集合,互相关联,进程组可以接收同一终端的信号,关联的进程有一个进程组号(PGID)
pid_t getpid(void);//获取进程号
pid_t getppid(void);//获取父进程号
pid_t getpgid(pid_t pid);//获取进程的组进程号
进程创建函数fork() :调用 fork()后,会创建一个与父进程状态几乎一致的子进程。但子进程与父进程采用读时共享、写时复制 的策略,在fork()后父进程和子进程共享同一个虚拟地址空间映射(页表),映射到同一物理地址 。只有在父进程或子进程需要进行写入操作时才会为相应的进程分配物理地址空间 ,从而使各自进程拥有各自的地址空间。子进程也可以创建其新的子进程。fork()会返回两次,通过返回的pid判断是父进程还是子进程,返回值>0为父进程,==0为子进程 ,交替抢CPU使用。
#include <sys/types.h>
#include <iostream>
#include <unistd.h>
int main(){int num=10;//创建子进程pid_t pid=fork();//如果使用循环创建多个子进程一定注意,只让父进程创建,对于创建的子进程不做处理即需要判断if(pid==0){break;}//判断父进程还是子进程if(pid>0){std::cout<<"父进程"<<getpid()<<std::endl;num+=10;std::cout<<num<<std::endl;}//判断子进程else if(pid==0){std::cout<<"子进程"<<getpid()<<"父进程"<<getppid()<<std::endl;num+=100;std::cout<<num<<std::endl;}return 0;
}
#include <stdlib.h>//c语言的进程退出,通过调用Linux的进程退出来实现,并且引入了缓冲区,同c的文件IO函数fopen和fclose也是调用Linux的文件IO函数
void exit(int status)#include <unistd.h>//Linux的进程退出
void _exit(int status)
孤儿进程
父进程结束但是子进程还在运行,该子进程就叫孤儿进程,孤儿进程的父进程会被init进程替代,由init进程对其资源进行释放操作,无危害。
僵尸进程
每个进程结束后用户区的数据可以自行释放,而内核区的PCB需要父进程释放,如果子进程终止,但父进程没有对其内核区的PCB进行回收,子进程的资源残留在内核中,变成僵尸进程,一直占用PID等资源且无法被kill -9杀死,只有杀死其父进程使其变成孤儿进程最后才能被init进程回收。
进程回收
父进程通过调用wait()或waitpid()
函数进行进程的回收,二者不同的是前者会阻塞等待进程结束,而后者可以设置WNOHANG
不阻塞且waitpid()可以指定等待哪个子进程结束,一次调用只能清理一个子进程。
进程通信(IPC)
管道通信pipe:例如常见的管道符|
就是管道通信,ps -ef | grep ssh
,将前一个指令的输出作为后一个指令的输入,管道通信是单向的半双工通信,一端读另一端写。管道实际是内存维护的一片缓冲区,可以使用操作文件的方式操作管道,使用文件描述符fd以及open()、close()、read()和write()
。
匿名管道pipe通信的使用
创建管道
#include <unistd.h>
int pipe(int pipefd[2]);
//包括读端pipefd[0]和写端pipefd[1]
//如果成功创建管道,pipe()函数返回0。若出现错误(比如资源不足),则返回-1,并用perror输出。//创建管道
int pipefd[2];
int ret=pipe(pipefd);
if(ret==-1){perror("pipe");return 0;
}//读管道,类似读文件
....
close(pipefd[1]);//关闭写端
char buff[1024]={0};
while(1){
int len=read(pipefd[0],buff,sizeof(buff));
if(len==0){break;
}
}
....//写管道,类似写文件
....
close(pipefd[0]);//关闭读端
const char *str="HELLO!HAHAHA!" ;
write(pipefd[1],str,sizeof(str));
....
匿名管道只能在有公共祖先的进程下使用,因为父进程fork()后,其子进程都共享父进程的虚拟地址空间映射,包括文件描述符表,其中包含读端pipefd[0]和写端pipefd[1],所以才能进行通信。同时因为半双工通信,读进程需要关闭写端close(pipefd[1]),写进程需要关闭读端close(pipefd[0]) ulimit -a
:查看管道缓冲的大小
有名管道:FIFO
通过创建管道名称(实则是文件作为管道),可以用于无关系之间的进程的通信,也是通过文件描述符和文件IO进行操作。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//pathname:一个指向包含命名管道路径名的字符串指针。
//mode:指定命名管道的权限位,类似于普通文件的权限设置。
//如果成功创建命名管道,返回0。如果失败则返回-1,并设置errno变量//创建管道test
int ret=mkfifo("test",0664);
if(ret==-1){perror("管道创建错误");return 0;
}//只写打开管道即关闭读端,类似打开文件操作。
int fd=open("test",O_WRONLY);
if(fd==-1){perror("open");return 0;
}
//写管道,类似写文件
....
const char *str="HELLO!HAHAHA!";
write(fd,str,sizeof(str));
....//只读打开管道即关闭写端,类似打开文件操作。
int fd=open("test",O_RDONLY);
if(fd==-1){perror("open");return 0;
}
//读管道,类似读文件
....
char buff[1024]={0};
while(1){
int len=read(fd,buff,sizeof(buff));
if(len==0){break;
}
}
....