您的位置:首页 > 大数据 > 人工智能

进程控制开发[fork() exec exit _exit wait waitpid 守护进程]

2016-04-15 14:54 369 查看


第七章 进程控制开发[fork() exec exit _exit wait waitpid 守护进程]

转:http://www.cnblogs.com/jimwind/archive/2012/12/26/2834147.html
前言:
1、fork 创建一个子进程,有两个返回值。返回0为子进程,返回大于0为父进程。
2、exec 运行新的可执行文件,取代原调用进程的数据段、代码段和堆栈段。一般是运行fork后,在子进程中执行exec。
3、exit(0)和_exit(0):exit(0)会先清理I/O缓冲后再调用系统exit,而_exit(0)是直接调用系统exit
4、wait函数是用于使父进程(也就是调用wait的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。如果该进程没有子进程或者他的子进程已经结束,则wait就会立即返回。
5、守护进程使用在android的system下面,如netd,vold等。
====================================================================
7.1.1 Linux 进程相关基本概念
进程是一个程序的一次执行的过程。程序是静态的,进程是动态的,包括动态创建、调试和消亡的整个过程。
2、进程控制块
进程是Linux系统的基本调度单位,系统通过进程控制块描述并表示它的变化。
进程控制块包含了进程的描述信息、控制信息以及资源信息,它是进程的一个静态描述。
在Linux中,进程控制块中的每一项都是一个task_struct结构,它是在include/linux/sched.h中定义的。
3.进程的标识
进程号(PID, Process Idenity Number) 和  父进程号(PPID, parent process ID)
在Linux中获得当前进程的PID和PPID的系统调用函数为getpid和getppid,
用户和用户组标识、进程时间、资源利用情况等。
4.进程运行的状态 进程是程序的执行过程,根据它的生命期可以划分成3种状态
执行态/就绪态/等待态

7.1.2 Linux下的进程结构
Linux中的进程包含3个段,分别为“数据段”、“代码段”和“堆栈段”。
数据段存放的是全局变量、常数以及动态数据分配的数据空间(如malloc函数取得的空间)等。
代码段存放的是程序代码数据。
堆栈段存放的是子程序的返回地址、子程序的参数以及程序的局部变量。
在Linux系统中,进程的执行模式划分为用户模式和内核模式。
如果用户程序执行过程中出现系统调用或者发生中断事件,就要运行操作系统程序,变成内核模式。
7.1.4 Linux下的进程管理
进程管理分为启动进程和调度进程。
1.启动进程 
主要有两种途径:手工启动和调度启动。手工启动是由用户输入命令直接启动进程,而调度启动是指系统根据用户的设置自行启动进程。
(1)手工启动进程又可分为前台启动和后台启动。
前台启动是手工启动一个进程的最常用方式。一般地,当用户键入一个命令如"ls -l"时,就已经启动了一个进程,并且是一个前台进程。
后台启动往往是在该进程非常耗时,且用户也不急着需要结果的时候启动的。比如格式化文本文件的进程。
(2)调度启动
费时且占用资源的维护工作,并且在深夜无人职守的时候进行,用户可以事先进行调度安排,指定任务运行的时间或者场合。
使用调度启动进程有几个常用的命令,如at命令在指定时刻执行相关进程,cron命令可以自动周期性的执行相关进程。
2.调度进程
调度进程包括对进程的中断操作、改变优先级、查看进程状态等。
ps | top | nice | renice | kill | crontab | bg

======================================================================
7.2 Linux进程控制编程
进程创建
1. fork()
pid_t fork(void);
在Linux中创建一个新进程的惟一方法是使用fork函数。它执行一次却返回两个值。
(1) fork函数说明
fork函数用于从已存在进程中创建一个新进程。新进程称为子进程,而原进程称为父进程。
这两个分别带回它们各自的返回值,其中父进程的返回值是子进程的进程号,而子进程则返回0。因此,可以通过返回值来判断该进程是父进程还是子进程。



#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(1);
} else if(result == 0){
printf("The return value is 0,In child process!! My PID is %d\n",getpid());
} else {
printf("The return value is %d, In father process !! My PID is %d\n", result, getpid());
}
}


(4)函数使用注意点
2. exec函数族
(1)exec函数族说明
fork函数用于创建一个子进程,该子进程几乎copy了父进程的全部内容,但是这个新创建的进程如何执行呢?
这个exec函数族就提供了一个在进程中启动另一个程序执行的方法,它可以根据指定的文件名或目录名找到可执行文件。
在Linux中使用exec函数族主要有两种情况:
*当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec函数族让自己重生
*如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec,这样看起来就好像通过执行应用程序而产生一个新进程。(这种情况非常普遍)
(2)exec函数族语法 函数族里有6个以exec开头的函数
int execl(const char *path, const char *arg, ...)
int execv(const char *path, char * const argv[])
int execle(const char *path, const char *arg, ..., char * const envp[])
int execve(const char *path, char * const argv[], char * const envp[])
int execlp(const char *file, const char *arg, ...)从环境变量$PATH所指出的路径中进行查找
int execvp(const char *file, char * const argv[])从环境变量$PATH所指出的路径中进行查找
参数传递方式有两种:一种是逐个列举的方式,另一种是将所有参数整体构造指针数组传递。
字母为“l"(list)的表示逐个列举,其语法为char *arg;字母为"v"(vector)的表示将所有参数整体构造指针数组传递,其语法为*const argv[]。





(3)使用实例



#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
if(fork() == 0){
/*call execlp, just like call "ps -ef"*/
if(execlp("ps","ps","-ef",NULL) < 0)
perror("execlp error!");
}
}



execl


execle


execve

3.exit和 _exit
(1)函数说明,exit和_exit函数都是用来终止进程的,进程会无条件的停止剩下的所有操作。
但这两个函数还是有区别的
_exit() 直接调用 exit系统调用
exit()->调用退出处理函数->清理I/O缓冲->调用exit系统调用
若想保证数据的完整性,就一定要使用exit()函数,因为程序处理数据有缓冲区。
由于printf函数使用的缓冲I/O方式,该函数在遇到"\n"换行符时自动从缓冲区中将记录读出。
如果没有"\n",exit(0)能从缓冲区读出,而_exit(0)则不能。

4. wait 和 waitpid
(1)wait和waitpid函数说明
wait函数是用于使父进程(也就是调用wait的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。
如果该进程没有子进程或者他的子进程已经结束,则wait就会立即返回。
waitpid的作用和wait一样,但它并不一定要等待第一个终止的子进程,它还有若干选项。wait只是waitpid的一个特例
(2)wait和waitpid函数格式说明
pid_t wait(int *status)
pid_t waitpid(pid_t pid, int *status, int options)





(3) waitpid使用实例
本例中首先使用fork()新建一子进程,然后让其子进程暂停5s,接下来对原有的父进程使用waitpid函数,并使用参数WNOHANG使该父进程不会阻塞。若有子进程退出,则waitpid返回子进程号;若没有子进程限出,则waitpid返回0,并且父进程每隔一秒循环判断一次。


waitpid

7.3 Linux守护进程
1.创建子进程,父进程退出
pid = fork();
if(pid>0)
exit(0);父进程退出了。
子进程变成了孤儿进程,会自动由1号进程(也就是init进程)收养它。这样,原先的子进程就会变成init进程的子进程了。

2.在子进程中创建新会话
pid_t setsid(void)
进程组:进程组是一个或多个进程的集合。进程组由进程组ID来惟一标识。除了进程号PID之后,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID,且该进程ID不会因组长进程的退出而受到影响。
会话期:会话组是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期,它们之间的关系如下图所示。



setsid函数作用:
setsid函数用于创建一个新的会话,并担任该会话组的组长。
让进程摆脱原会话的控制。让进程摆脱原进程组的控制。让进程摆脱原控制终端的控制。

由于调用fork函数时,子进程全盘拷贝了父进程的会话期、进程组控制终端等,虽然父进程退出了,但原先的会话期、进程组、控制终端等并没有改变。



3.改变当前目录为根目录
让"/"作为守护进程的当前工作目录。常见函数为chdir
4.重设文件权限掩码
umask(0)
5.关闭文件描述符
for(i = 0; i<MAXFILE; i++)
  close(i);


dameon

7.3.3 守护进程的出错处理
gdb是通过输出错误信息到控制终端来通知程序员
守护进程使用syslog服务,将程序中的出错信息输入到“/var/log/messages"系统日志文件中。
syslog是Linux中的系统日志管理服务,通过守护进程syslogd来维护。该守护进程在启动时会读一个配置文件"/etc/syslog.conf"。该文件决定了不同种类的消息会发送向何处。该机制提供了3个syslog函数,分别为openlog、syslog和closelog。
void openlog(char *ident, int option, int facility)
void syslog(int priority, char *format, ...)
void closelog(void)







(3)使用实例

7.4实验内容
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: