多进程编程
基本概念
进程是一个具有单独功能的程序对某个数据集在处理机上的执行过程,进程也是作为资源分配的一个单位。
进程和程序是相辅相成的,进程是一个动态概念。
进程具有并行性特征。进程具有独立性和异步性。
进程的描述
进程分为三部分:进程控制块PCB、程序段、数据集
PCB是标识和描述进程存在及其相关特性的数据块,是进程存在的唯一标识。
系统首先创建其PCB然后根据PCB中的信息对进程进行有效的管理和控制。
PCB包括:进程标识、状态信息、进程的优先级、CPU现场信息、资源清单、队列指针等。
每个进程都由一个task_struct数据结构来表示,task_struct叫做进程控制块或者进程描述符。
task_struct
可分为进程标识符信息PID、进程调度信息、进程间通信信息、时间和定时器信息、进程链接信息、文件系统信息、虚拟内存信息、处理器特定信息等。
1.进程标识符信息
每个进程都有唯一的进程标识符,最大的PID号是32767,进程标识符还包括了用户标识符uid、有效用户标识符euid、组标识符gid、有效组标识符egid等
2.进程调度信息
主要包括调度标志、调度的策略、进程的类别、进程的优先级、进程的状态
状态一般有可运行、可中断等待、不可中断等待、暂停、僵尸等
3.进程链接信息
存储了用于链入进程双向链表的前后指针,指向祖先进程、父进程、子进程、兄弟的指针
4.时间和定时器
内核需要记录进程的创建时间以及在其生命周期中消耗的CPU时间。
5.文件系统信息
进程会经常访问文件系统资源,打开或关闭文件,Linux内核要对进程使用文件的情况进行记录。
6.虚拟内存信息
Linux采用按需分页的策略来解决进程的内存需求,当物理内存不足时,Linux内存管理系统需要把内存中的部分页面交换到外存。
7.处理器特定信息
所有与CPU相关的处理机状态都储存在这。
Linux支持两种进程:普通进程和实时进程。
进程的标识符
用于唯一标识某个进程,在程序运行时,PID不会改变,程序终止之后PID就会被系统回收,以后分配给新的进程。类型pid_t,实际上就是int。
我们可以使用
pid_t getpid(void);
来查看进程的pid
PID文件
PID文件为文本文件,内容只有一行,记录了该进程的id。
用于防止进程启动多个副本。只有获得相应PID文件写入权限的进程才能正常启动,并把自身PID写入该文件中。
通常有两种方法配合PID文件来实现进程的重复启动:文件加锁法、PID读写法。
文件加锁法:进程运行后会给pid文件加一个文件锁,只有获得该锁的进程才有写入权限,其他试图获得该锁的进程会自动退出。加锁函数 fcntl。
PID读写法:先启动的进程往PID中写入自己的进程ID号,然后其他进程判断该PID文件中是否有数据。
tinclude <stdlib.h>
tinclude <stdio.h>
finclude <sys/types.h>
tinclude <sys/stat.h>
tinclude <fcntl.h>
tinclude <unistd.h>
tinclude <string.h>
include <signal.h>static char*starter pid file default ="/var/run/test.pid";static bool check pid(char *pid file)
{struct stat stb;FILE *pidfile;if (stat(pid file,6stb)==0){pidfile = fopen(pid file,"r");if (pidfile){char buf[64];pid_t pid =0;memset(buf,0,sizeof(buf));if (fread(buf,1,sizeof(buf),pidfile)){buf[sizeof (buf)-1]\0';pid = atoi (buf);}fclose(pidfile);if(pid&&kill(pid,0)==0)/检查进程{ /* such a process is running */return 1;}}printf ("removing pidfile '&s',process not running",pid file);unlink (pid file);}return 0;
}
int main()
{FILE *fd = fopen(starter pid file default,"w");if (fd){fprintf (fd,"%u\n",getpid());fclose(fd);}if (check_pid(starter_pid_file_default)){printf("test is already running (%s exists)",starter_pid_file_default);}elseprintf("test is NOT running(%s NOT exists)",starter_pid_file_defult);unlink(starter_pid_file_default);return 0;
}
进程的创建
使用fork创建进程
由fork创建的进程称为子进程,和父进程的属性基本是等同的。但是他们并不共享内存,而是采用写时复制的技术。
#inlcude<unistd.h>
pid_t fork();
使用exec创建进程
#include <unistd.h>
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
int execle(const char *path,const char *arg,···,char const envp[]);
int execv(const char *path,char *const argv[]);
int execvpe (const char *file,char *const argv[],char *const envp[]);
进程调度
进程调度就是处理机调度。
任务就是控制和协调进程对CPU的竞争,按照一定的算法是某一就绪程序取得CPU的控制权,转为运行状态。
常用算法有:先来先服务算法,时间片轮转法,优先级算法,多级反馈算法
进程状态转换
进程的分类
前台进程
普通进程
可以通过
ps -e|grep 进程名
来查看
后台进程
在启动时加一个'&'。一般称为job。
守护进程
是运行在后台的特殊进程。独立于终端并周期性的执行某种任务或等待处理某些发生的事件。
它的父进程是init进程,因为它产生以后它的父进程就退出由init继承。
特点
具有超级用户的权限。
守护进程的父进程是init进程。
不用控制终端,tty以'?'表示。
是自己进程组合会话过程的唯一进程。
查看守护进程
ps x或ps axj