在linux多用户、多任务操作系统中,进程是应用程序在内存中的基本执行单元,同时也是系统资源分配的基本单元。
1.进程的概念
进程是指操作系统中被加载到内存中、正在运行的应用程序的实例。
1.进程的组成
进程主要由程序、数据以及进程控制块(PCB)组成
1.程序
程序是描述进程功能的可执行机器指令,它通常作为一个静态文件存储在计算机系统的硬盘等存储设备之中。
2.数据
数据是进程的操作对象,同一段程序在不同数据集上的执行过程是不同的进程。
3.进程控制块
进程控制块是操作系统中用来描述和控制进程的一种数据结构,他是进程存在的唯一标志。它主要包括进程的标示符(PID)、
父进程的标示符(PPID)、启动进程的用户ID(UID)和所归属的组(GID)、进程当前的状态、进程的优先级以及进程的资源占用情况。
2.进程状态的转化
在多任务系统中,进程在CPU上交替运行,状态不断改变,
下面介绍3中最基本的状态::
运行态:当一个进程在CPU上运行时,称该进程处于运行态
就绪态:当进程获得了除了CPU之外的其他所有系统资源,即一旦调入CPU即可运行,则称该进程处于就绪态
阻塞态:当一个进程正在请求I/O或等待某一事件发生而暂停运行时,称阻塞或睡眠
2.linux进程
linux系统中,每个进程由一个task_struct数据结构来描述,即前面说的进程控制块。
1.进程的状态
1.运行状态
处于该状态的进程包含前面介绍的运行态和就绪态。
2.等待状态
处于该状态的进程正在等待某个事件或资源。
等待进程分为两种:可中断的等待状态和不可中断的等待状态。
3.暂停状态
这种状态的进程通常是收到某个信号(SIGSTOP/SIGTSTP/SIGTTIN以及SIGTTOU等)而暂时停止运行,例如正在调试的进程
4.僵死状态
处于该状态的进程一般是由于某些原因被终止,但系统中仍保留着task_struct数据结构,必须进行相应处理以释放其占用的资源。
2.进程调度信息
linux系统中,进程可以分为两类:普通进程和实时进程,实时进程的调度优先级比普通进程的调度优先级高,即只要系统中有一个实时
进程处于就绪态,则下一个被调度的进程就是这个实时进程。
进程的调度策略也分为两种:时间片轮转调度和先来先服务调度。
3.进程的标示符
1.用户标示符(UID)和组标示符(GID)
2.有效用户标示符(EUID)和有效组标示符(EGID)
可以通过setuid和setgid来设置
3.文件系统用户标示符(FSUID)和文件系统组标示符(FSGID)
可以通过setfsuid和setfsgid来设置
4.备份用户标示符(SUID)和备份组标示符(SGID)
4.进程间通信相关信息
信号,管道,共享内存,信号量和消息队列
5.进程的链路信息
pstree命令可以查看
6.时间和定时器信息
一个进程从创建到终止称为该进程的生存期。
进程耗费的CPU时间由两部分时间组成:一是在用户模式下耗费的时间;二是在系统模式下耗费的时间。
linux系统中定时器主要包括如下:
1.实时定时器(ITIMER_REAL)
实时定时器不管其所属的进程是否运行都会进行更新。
2.虚拟定时器(ITIMER_VIRTUAL)
该定时器只在进程运行且处于用户态时才会进行更新。
3.Profile定时器(ITIMER_PROF)
该定时器只在进程运行时进行更新。
7.文件系统信息
3.进程创建与控制
1.fork函数
复制当前进程内容,产生一个新进程,返回值为0,则为子进程。
也可以将父进程阻塞直到子进程完成任务,这时使用的函数为wait或waitpid,这两个函数会暂停当前进程的运行,直到有信号或子进程结束。
wait和waitpid函数的主要差别在于:wait函数阻塞调用进程直至有一个子进程结束,而waitpid函数则可以选择不阻塞立即返回,或指定
等待直至有n个子进程结束。
2.vfork函数和exec函数
vfork函数的作用类似于fork函数,使用vfork函数创建新进程的主要目的在于使用exec函数来执行别的程序,即启动一个新的应用程序。
vfork和fork另一个区别是:vfork函数保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行,
exec函数有6种不同的形式:
1.execl
2.execlp
3.execle
4.execv
5.execvp
6.execve
函数中带有l表示list,
函数中带有v表示vector
函数中带有p,表示可执行文件在PATH环境变量的目录列表中搜寻,而不带p的函数表示第一个变量必须制定可执行文件的全路径。
函数中带有e,表示可以传入一个新的环境变量列表,而其他exec函数则使用当前的环境变量。
3.system函数
也可以用来创建一个新的进程
该函数通过调用Shell程序/bin/sh来在新的进程中执行参数cmd所指定的命令,
4.popen函数
popen函数类似于system函数,它是一种执行外部程序的简易方法,不同是它使用 了管道的方式来启动新的进程。
pclose来关闭
5.进程的终止
终止主要有以下几种方法:
在main函数中执行return语句;
调用exit函数;
调用_exit函数;
调用abort函数;
收到能导致进程终止的信号。
6.获取进程信息
获取当前进程
getpid
获取当前父进程
getppid
获取pid所属的组标示符
getpgid
获取当前进程所属的组标示符
getpgrp
获取进程、进程组、以及进程运行优先级。
getpriority
4.守护进程
守护进程是一种运行在后台,独立于所有终端控制之外的特殊进程,守护进程通常是在系统引导时启动,在系统关闭时终止,它周期
性地执行某种任务或等待处理某些发生的事件。
1.守护进程的创建方法
1.将子进程放入后台运行
先调用fork函数创建子进程,然后终止父进程,这样子进程就会有init收养,
2.在子进程中创建新会话
此时的子进程的进程组、会话过程以及控制终端等都是从父进程继承下来的,实现独立功能的一种方式是调用setsid函数,
创建一个新回话,并与原来的进程组、会话过程等脱离。
3.关闭打开的文件描述
守护进程从创建它的父进程哪里继承了打开的文件描述符,但这些被打开的文件可能永远都不会被守护进程读写,如果不
关闭,将会浪费大量系统资源。
4.改变工作目录
使用fork函数创建的子进程会继承父进程的工作目录,而在进程的运行过程中,其工作目录所在的文件系统是不能被卸载的,
通常的做法是将工作目录改变为根目录。
chdir(" ");
5.重设文件权限掩码
守护进程的文件权限掩码是从创建它的父进程哪里继承来的,这会给守护进程使用文件带来诸多麻烦。
umask(0);
定义:
mode_t umask(mode_t mask);
表头文件:
#include<sys/types.h>
#include<sys/stat.h>
说明:
umask()会将系统umask值设成参数mask&0777后的值, 然后将先前的umask值返回。在使用open()建立新文件时,
该参数mode并非真正建立文件的权限, 而是(mode&~umask)的权限值。例如, 在建立文件时指定文件权限为0666,
通常umask值默认为022, 则该文件的真正权限则为0666&~022=0644, 也就是rw-r--r--返回值此调用不会有错误值返回。
返回值为原先系统的umask值。
6.处理SIGCHLD信号
signal(SIGCHLD, SIG_IGN);
定义:
void (*signal(int signum,void(* handler)(int)))(int);
表头文件:
#include<signal.h>
说明:
signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到
参数handler指定的函数执行。如果参数handler不是函数指针, 则必须是下列两个常数之一:
SIG_IGN 忽略参数signum指定的信号。
SIG_DFL 将参数signum 指定的信号重设为核心预设的信号处理方式。
2.守护进程的输出
守护进程没有控制终端,需要提供专门的途径来处理从系统中获得的各种信息,
linux提供了一个syslog守护进程,任何进程都可以通过syslog函数来记录各种事件。
#include<syslog.h>
void syslog(int priority, char *format, ...);