这篇文章介绍的内容包括
1.Linux进程--进程标识号
2.进程控制--进程创建
1.Linux进程--进程标识号
进程(Process)是一个程序在其自身的虚拟地址空间中的一次执行活动。多个程序并发执行,可以提高系统的资源利用率和吞吐量。
进程和程序:
程序只是一个静态的数据和指令集合,而进程是一个程序的动态执行过程,具有生命周期,是动态的产生和消亡的。
进程是资源申请、调度和独立运行的单位,因此它使用系统中的运行资源,而程序不占用系统的运行资源。
程序与进程无一一对应关系。一个程序可以由多个进程所共用,即一个程序在运行过程中可以产生多个进程;一个进程在生命周期内可以顺序执行若干个程序。
Linux中的进程,每个进程有一个识别号,PID(Process ID)。系统启动后的第一个进程是init,PID是1。init是唯一一个由系统内核直接运行的进程。新的进程可以用系统调用fork产生,从一个旧进程中分出一个新进程来,旧进程是新进程的父进程,新进程是产生他的旧进程的子进程,除了init之外,每一个进程都有父进程。
系统启动后,init进程会创建login进程等待用户登录,当用户登录系统后,login进程就会为用户启动shell进程,此后用户运行的进程都是由shell衍生出来的。
除了PID外,每个进程还有另外4个识别号:
实际用户识别号(real user ID),
实际组识别号,
有效用户识别号(effect user ID),
有效组识别号。
实际用户识别号和实际组识别号用于识别正在运行此进程的用户和组,也即运行此进程的用户的识别号和组的识别号。有效用户识别号和有效组识别号确定一个进程对其访问的文件的权限和优先权。
2.进程控制--进程创建
所谓控制,就是将进程的一个完整的生命周期(进程的诞生,繁衍与消亡)完全的控制在在程序员的手中。
刚才提到,在Linux 环境下启动进程时,系统会自动分配一个唯一的数值给这个进程,这个数值就是这个进程的标识符。
在Linux 中主要的进程标识符有进程号(PID),和它的父进程(PPID)(parents process ID)。PID和PPID都是非零的正整数,在linux中获得当前的进程PID和PPID的系统调用函数为getpid和getppid 。
getpid系统调用说明:
所需头文件 | #include <unistd.h> |
函数功能: |
取得当前进程的进程号 |
函数原型: |
int getpid(); |
函数传入值: |
无 |
函数返回 |
成功返回当前进程的进程号,失败将错误包含在perror中 |
备注: |
|
getppid系统调用说明:
所需头文件 | #include <unistd.h> |
|
函数功能: |
取得当前进程的父进程号 |
|
函数原型: |
int getppid(); |
|
函数传入值: |
无 |
|
函数返回 |
成功返回当前进程的父进程号,失败将错误包含在perror中 |
|
备注: |
|
下面给出一个例子显示进程的标示符和父进程的标示符
#include <stdio.h> #include <unistd.h> int main(){ printf("系统分配的进程号是:%d\n",getpid()); printf("系统分贝的父进程号是:%d\n",getppid()); return 0; }
进程的创建
fork系统调用
fork系统调用在linux中视最重要的系统调用之一,是我们实现进程控制的最常用的系统调用,并且,很多其他的系统调用也要使用到它!
Fork系统调用说明
所需文件头: | #include <unistd> |
函数功能: |
建立一个新进程 |
函数原型: |
Pid_t fork(void); |
函数传入之值 |
无 |
函数返回值 |
执行成功则建立一个新的进程,在子进程中则返回0,在父进程中回返回新建子进程的进程号(PID),失败则返回-1 |
备注: |
新进程的信息是复制而来,而非指相同的内存空间,因子进程对这些变量的修改和父进程并不同步。 |
我们知道,进程必须有依托生存的环境,也就是其父进程,每一个进程必须在其父进程的照顾下才能生存,一旦失去其依托的进程,那么进程就失去了原有的功能,成为一个即浪费资源,占用空间而又没有任何贡献的行尸走肉——我们叫它——僵死进程。
fork函数的作用是在其进程中创建一个新的进程,这个新的进程不会取代原来的进程,而是以当前进程的一个子进程而存在的。这个子进程会有一个新的进程标识符pid。并且这个子进程会继承父进程的一切!
什么是叫继承父进程的一切呢?就是克隆父进程的所有,包括父进程的代码,父进程正在执行的状态,父进程的工作目录,父进程的所有资源等等,一切的一切,fork系统调用会全部的复制下来。
我们在来看fork函数的返回值,它好像有两个返回值,其实是一个,在调用fork系统调用后,原先的进程空间从一个变成两个,而fork函数在不同的进程空间会返回不同的值,在子进程的进程空间中,fork返回 0 ,而在父进程的进程中则返回刚刚新建的子进程的进程标识符,也就是子进程的pid。
下面给出几个实例
实例1.该实例程序说明fork系统函数执行成功后,生成的子进程从程序中间开始执行程序,此外,还说明父子进程的返回值问题
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { pid_t pid; printf("Start of fork testing. \n"); pid=fork(); printf("Return of fork success:pid=%d\n",pid); return 0; }
实例2.该程序说明子进程对父进程数据段的继承性
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define SIZE 1024 #define KEY 99 int shmid; int j=5; int main() { int i, *pint; pid_t pid; char *addr; i=10; shmid=shmget(SIZE,KEY,IPC_CREAT|0777); pint=shmat(shmid,0,0); *pint=100; printf("Start of fork testing \n"); pid=fork(); i++;j++;*pint+=1; printf("Return of fork success:pi=%d\n",pid); printf("i=%d, j=%d\n",i,j); printf("*pint=%d\n",*pint); return 0; }
实例3.该程序说明fork系统调用后父子进程间共享文件指针的问题,功能为复制文件
#include <sys/types.h> #include <unistd.h> #include <fcntl.h> int rfd, wfd; char c; int main(int argc, char*argv[]) { if(argc!=3) { printf("Usage %s sourcesfiel destfile. \n",argv[0]); return 1; } if((rfd=open(argv[1], O_RDONLY))==-1) { printf("openf file %s failed.\n",argv[1]); return 2; } if((wfd=creat(argv[2],0666))==-1) { printf("create file %s failed.\n",argv[2]); return 3; } fork(); for(;;) { if(read(rfd,&c,1)!=1) return 1; write(wfd,&c,1); } return 0; }
下面,我们将看到一个有意思的东西,但是,这必须建立在我们理解了fork系统调用之后!
我们知道fork会创建子进程,一个和父进程一样但是完全独立的子进程。那么我们下面的代码:
for(;;) fork();
这是一个无限循环创建子进程的语句,我们想象一下,一旦我们把这样的语句写在一个程序中并且执行程序,那么那个进程就会在无限循环中创建子进程。
那么系统资源很快就会被不断创建的子进程占满,使系统无法继续正常工作,那么,以上就是一个简单的系统炸弹!
很简单,一句代码,你就可以做步入了黑客的殿堂!
当然,这里真正的黑客还差的很远,上面的程序破解起来也很简单,只要系统管理员对用户可以调用的进程数量进行限制就是破解!
vfork()
创建的子进程结束后需要运行exit()或exec父进程才会运行,否则就会造成死锁
下面给出一个简单的vfork调用
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int global=4; int main() { pid_t pid; int vari=5; if((pid=vfork())<0) { printf("vfork error.\n"); return 1; } else if(pid==0) { global++; vari--; printf("Child changed the vari and global \n"); _exit(0); } else printf("Parent didn't changed the vari and global\n"); printf("global =%d ,vari=%d \n",global,vari); return 0; }