Linux进程控制
程序和进程
程序(program)是存放在磁盘文件中的可执行文件。
进程和进程ID
程序的执行实例被称为进程(process)也可以称为任务。某些操作系统用任务表示正被执行的程序。
每个Linux进程都一定有一个唯一的数字标识符,称为进程ID(process ID)。进程ID总是一非负整数(大于1的)。
Linux下的进程结构
Linux系统是一个多进程的系统,进程之间具有并行性、互不干扰的特点。Linux中进程包含3个段,分别为“代码段(程序的流程)”,“数据段(系统使用有关重要的数据——全局变量和静态变量)”和“堆栈段(存放基本的临时变量)”。
哪些变量放在堆中,哪些变量放在栈中?
静态变量或是全局变量放在堆中。
子函数入口的临时变量或是局部变量放在栈中。
“数据段”存放全局变量、常数以及动态数据分配的空间(malloc函数取得的空间);“代码段”存放程序代码;“堆栈段”存放子程序的返回地址、子程序的参数以及程序的局部变量。
init进程
A、进程ID为1通常是init进程,在自举过程结束时由内核调用。
B、init进程绝不会终止。
C、它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。
获取进程标识
#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void);返回:调用进程的进程ID
pid_t getppid(void);返回:调用进程的父进程ID
uid_t getuid(void);返回:调用进程的实际用户ID
uid_t geteuie(void);返回:调用进程的有效用户ID
gid_t getgid(void);返回:调用进程的实际组ID
gid_t getegid(void);返回:调用进程的有效组ID
fork()函数
表示由当前进程生成一个进程出来
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
返回:子进程中为0,父进程中为子进程ID,出错为-1
返回值有两个,大于0代表父进程中,等于0子进程中创建
由fork创建的新进程被称为子进程(child process)。
该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是子进程的进程ID。
一般来说,在fork之后是父进各程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。
使用fork函数得到的子进程是父进程的处继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
父、子进程之间的区别是:
A、fork的返回值;
B、进程ID、不同的父进程ID;
C、子进程的tms_utime,tms_stime,tms_cutime以及tms_ustime设置为0;运行时间,产生时间,调整时间,u代表毫秒数,s代表秒数
D、父进程设置的锁,子进程不继承;
E、子进程的未决告警被清除;
F、子进程的未决信号集设置为空集。
source Insight软件
fork.c
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
pid_t result;
result=fork();
if(result==1){
perror("fork");
exit;
}
else if(result==0){
printf("The return value if %d/nIn child process!!/nMyPID is %d/n",result,getpid());
}else {
printf("The return value is %d/nIn father process!!/nMy PID is %d/n",result,getpid());
}
}
vfork()函数
vfork函数的调用序列和返回值与fork相同,但两者的语义不同。现在很多的实现并不做一个父进程数据段和堆的完全拷贝,因为在fork之后经常跟随着exec。作为替代,使用了在写时复制(copy-on-Write,COW)的技术。这些区域由父、子进程共享,而且内核将它们的存取许可权改变为只读的。如果有进程试图修改这些区域,则内核为有关部分,典型的是虚存系统中的“页”,做一个拷贝。如:uclinux中的进程创建。
exec函数
在用fork函数创建了子进程以后,子进程往往要调用一种exec函数以执行另一个程序。
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。
#include<unistd.h>
int execl(const char * pathname,const char * arg0,..../*(char *) 0 */);
int execv(const char * pathname, char *const a rgv[]);
int execle(const char * pathname, const char * arg0,..../*(char *)0,char *const envp[] */);
int execve(const char * pathname char * const argv[],char *const envp[]);
int execlp(const char* pathname,const char * arg0 ,.../*(char *)0*/);
int execvp(const char * pahtname, char *const argv[]);
六个函数返回:若出现则为-1,若成功则不返回
说明:
(1)名字中带有P的(execlp,execvp);名字中带P的exec函数以一个完整的路径名字当作第一个参数,而其它四个参数将以可执行文件的名字作为参数,他们会在环境变量PATH指定的路径中寻找可执行文件。
(2)名字中带有l的(execl,execlp,execle)):表示命令行参数使用list(列表)的形式传递,以一个NULL结尾。
(3)名字中带有V的(execv,execvp,execve):表示命名行参数以vertor(向量数组)的形式传递。
(4) 名字中带有e的(execve,execle):表示在执行命令时,使用参数envp中指定的环境变量。
实例代码如下:
execlp.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
if(fork()==0){
if(execlp("ps","ps","-ef",NULL)<0)
perror("execlp error!");
}
return 0;
}
execl.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
if(fork()==0){
if(execl("/bin/ps","ps","-ef",NULL)<0)
perror("execl error!");
}
return 0;
}
execle.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
char *envp[]={"PATH=/tmp","USER=xuepeng",NULL};
if(fork()==0){
if(ececle("/bin/env","env",NULL,envp)<0)
perror("execle error!");
}
return 0;
}