欢迎进入Linux社区论坛,与200万技术人员互动交流 >>进入
1. Linux进程概述
进程是程序的执行实例,它是Linux的基本调度单位。一个进程由如下元素组成:
程序的当前上下文,即程序的当前执行状态;
程序的当前执行目录
程序访问的文件和目录
程序的访问权限,比如它的文件模式和所有权
内存和其他分配给进程的系统资源
内核使用进程来控制对CPU和其他系统资源的访问,并且使用进程来决定在CPU上运行哪个程序,运行多久以及采用什么特性运行它。内核的调度器负责在所有的进程间分配CPU执行时间,称为时间片(time slice),它轮流在每个进程分得的时间片用完后从进程那里抢回控制权。
1.1. 进程标1.2. 识
OS会为每个进程分配一个唯一的整型ID,做为进程的标识号(pid)。进程除了自身的ID外,还有父进程ID(ppid),所有进程的祖先进程是同一个进程,它叫做init进程,ID为1,init进程是内核自举后的一个启动的进程。init进程负责引导系统、启动守护(后台)进程并且运行必要的程序。
进程的pid和ppid可以分别通过函数getpid()和getppid()获得。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf(“pid:%d ppid:%d\n”,getpid(),getppid());
return 0;
}
1.3. 进程的用户ID与组ID(进程的运行身份)
进程在运行过程中,必须具有一类似于用户的身份,以便进行进程的权限控制,缺省情况下,哪个登录用户运行程序,该程序进程就具有该用户的身份。例如,假设当前登录用户为gotter,他运行了ls程序,则ls在运行过程中就具有gotter的身份,该ls进程的用户ID和组ID分别为gotter和gotter所属的组。这类型的ID叫做进程的真实用户ID和真实组ID。真实用户ID和真实组ID可以通过函数getuid()和getgid()获得。
与真实ID对应,进程还具有有效用户ID和有效组ID的属性,内核对进程的访问权限检查时,它检查的是进程的有效用户ID和有效组ID,而不是真实用户ID和真实组ID。缺省情况下,用户的(有效用户ID和有效组ID)与(真实用户ID和真实组ID)是相同的。有效用户id和有效组id通过函数geteuid()和getegid()获得。
示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf(“uid:%d gid:%d euid:%d egid:%d\n”,
getuid(),getgid(),geteuid(),getegid());
return 0;
}
开始时登录用户为ghaha(uid为500):
shell>id
uid=500(ghaha) gid=500(ghaha) groups=500(ghaha)
编译生成可执行文件a.out,程序文件的属性可能为:
-rwxrwxr-x 1 ghaha ghaha 12132 Oct 7 09:26 a.out
执行结果可能为:
shell>a.out
uid:500 gid:500 euid:500 egid:500
现在将a.out的所有者可执行属性改为s
shell>chmod u+s a.out
shell>ll
-rwsrwxr-x 1 ghaha ghaha 12132 Oct 7 09:26 a.out
此时改另外一个用户gotter登录并运行程序a.out
shell>id
uid=502(gotter) gid=502(gotter) groups=502(gotter)
shell>a.out
uid:502 gid:502 euid:500 egid:502
可以看到,进程的有效用户身份变为了ghaha,而不是gotter了,这是因为文件a.out的访问权限的所有者可执行为设置了s的属性,设置了改属性以后,用户运行a.out时,a.out进程的有效用户身份将不再是运行a.out的用户,而是a.out文件的所有者。
s权限最常见的例子是
/usr/bin/passwd程序,它的权限位为
shell>ll /usr/bin/passwd
-r-s–x–x 1 root root 16336 Feb 13 2003 /usr/bin/passwd
说明任何一个用户运行该程序时,该程序的有效身份都将是root,这样passwd程序才有权限读取/etc/passwd文件的信息。
2. 进程的创建
Linux下有四类创建子进程的函数:
system(),fork(),exec*(),popen()
2.1. system函数
原型:
#include <stdlib.h>
int system(const char *string);
system函数通过调用shell程序/bin/sh -c来执行string所指定的命令,该函数在内部是通过调用execve(“/bin/sh”,..)函数来实现的。通过system创建子进程后,原进程和子进程各自运行,相互间关联较少。如果system调用成功,将返回0。
示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
system(“ls -l”);
return 0;
}
示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
system(“ls -l”);
return 0;
}
2.2. fork函数
原型:
#include <unistd.h>
pid_t fork(void);
fork函数创建子进程的过程为:在进程调用fork时,系统根据现有的进程的特性几乎是完全意义的复制出一个新的子进程,复制的内容包括原进程的当时内存空间的代码、变量、对象等所有内存状态,真实和有效uid和gid,环境、资源限制、打开的文件等。通过这种复制方式创建出子进程后,原有进程和子进程都从函数fork返回,各自继续往下运行,但是原进程的fork返回值于子进程的fork返回值不同,在原进程中,fork返回子进程的pid,而在进程中,fork返回0,如果fork返回负值,表示创建子进程失败。
示例:
// TestFork.cpp {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf(“Parent process id:%d\n”,getpid());
pid_t iRet=fork();
if(iRet<0){
printf(“Create child process fail!\n”);
}else if(iRet==0){
printf(“I’m child process,and id:%d ppid:%d\n”,
getpid(),getppid());
}else{
printf(“Create child process success,child id:%d\n”,iRet);
}
return 0;
}
// TestFork.cpp }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
shell>g++ TestFork..cpp
shell>a.out
Parent process id:6059
I’m child process,and id:6060 ppid:6059
Create child process success,child id:6060
[1][2]
哪里有意志存在,哪里就会有出路。