1、创建进程函数:fork(),vfork(),返回值为0,则为子进程,大于0,父进程,-1错误
fork:子进程复制父进程资源,子进程独立于父进程
父子进程运行顺序不确定
创建进程系统开销大,写时复制可以缓解
vfork:子进程共享父进程地址空间
子进程先运行
创建进程系统开销小
quick start:
#include <stdio.h>
#include <unistd.h>
int main(){
pid_t pid;
pid = fork();
switch(pid){
case -1:
printf("error!\n");
break;
case 0:
printf("this is child--pid=%d\n", getpid());
break;
default:
printf("this is paren--pid=%d\n", getpid());
break;
}
return 0;
}
2、有了子进程之后,我们希望他执行不一样的代码,这个时候就需要execve函数
其中,第一个函数为执行的路径或文件名
第二个函数为传的main函数的命令行参数
第三个函数为环境变量(这里把PATH设为.意为当前路径)
quick start:
//父进程代码
#include <stdio.h>
#include <unistd.h>
int main(int argc, char*argv[], char**enviro){
printf("Start=======\n");
pid_t pid;
char*envp[]={"PATH=.", "NULL"};
pid = vfork();
switch(pid){
case 0:
printf("this is child proc\n");
printf("pid=%d,ppid=%d\n", getpid(), getppid());
printf("uid=%d,gid=%d\n", getuid(),getgid());
execve("hello",argv,envp);
default:
printf("this is parent proc\n");
}
return 0;
}
//子进程代码,编译完成后命名为hello
#include <stdio.h>
int main(){
printf("HELLO PROC:\n");
printf("pid=%d,ppid=%d\n", getpid(), getppid());
printf("uid=%d,gid=%d\n", getuid(), getgid());
return 0;
}
3、代码执行完成后,需要结束掉,这时候就需要exit函数,如果我们想在进程结束前做一些事,急需要on_exit函数
on_exit有两个参数,第一个是执行的代码地址,第二个是传递的参数
exit中传递的是0或者EXIT_SUCCESS则为正常退出
为非0或者EXIT_FAILURE为非正常退出
不写exit的话,系统默认为非正常,状态码为6
quick start:
#include <stdio.h>
#include <stdlib.h>
void be(int arg, void*str){
printf("this is be\n");
printf("exit_code:%d\n", arg);
printf("the tell is:%s\n", str);
}
void be_2(int arg, void*str){
printf("this is be_2\n");
printf("exit_code:%d\n", arg);
printf("the tell is:%s\n", str);
}
int main(){
char*str="this is onexit";
on_exit(be,(void*)str);
on_exit(be_2,(void*)str);
printf("11111\n");
//exit(2222);
//return 0;
}
4、如果父进程在子进程终止之前终止,子进程就会变成孤儿进程,这时会由init收养
但如果子进程在父进程之前终止,子进程就会变成僵尸进程,这时就需要利用wait函数对其资源进行回收
wait函数会返回其pid,如果给定参数,则会赋值其退出的状态码
quick start:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void chile(){
printf("this is chile\n");
printf("pid=%d,ppid=%d",getpid(),getppid());
exit(0);
}
void parent(){
int p_return, stat;
printf("this is parent\n");
p_return = wait(&stat);
printf("pid=%d\np_return=%d\nstat=%d\n",getpid(),p_return,stat);
}
int main(){
pid_t pid;
pid = fork();
switch(pid){
case -1:
printf("error\n");
case 0:
chile();
default:
parent();
}
return 0;
}
5、守护进程
对于某些进程,我们不希望他有终端,希望他一直后台执行,如系统服务等,就可以设置守护进程
流程
①:让进程在后台执行,可以先调用fork()生成一个子进程,再利用exit使父进程退出
②:摆脱控制终端,可以使用setsid,这个函数会生成一个新的会话,并脱离控制终端,但这里进程是会话组长,可以开一个新的终端
③:禁止进程打开终端,可以再次使用fork,退出父进程,这样,进程就不是会话组长
④:更改当前目录为根目录,因为子进程的目录是继承了父进程的,所以为防止对父进程误操作,将子进程当前目录改为根目录,chdir("/")
⑤:将文件创建时的屏蔽字置为0,因为子进程从父进程继承的屏蔽字可能会拒绝某些许可,影响操作,可以用umask(0)
⑥:关闭文件描述符,子进程继承的文件描述符不关闭会导致系统资源的浪费以及文件系统的无法卸载
⑦:处理SIGCHID信号:对于服务进程,往往在请求到来时生成子进程处理请求,如果不等待子进程就会产生僵尸进程,如果等待又会增加服务器负担,影响服务器并发性能,所以对SIG_CHID做忽略处理,就不会产生僵尸进程,也保证了进程的并发性,可以用signal(SIG_HUP,SIG_IGN)