2)Linux程序设计入门--进程介绍 mmsLinux联盟
Linux下进程的创建 mmsLinux联盟
前言: mmsLinux联盟
这篇文章是用来介绍在Linux下和进程相关的各个概念.我们将会学到: mmsLinux联盟
进程的概念 mmsLinux联盟
进程的身份 mmsLinux联盟
进程的创建 mmsLinux联盟
守护进程的创建 mmsLinux联盟
---------------------------------------------------------------------------- mmsLinux联盟
---- mmsLinux联盟
1。进程的概念 mmsLinux联盟
Linux操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命 mmsLinux联盟
令.那么操作系统是怎么实现多用户的环境呢? 在现代的操作系统里面,都有程序和进程 mmsLinux联盟
的概念.那么什么是程序,什么是进程呢? 通俗的讲程序是一个包含可以执行代码的文件 mmsLinux联盟
,是一个静态的文件.而进程是一个开始执行但是还没有结束的程序的实例.就是可执行文 mmsLinux联盟
件的具体实现. 一个程序可能有许多进程,而每一个进程又可以有许多子进程.依次循环 mmsLinux联盟
下去,而产生子孙进程. 当程序被系统调用到内存以后,系统会给程序分配一定的资源(内 mmsLinux联盟
存,设备等等)然后进行一系列的复杂操作,使程序变成进程以供系统调用.在系统里面只 mmsLinux联盟
有进程没有程序,为了区分各个不同的进程,系统给每一个进程分配了一个ID(就象我们的 mmsLinux联盟
身份证)以便识别. 为了充分的利用资源,系统还对进程区分了不同的状态.将进程分为新 mmsLinux联盟
建,运行,阻塞,就绪和完成五个状态. 新建表示进程正在被创建,运行是进程正在运行,阻 mmsLinux联盟
塞是进程正在等待某一个事件发生,就绪是表示系统正在等待CPU来执行命令,而完成表示 mmsLinux联盟
进程已经结束了系统正在回收资源. 关于进程五个状态的详细解说我们可以看《操作系 mmsLinux联盟
统》上面有详细的解说。 mmsLinux联盟
2。进程的标志 mmsLinux联盟
上面我们知道了进程都有一个ID,那么我们怎么得到进程的ID呢?系统调用getpid可 mmsLinux联盟
以得到进程的ID,而getppid可以得到父进程(创建调用该函数进程的进程)的ID. mmsLinux联盟
#include <unistd> mmsLinux联盟
pid_t getpid(void); mmsLinux联盟
pid_t getppid(void); mmsLinux联盟
进程是为程序服务的,而程序是为了用户服务的.系统为了找到进程的用户名,还为进程和 mmsLinux联盟
用户建立联系.这个用户称为进程的所有者.相应的每一个用户也有一个用户ID.通过系统 mmsLinux联盟
调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是 mmsLinux联盟
进行保护的,为了获取一定资源进程还有一个有效用户ID.这个ID和系统的资源使用有关 mmsLinux联盟
,涉及到进程的权限. 通过系统调用geteuid我们可以得到进程的有效用户ID. 和用户ID mmsLinux联盟
相对应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效 mmsLinux联盟
组ID mmsLinux联盟
#include <unistd> mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
mmsLinux联盟
mmsLinux联盟
uid_t getuid(void); mmsLinux联盟
uid_t geteuid(void); mmsLinux联盟
gid_t getgid(void); mmsLinux联盟
git_t getegid(void); mmsLinux联盟
有时候我们还会对用户的其他信息感兴趣(登录名等等),这个时候我们可以调用getpwui mmsLinux联盟
d来得到. mmsLinux联盟
struct passwd { mmsLinux联盟
char *pw_name; /* 登录名称 */ mmsLinux联盟
char *pw_passwd; /* 登录口令 */ mmsLinux联盟
uid_t pw_uid; /* 用户ID */ mmsLinux联盟
gid_t pw_gid; /* 用户组ID */ mmsLinux联盟
char *pw_gecos; /* 用户的真名 */ mmsLinux联盟
char *pw_dir; /* 用户的目录 */ mmsLinux联盟
char *pw_shell; /* 用户的SHELL */ mmsLinux联盟
}; mmsLinux联盟
#include <pwd.h> mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
mmsLinux联盟
mmsLinux联盟
struct passwd *getpwuid(uid_t uid); mmsLinux联盟
下面我们学习一个实例来实践一下上面我们所学习的几个函数: mmsLinux联盟
#include <unistd.h> mmsLinux联盟
#include <pwd.h> mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
#include <stdio.h> mmsLinux联盟
int main(int argc,char **argv) mmsLinux联盟
{ mmsLinux联盟
pid_t my_pid,parent_pid; mmsLinux联盟
uid_t my_uid,my_euid; mmsLinux联盟
gid_t my_gid,my_egid; mmsLinux联盟
struct passwd *my_info; mmsLinux联盟
my_pid=getpid(); mmsLinux联盟
parent_pid=getppid(); mmsLinux联盟
my_uid=getuid(); mmsLinux联盟
my_euid=geteuid(); mmsLinux联盟
my_gid=getgid(); mmsLinux联盟
my_egid=getegid(); mmsLinux联盟
my_info=getpwuid(my_uid); mmsLinux联盟
printf("Process ID:%ld\n",my_pid); mmsLinux联盟
printf("Parent ID:%ld\n",parent_pid); mmsLinux联盟
printf("User ID:%ld\n",my_uid); mmsLinux联盟
printf("Effective User ID:%ld\n",my_euid); mmsLinux联盟
printf("Group ID:%ld\n",my_gid); mmsLinux联盟
printf("Effective Group ID:%ld\n",my_egid); mmsLinux联盟
if(my_info) mmsLinux联盟
{ mmsLinux联盟
printf("My Login Name:%s\n" ,my_info->pw_name); mmsLinux联盟
printf("My Password :%s\n" ,my_info->pw_passwd); mmsLinux联盟
printf("My User ID :%ld\n",my_info->pw_uid); mmsLinux联盟
printf("My Group ID :%ld\n",my_info->pw_gid); mmsLinux联盟
printf("My Real Name:%s\n" ,my_info->pw_gecos); mmsLinux联盟
printf("My Home Dir :%s\n", my_info->pw_dir); mmsLinux联盟
printf("My Work Shell:%s\n", my_info->pw_shell); mmsLinux联盟
} mmsLinux联盟
} mmsLinux联盟
3。进程的创建 mmsLinux联盟
创建一个进程的系统调用很简单.我们只要调用fork函数就可以了. mmsLinux联盟
#include <unistd.h> mmsLinux联盟
mmsLinux联盟
mmsLinux联盟
pid_t fork(); mmsLinux联盟
当一个进程调用了fork以后,系统会创建一个子进程.这个子进程和父进程不同的地方只 mmsLinux联盟
有他的进程ID和父进程ID,其他的都是一样.就象符进程克隆(clone)自己一样.当然创建 mmsLinux联盟
两个一模一样的进程是没有意义的.为了区分父进程和子进程,我们必须跟踪fork的返回 mmsLinux联盟
值. 当fork掉用失败的时候(内存不足或者是用户的最大进程数已到)fork返回-1,否则f mmsLinux联盟
ork的返回值有重要的作用.对于父进程fork返回子进程的ID,而对于fork子进程返回0.我 mmsLinux联盟
们就是根据这个返回值来区分父子进程的. 父进程为什么要创建子进程呢?前面我们已经 mmsLinux联盟
说过了Linux是一个多用户操作系统,在同一时间会有许多的用户在争夺系统的资源.有时 mmsLinux联盟
进程为了早一点完成任务就创建子进程来争夺资源. 一旦子进程被创建,父子进程一起从 mmsLinux联盟
fork处继续执行,相互竞争系统的资源.有时候我们希望子进程继续执行,而父进程阻塞直 mmsLinux联盟
到子进程完成任务.这个时候我们可以调用wait或者waitpid系统调用. mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
#include <sys/wait.h> mmsLinux联盟
mmsLinux联盟
mmsLinux联盟
pid_t wait(int *stat_loc); mmsLinux联盟
pid_t waitpid(pid_t pid,int *stat_loc,int options); mmsLinux联盟
wait系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号.如果 mmsLinux联盟
没有父进程没有子进程或者他的子进程已经结束了wait回立即返回.成功时(因一个子进 mmsLinux联盟
程结束)wait将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc是子进程的 mmsLinux联盟
退出状态.子进程调用exit,_exit 或者是return来设置这个值. 为了得到这个值Linux定 mmsLinux联盟
义了几个宏来测试这个返回值. mmsLinux联盟
WIFEXITED:判断子进程退出值是非0 mmsLinux联盟
WEXITSTATUS:判断子进程的退出值(当子进程退出时非0). mmsLinux联盟
WIFSIGNALED:子进程由于有没有获得的信号而退出. mmsLinux联盟
WTERMSIG:子进程没有获得的信号号(在WIFSIGNALED为真时才有意义). mmsLinux联盟
waitpid等待指定的子进程直到子进程返回.如果pid为正值则等待指定的进程(pid).如果 mmsLinux联盟
为0则等待任何一个组ID和调用者的组ID相同的进程.为-1时等同于wait调用.小于-1时等 mmsLinux联盟
待任何一个组ID等于pid绝对值的进程. stat_loc和wait的意义一样. options可以决定 mmsLinux联盟
父进程的状态.可以取两个值 WNOHANG:父进程立即返回当没有子进程存在时. WUNTACHE mmsLinux联盟
D:当子进程结束时waitpid返回,但是子进程的退出状态不可得到. mmsLinux联盟
父进程创建子进程后,子进程一般要执行不同的程序.为了调用系统程序,我们可以使用系 mmsLinux联盟
统调用exec族调用.exec族调用有着5个函数. mmsLinux联盟
#include <unistd.h> mmsLinux联盟
int execl(const char *path,const char *arg,...); mmsLinux联盟
int execlp(const char *file,const char *arg,...); mmsLinux联盟
int execle(const char *path,const char *arg,...); mmsLinux联盟
int execv(const char *path,char *const argv[]); mmsLinux联盟
int execvp(const char *file,char *const argv[]): mmsLinux联盟
exec族调用可以执行给定程序.关于exec族调用的详细解说可以参考系统手册(man exec mmsLinux联盟
l). 下面我们来学习一个实例.注意编译的时候要加 -lm以便连接数学函数库. mmsLinux联盟
#include <unistd.h> mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
#include <sys/wait.h> mmsLinux联盟
#include <stdio.h> mmsLinux联盟
#include <errno.h> mmsLinux联盟
#include <math.h> mmsLinux联盟
void main(void) mmsLinux联盟
{ mmsLinux联盟
pid_t child; mmsLinux联盟
int status; mmsLinux联盟
printf("This will demostrate how to get child status\n"); mmsLinux联盟
if((child=fork())==-1) mmsLinux联盟
{ mmsLinux联盟
printf("Fork Error :%s\n",strerror(errno)); mmsLinux联盟
exit(1); mmsLinux联盟
} mmsLinux联盟
else if(child==0) mmsLinux联盟
{ mmsLinux联盟
int i; mmsLinux联盟
printf("I am the child:%ld\n",getpid()); mmsLinux联盟
for(i=0;i<1000000;i++) sin(i); mmsLinux联盟
i=5; mmsLinux联盟
printf("I exit with %d\n",i); mmsLinux联盟
exit(i); mmsLinux联盟
} mmsLinux联盟
while(((child=wait(&status))==-1)&(errno==EINTR)); mmsLinux联盟
if(child==-1) mmsLinux联盟
printf("Wait Error:%s\n",strerror(errno)); mmsLinux联盟
else if(!status) mmsLinux联盟
printf("Child %ld terminated normally return status is zero\n", mmsLinux联盟
child); mmsLinux联盟
else if(WIFEXITED(status)) mmsLinux联盟
printf("Child %ld terminated normally return status is %d\n", mmsLinux联盟
child,WEXITSTATUS(status)); mmsLinux联盟
else if(WIFSIGNALED(status)) mmsLinux联盟
printf("Child %ld terminated due to signal %d znot caught\n", mmsLinux联盟
child,WTERMSIG(status)); mmsLinux联盟
} mmsLinux联盟
strerror函数会返回一个指定的错误号的错误信息的字符串. mmsLinux联盟
4。守护进程的创建 mmsLinux联盟
如果你在DOS时代编写过程序,那么你也许知道在DOS下为了编写一个常驻内存的程序 mmsLinux联盟
我们要编写多少代码了.相反如果在Linux下编写一个"常驻内存"的程序却是很容易的.我 mmsLinux联盟
们只要几行代码就可以做到. 实际上由于Linux是多任务操作系统,我们就是不编写代码 mmsLinux联盟
也可以把一个程序放到后台去执行的.我们只要在命令后面加上&符号SHELL就会把我们的 mmsLinux联盟
程序放到后台去运行的. 这里我们"开发"一个后台检查邮件的程序.这个程序每个一个指 mmsLinux联盟
定的时间回去检查我们的邮箱,如果发现我们有邮件了,会不断的报警(通过机箱上的小喇 mmsLinux联盟
叭来发出声音). 后面有这个函数的加强版本加强版本 mmsLinux联盟
后台进程的创建思想: 首先父进程创建一个子进程.然后子进程杀死父进程(是不是很无 mmsLinux联盟
情?). 信号处理所有的工作由子进程来处理. mmsLinux联盟
#include <unistd.h> mmsLinux联盟
#include <sys/types.h> mmsLinux联盟
#include <sys/stat.h> mmsLinux联盟
#include <stdio.h> mmsLinux联盟
#include <errno.h> mmsLinux联盟
#include <fcntl.h> mmsLinux联盟
#include <signal.h> mmsLinux联盟
/* Linux 的默任个人的邮箱地址是 /var/spool/mail/用户的登录名 */ mmsLinux联盟
#define MAIL "/var/spool/mail/hoyt" mmsLinux联盟
/* 睡眠10秒钟 */ mmsLinux联盟
mmsLinux联盟
mmsLinux联盟
#define SLEEP_TIME 10 mmsLinux联盟
main(void) mmsLinux联盟
{ mmsLinux联盟
pid_t child; mmsLinux联盟
if((child=fork())==-1) mmsLinux联盟
{ mmsLinux联盟
printf("Fork Error:%s\n",strerror(errno)); mmsLinux联盟
exit(1); mmsLinux联盟
} mmsLinux联盟
else if(child>0) mmsLinux联盟
while(1); mmsLinux联盟
if(kill(getppid(),SIGTERM)==-1) mmsLinux联盟
{ mmsLinux联盟
printf("Kill Parent Error:%s\n",strerror(errno)); mmsLinux联盟
exit(1); mmsLinux联盟
} mmsLinux联盟
{ mmsLinux联盟
int mailfd; mmsLinux联盟
while(1) mmsLinux联盟
{ mmsLinux联盟
if((mailfd=open(MAIL,O_RDONLY))!=-1) mmsLinux联盟
{ mmsLinux联盟
fprintf(stderr,"%s","\007"); mmsLinux联盟
close(mailfd); mmsLinux联盟
} mmsLinux联盟
sleep(SLEEP_TIME); mmsLinux联盟
} mmsLinux联盟
} mmsLinux联盟
} mmsLinux联盟
你可以在默认的路径下创建你的邮箱文件,然后测试一下这个程序.当然这个程序还有很 mmsLinux联盟
多地方要改善的.我们后面会对这个小程序改善的,再看我的改善之前你可以尝试自己改 mmsLinux联盟
善一下.比如让用户指定邮相的路径和睡眠时间等等.相信自己可以做到的.动手吧,勇敢 mmsLinux联盟
的探险者. mmsLinux联盟
好了进程一节的内容我们就先学到这里了.进程是一个非常重要的概念,许多的程序都会 mmsLinux联盟
用子进程.创建一个子进程是每一个程序员的基本要求! mmsLinux联盟