所有的进程都是由其他进程创建(除了 pid 为 0 号的 idle 进程),pid 号为 1 的 init 进程是系统启动后运行的第一个进程,是所有进程的父进程,init 进程会初始化一部分系统服务,创建其他进程。
子进程结束后,它的父进程要回收它的资源,否则就会成为僵尸进程 。
如果父进程先结束,子进程会被 init 进程收养,称为孤儿进程。
“ps -el”查看进程状态 PID 为进程号,PPID 为此进程的父进程号
getpid():获取进程 PID,
#include
#include
pid_t getpid(void);//获取此进程
PID pid_t getppid(void);//获取父进程 PID
返回值为 PID 号。
fork():系统调用,创建一个进程,
#include pid_t fork(void);
//调用成功父进程返回子进程号,子进程返回 0,失败返回-1。
/*第一次调用 fork()进程 2531 创建子进程 2532;第二次 fork(),进程 2531 创建子进程 2533, 进程 2532 创建 2534*/
#include
#include
#include
int main(int argc, const char *argv[])
{
pid_t pid;
int i;
for(i=0;i<2;i++)
{
pid=fork();
if(pid>0)
{
printf("进程%d\n",getpid());
}else if(pid == 0)
{
printf("进程%d\n",getpid());
}
}
return 0;
}
exit():进程终止可使用,
#include
void exit(int status);
/*函数功能:调用各终止处理程序进行清理退出,通过参数保存退出状态,调用_exit 进入内核态,向父进程发送信号 SIGCHLD,子进程终止。 参数含义:退出状态,如 EXIT_SUCCESS 和 EXIT_FAILURE。*/
wait(),waitpid():回收子进程可使用,
#include
#include
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
参数含义:
/*status:
子进程的退出状态,可以用以下几个宏判断状态:
WIFEXITED(status) //判断子进程是否正常结束
WEXITSTATUS(status) //获取子进程返回值,1为正常,0为失败
WIFSIGNALED(status) //判断子进程是否被信号结束
WTERMSIG(status) //获取结束子进程的信号类型
/*
pid:
waitpid()中的参数,
pid=-1 时,表示等待任一子进程退出,
pid > 0 等待 pid 号子进程退出。
options:
waitpid()中的参数,控制 waitpid()。
返回值:若成功则为进程 ID,若出错则为-1 。
*/
#include
#include
#include
#include
void exit_status(pid_t p ,int s);
int main(int argc, const char *argv[])
{
pid_t pid,spid;
int s_son=-1;
pid = fork();
if(pid<0)
{
perror("fork"); return 1;
}else if(pid == 0)
{
printf("子进程 %d\n",getpid());
sleep(1);
exit(EXIT_FAILURE);
//exit(EXIT_SUCCESS);//返回 0
}else if(pid >0)
{
printf("父进程 %d\n",getpid());
//sleep(1);//等待子进程退出
spid = wait(&s_son);//获取子进程退出状态
exit_status(spid,WEXITSTATUS(s_son));//打印
}
return 0;
}
/** 打印退出状态 */
void exit_status(pid_t p ,int s)
{
switch (s)
{
case 0: printf("子进程--%d--退出状态: EXIT_SUCCESS\n",p); break;
case 1: printf("子进程--%d--退出状态: EXIT_FAILURE\n",p); break;
default: printf("other\n"); break;
}
}
用 fork 函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序,该子进程被新的程序 替换,改变地址空间,进程映像和一些属性,但是 pid 号不变。
execve():
#include
int execve(const char *filename, char *const argv[], char *const envp[]);
/*
参数含义:
filename:路径名,表示载入进程空间的新程序路径。
argv[]:命令行参数,argv[0]为命令名。
envp[]:新程序的环境变量。
返回值:成功时不会返回,使用时不用检查返回值,可通过 errno 检查。
以下函数都是根据 execve 实现:
*/
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
#include
#include
#include
#include
void exit_status(pid_t p ,int s);
int main(int argc, const char *argv[])
{
pid_t pid,spid;
int s_son=-1;
pid = fork();
if(pid<0)
{
perror("fork");
return 1;
}else if(pid == 0)
{
printf("子进程 %d\n",getpid());
if(-1 == execl("/bin/ls","ls","./",NULL))//ls 进程替换子进程
{
perror("execl");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);//返回 0
}
else if(pid >0)
{
printf("父进程 %d\n",getpid());
spid = wait(&s_son);//获取子进程退出状态
exit_status(spid,WEXITSTATUS(s_son));//打印
}while(1)
{
printf("hello\n");
sleep(2);
}
return 0;
}/** 打印退出状态 */
void exit_status(pid_t p ,int s)
{
switch (s)
{
case 0: printf("子进程--%d--退出状态: EXIT_SUCCESS\n",p); break;
case 1: printf("子进程--%d--退出状态: EXIT_FAILURE\n",p); break;
default: printf("other\n"); break;
}
}
1.什么是守护进程? 守护进程运行在后台,不跟任何控制终端关联。
2.怎么创建一个守护进程?
有俩个基本要求:1.必须作为我们init进程的子进程⒉.不跟控制终端交互。
1.使用fork函数创建一个新的进程,然后让父进程使用exit函数直接退出(必须要的)
2.调用setsid函数。(必须要的)
3.调用chdir函数,将当前的工作日录改成根目录,增强程序的健壮性。(不是必须要的)
4.重设我们umask文件掩码,增强程序的健壮性和灵活性(不是必须要的)
5.关闭文件描述符,节省资源(不是必须要的)
6.执行我们需要执行的代码(必须要的)
#include
#include
#include
#include
#include
int main(vold)
{
pid_t pid;
//步骤一:创建一个新的进程
pid = fork();
/父进程直接退出
if(pid > 0)
{
exit(0);
}
if(pld == 0)
{
//步骤2:调用setsid函数摆脱控制终端
setsid();
//步骤3:更改工作目录
chdir("/");
//步骤4:重新设置umask文件掩码
umask(0);
//步骤5:关闭012三个文件描述符
close(0);
close(1);
close(2);
//步骤6:执行我们要执行的代码
while(1)
{
}
}
return 0;
}