资源描述
///////////////////////// 第五天 进程 //////////////////////////////////////////////////////////////////////////////////////////
三、进程
(一)进程的基本概念
程序:代码,一个普通二制流的文件,里面都是指令。
进程:是处于运行状态的程序,是程序执行过程,是运行的程序。
一个源程序经过编译、链接后,成为一个可执行的程序,当可执行程序被系统加载到内存空间运行时就称为进程。
同一个程序可以有多个进程,每一个进程都有自已的PID
线程:是独立指令流,是运行的代码段。是进程的一部分,在一个进程中可以有多个线程。
进程组:linux系统中,每个进程都唯一归属于某个进程组,在shell环境中,一条linux指令就形成一个进程组,一个进程组可以有多个进程。
进程会话:用户登陆一个新的shell环境,一个新的会话就产生了,一个会话可以包括若干个进程组。
这些进程组只能有一个前台进程组,其它进程组为后台运行进程组
1.进程属性
进程创建后,系统内核为其分配了一系列的数据结构。这些数据结构中保存了进程的相关属性,如下内容:
ps -Alef 查看所有进程
ps -alef 查看当前终端进程
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 1 0 0 78 0 - 518 - ? 00:00:01 init
UID 进程用户标识
PID 进程的标识符,进程创建时,内核为进程分配的唯一标识ID
PPID 进程的父标识符,linux下全部进程组成一棵进程树,树根进程是0号进程swapper,除根进程外,每个进程都有其对应的父进程
STIME 进程状态,wait等待状态,-运行状态
2.进程的内存映像
地址由高到低
命令行参数
栈:栈存放珵序局部变量,函数参数、函数返回值
堆:堆的大小不固定,可根据程序运行过程中的要求动态变化
BBS段:程序未初始化的全局变量
数据段:数来存放程序已初始化的全局变量、静态变量等
代码段:可执行程序在内存中的映像,代码段是只读的,存放可执行文件指令
3.进程的启动
手动启动(加载)
前台:目录/程序名 程序运行于终端前台,终端不能做其它操作
后台:目录/程序名 & 程序执行于终端后台,终端还可以操作其它的程序
自动启动(加载)
程序在开机时自动运行
4.进程状态
进程是由操作系统内核调度运行,在调度过程中,进程的状态是不断发生变化的,这些状态包括:运行状态
等待状态、暂停状态、僵尸状态、退出状态。
运行状态:有两种,一是正在运行,二是处于就绪状态。就绪状态只要得到CPU就可以立即投入运行。
等待状态:有两类,可中断,不可中断。处于中断等待状态进程,即可以被信号中断,也可以被唤醒。
不可中断状态只有等待被唤醒,不可被中断
暂停状态:接到某个信号,就会暂时停止运行。
僵尸状态:表示进程结束但尚未消亡,一个进程结束运行就会处于僵尸状态,向父进程发送SIGCLD信号,父进程
调用wait为子进程的退出做最后的收尾工作。
5.进程的优先级
linux允许多个进程同时运行,但不可能实现真正的同时占用CPU,而是使用“时间片轮转”的进程调度方式,为每个
进程指派一定的运行时间。进程的优先级是由优先级别PR和进程的谦让值NI两个因素联合确定的。
6.进程的运行环境
程序的入口函数main,进程开始执行时都是从main函数开始。其中argv是参数数组,编程时可以直接访问命令行参数组。
用户在编程过程中,需要保存某个配置项的值,一个方法是将其写到文件中,另一个方法是定义成环境变量。
用户在登录shell时,会从两个位置获得环境变量的定义,一个是全局环境变量文件 /etc/profile 另一个是当前用户的环境变量文件
在命令行内增加环境变量
export 环境变量名=值
echo $环境变量名
unset $环境变量名
env
在命令行增加的环境变量,只在本次会话中有效,会话一旦退出,该环境变量将会失效。如果要永久增加环境变量,可以在.profile中写入
在程序中用函数增加环境变量
#include <stdlib.h>
1)获取环境变量
char *getenv(const char *name);
name 环境变量名
返回值 成功 环境变量值 失败 NULL(未定义环境变量)
2)设置环境变量
int putenv(char *string);
string 要设置的环境变量串,其格式为"环境变量名=值"
返回值 成功 0 失败 -1
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *buffer;
buffer = getenv ("CONFIG_PATH");
if(buffer==NULL) {
putenv("CONFIG_PATH=/etc");
}
printf("CONFIG_PATH=%s\n",getenv("CONFIG_PATH"));
return 0;
}
7.获取进程标识符函数
同一个程序可以有多个进程,每一个进程都有自已的PID
pid_t getpid() //获取进程PID
pid_t getppid() //获取进程的父进程ID
pid_t getpgrp() //获取进程组ID
例:
//---------------下午--------------------------------
(二)进程的创建与终止
1.创建子进程
#include <sys/types.h>
#include <unistd.h>
pid_t fork();
返回值:父进程和子进程ID,失败 -1
fork调用一次将返回两次,分别在父进程和子进程返回。
在父进程中,返回值为子进程的进程标识符。
在子进程中,返回值为0
fork调用成功后,将由一个进程产生两个进程,各占独自的内存空间。同时运行父进程和子进程。
创建子进程时,把原来所有的资源复制了一份,做为子进程的资源。子进程复制了父进程的数据段包括全局变量,但父子进程各有一份全局变量的拷贝。
所有各占独自的内存,因此不能通过全局变量在父子进程间通信。
子进程继承了父进程的所有资源,包括用户ID、用户组ID、有效用户ID、有效用户组ID、进程组ID、文件创建掩码和环境变量等。
2.退出进程
void exit(int status); //先检查文件是否被打开,把文件缓冲区内的数据写入文件,然后终止进程
void _exit(int status);//是直接终止进程,把退出地进程内存释放
status:是进程结束后的返回值
3. 等待进程结束
#include <linux/wait.h>
pid_t wait(int *status); //是阻塞方式
pid_t wait(pid_t pid,int *status,int options); //根据options确定是否阻塞
status:在此处获取的进程结束时的返回值,进程返回值的指针,前8位是子进程的返回值,后8位是操作系占用的位
pid :指定等待哪一个进程返回的值
options:等待返回信息的方式
WNOHANG 不阻塞模式
WUNTRACED 阻塞,只要进程暂停就返回状态
0 阻塞方式等待
例:
pid_t pid=fork();
int value;
if (pid==-1){
printf("fork err\n");
exit(0);
}
if (pid==0){
//子进程
printf("son------- pid =%d\n",getpid());
sleep(3);
exit(65);
}
else {
wait(&value);
printf("father ---- %d\n",value >> 8);
printf("father ---- %d\n",value &0xFF);
}
return 0;
例:
pid_t pid=fork();
int value;
if (pid==-1){
printf("fork err\n");
exit(0);
}
if (pid==0){
//子进程
printf("son------- pid =%d\n",getpid());
sleep(3);
exit(65);
}
else {
//wait(&value);
do{
pid=waitpid(pid,&value,WNOHANG);
printf("father ---- %d\n",value >> 8);
printf("father ---- %d\n",value &0xFF);
sleep(1);
}while(pid==0);
}
(三)其它创建进程的方式
创建子进程有三种方式:fork、exec、system
1.调用外部程序
int system(char *command);
command:要加载的外部程序调用
返回值:-1或127执行失败,其它 成功
例:调用其它程序
system("./play -f ./aaa.wav");
被调用者成为当前进程的子进程
通过system可以调用任何程序和指令,system首先fork子进程,然后调用exec执行新的shell,在shell中执行要执行的程序
2.exec系列函数
int execv(const char * path,char *const argv[]);
int execve(const char * path,char *const argv[],char *const envp[]);
int execvp(const char *file,char *const argv[]);
int execl(const char *path,const char *arg,...);
int execle(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
path:要执行的程序路径,这里是路径名,可以是绝对路径或相对路径
file:要执行的程序名,可以带路径
argv:命令行参数数组
envp:环境变量组。
arg: 程序第0个参数即程序名本身,相当于argv[0]
exec系统函数并不创建新进程,exec系列函数是清除父进程的可执行代码映像,用新程序的代码复盖调用exec的进程代码。
如果exec执行成功,进程将从新程序的main函数入口开始执行。
例:
execl("bin/ls","ls","-l",NULL);
char *para[]={"ls","-a",NULL};
execv("/bin/ls",para);
展开阅读全文