目录
一,进程标识:
在linux系统中,每个进程被创建时会被分配一个数据结构,称为进程控制块(PCB),它用来记录进程的外部特征。进程与PCB是一一对应得关系。通常PCB包含有以下信息:进程标识符,进程当前状态,进程相应程序和数据地址,进程资源清单,进程优先级,进程同步与通信机制,CPU现场保护区,进程所在队列PCB连接字等。其中进程标识符又称为进程ID,它是一个非负整数,用来唯一的表示一个进程。Linux系统中有专用的进程,进程ID 0是调度进程,常被称为交换进程,ID 0是init进程。
ps -aux //shell命令查看当前进程详情信息
如下所示,其中PID就是进程ID。
1,获取当前进程ID:
#include <sys/types>
#include <unistd.h>
pid_t getpid(void); //返回当前进程ID
实例如下: get_pid.c
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("The current process ID is %d",getpid());
return 0;
}
运行结果如下,返回当前进程ID:
二,进程控制:
1,进程的创建:
#include <sys/types>
#include <unistd.h>
pid_t fork(void);
fork函数用于派生出一个进程,若成功,父进程返回子进程得进程ID,子进程返回0,出错则返回-1。成功执行时,fork会返回两次。fork函数调用的作用是复制一个进程,由fork创建出得进程称为子进程,子进程从父进程得到了数据段和堆栈段得复制,并分配新的内存。对于只读代码段,通常使用共享内存的方式访问。
示例如下,fork函数创建一个进程:fork.c
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int count=0;
pid_t pid;
pid=fork(); //此时有两个进程同时运行
if(pid<0)
{
printf("some error!\n");
exit(1);
}
else if(pid==0) //子进程
{
printf("This is child process! and ID is %d\n",getpid());
}
else //父进程
{
printf("This is parent process,and ID is %d\n ",getpid());
}
return 0;
}
运行结果如下:
#include <sys/types>
#include <unistd.h>
pid_t vfork(void);
vfork()函数于fork函数类似,但区别在于:vfork无需全部复制父进程的数据段,在子进程进程未调用exec或exit函数之前,父子进程共享数据段,vfork函数中子进程先运行,父进程挂起,直到子进程运行到exit或exec后,父子进程执行次序不在有限制。严格意义上讲,vfork创建的只是线程,没有独立得内存资源。
2,exec函数族:
fork()函数创建的子进程和父进程几乎完全相同,要改变子进程,执行另一个程序,利用以下函数:
#include <unistd.h>
int execl(const char *pathname,const char *arg,...);
int execlp(const char *filename,const char *arg,...);
int execle(const char *pathname,const char *arg,...,char *const envp[]);
int execv(const char *pathname,char *const argv[]);
int execvp(const char *filename,char *const argv[]);
int execve(const char *pathname,char *const argv[],char *const envp[]);
六个函数若成功无返回,出错返回-1。
函数名含有" l",其参数个数不定,参数由所调用程序命令行列表组成,最后以NULL结束。
函数名含有" v",使用字符串数组指针argv指向参数列表,同含" l"类似,最后以NULL结束。
函数名含有"p"的自动在环境变量PATH指定的路径中搜索要执行的程序,故其第一个参数为filename.。
函数名含有"e"的,其参数envp是一个字符串数组指针,用于指定环境变量,可以有用户自定设置子进程的环境变量,envp数组也要以NULL结尾。
exce函数族作用是根据指定文件名找到可执行文件,并用它取代调用进程的内容,就是在调用进程内执行一个可执行文件,该文件可以是二进制文件也可以是脚本文件。
示例如下:exec.c
#include <stdio.h>
#include <unistd.h>
/*
echo 命令打印紧跟的命令行参数
env 查看所有的环境变量
*/
int main()
{
char *envp[]={"PATH=/tmp","USER=root","STATUS=testing",NULL};
char *argv_execv[]={"echo","excuted by execv","NULL"};
char *argv_execvp[]={"echo","excuted by execvp","NULL"};
char *argv_execve[]={"env","NULL"};
if(fork()==0)
{
if(execl("/usr/bin/echo","echo","excuted by excel",NULL))
perror("Err on excel\n"); //perror出错时,把出错原因输出到标准错误
}
if(fork()==0)
{
if(execlp("echo","echo","excuted by execlp",NULL))
perror("Err on execlp\n");
}
if(fork()==0)
{
if(execle("/usr/bin/env","echo","excuted by excele",NULL,envp))
perror("Err on excele\n");
}
if(fork()==0)
{
if(execv("/usr/bin/echo",argv_execv))
perror("Err on excev\n");
}
if(fork()==0)
{
if(execvp("echo",argv_execvp))
perror("Err on excevp\n");
}
if(fork()==0)
{
if(execve("/usr/bin/env",argv_execve,envp))
perror("Err on execve\n");
}
return 0;
}
3,终止进程
#include <stdlib.h>
void exit(int status);
statu为进程结束时的状态,一般0标识没有意外的正常结束。
4,进程等待:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
pid_t waitpid(pid_t pid,int* status,int options)
成功返回进程ID,出错返回-1。在调用exit后,进程并非完全消失,还留下了一个称为僵尸进程的数据结构,此时,要完全销毁掉该进程,调用wait函数立即阻塞自己,直到找到该僵尸进程并销毁它。可以是进程组之间的等待,也可以是父进程对子进程的等待。
status保存被收集进程退出时的状态。一般不关心直接取NULL。
waitpid函数参数pid等待指定进程:pid>0时,等待进程ID为pid的进程,pid=0时,等待同一进程组任意子进程,pid=-1,等待任意子进程退出,pid<-1,等待指定进程组任意子进程,指定进程组ID为pid绝对值,options可选参数为WNOHANG时,即使无子进程退出,他不会一直等待,会立即返回。
实例代码如下: wait.c
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pc,pr;
if((pc=fork())<0)
{
printf("some error\n");
exit(1);
}
if(pc==0)
{
printf("This is child process,ID is %d\n",getpid());
sleep(5); //睡眠5s
}
else
{
pr=wait(NULL); //等待子进程苏醒过来,才会执行
printf("Catch the child's ID is %d\n",pr);
}
return 0;
}
观察到第一句显示到频幕上后,间隔5秒后,第二句才显示: