fork
创建一个子进程
头文件:
#include <unistd.h>
原型:
pid_t fork(void);
返回值:
成功: >0 在父进程中,返回创建的子进程的PID;
0 在子进程中,返回0;
失败, -1 在父进程中,返回-1,没有子进程被创建,更新errno;
例程:
pid_t pid = fork();
if(pid > 0)//父进程
{
printf("this is parent %d %d\n", getpid(), pid);
printf("pid = %d %d\n", pid, __LINE__);
}
else if(0 == pid)//子进程
{
printf("this is child %d %d\n", getppid(), getpid());
printf("pid = %d %d\n", pid, __LINE__);
}
else if(pid < 0)//创建失败
{
perror("fork");
return -1;
}
getpid
获取当前进程的PID
头文件:
#include <sys/types.h>
#include <unistd.h>
原型:
pid_t getpid(void);
返回值:
获取当前进程的PID;
例程:
pid_t pid = getpid();
getppid
获取当前进程的父进程PID
头文件:
#include <sys/types.h>
#include <unistd.h>
原型:
pid_t getppid(void);
返回值:
获取当前进程的父进程PID;
例程:
pid_t ppid = getppid();
_exit
终止进程,清除进程使用的内存空间,销毁其在内核中的各种数据,但不会刷新缓冲区
头文件:
#include <unistd.h>
原型:
void _exit(int status);
参数:
int status:可以利用这个参数传递进程终止的状态,可以输入任意整形。这个参数会被 wait/waitpid 函数接收。
例程:
_exit(1);
exit
终止进程,并刷新缓冲区
头文件:
#include <stdlib.h>
原型:
void exit(int status);
参数:
int status:可以利用这个参数传递进程终止的状态,可以输入任意整形。这个参数会被 wait/waitpid 函数接收。
例程:
exit(1);
wait
阻塞等待子进程退出,并回收子进程的资源;如果没有子进程,父进程不等待,直接退出
头文件:
#include <sys/types.h>
#include <sys/wait.h>
原型:
pid_t wait(int *status);
参数:
int *status:获取子进程退出的状态;接收exit/_exit中的status传递值。如果不想接收,填NULL;
返回值:
成功,返回退出的子进程的pid;
失败,返回-1,更新errno;
例程:
//不接收子进程退出的状态值
pid_t c_pid = wait(NULL);
printf("回收子进程资源成功 %d\n", c_pid);
//接收子进程退出的状态值
int status;
pid_t c_pid = wait(&status);
printf("回收子进程资源成功 %d\n", c_pid);
printf("status=%d\n", status);
waitpid
等待指定进程退出,并回收指定进程的资源
头文件:
#include <sys/types.h>
#include <sys/wait.h>
原型:
pid_t waitpid(pid_t pid, int *status, int options);
参数:
pid_t pid:
< -1 回收指定进程组下的任意子进程,指定进程组的id = pid的绝对值。例如PGID=1976,则pid填-1976;
-1 等待当前进程下的任意子进程。例如:回收变为僵尸进程的子进程;
0 回收当前进程组下的任意子进程;
> 0 回收指定进程。
int *status:获取子进程退出的状态;接收exit/_exit中的status传递值。如果不想接收,填NULL;
int options:
0,阻塞等待,如果指定的进程没有退出,则当前进程阻塞等待。
WNOHANG:非阻塞,就算指定进程没有退出,当前进程不等待,继续执行后面的代码;
返回值:
成功,阻塞方式,返回被回收进程的pid.
非阻塞方式,如果指定进程退出,则返回被回收的进程的pid;如果指定进程没有退出,则返回0;
失败,返回-1,更新errno;
例程:
//不接收子进程退出的状态值
pid_t c_pid = waitpid(pid,NULL,0);
printf("回收子进程资源成功 %d\n", c_pid);
//接收子进程退出的状态值
int status;
pid_t c_pid = wait(pid,&status,0);
printf("回收子进程资源成功 %d\n", c_pid);
printf("status=%d\n", status);
WEXITSTATUS
从wait(&s)到提取exit(status)中status的值
pid_t wait(int*s); 中s存储的是一个32位的整形,其中[8,15]总共8bit存放的是exit(status)中的status;
用宏函数的方式返回子进程终止函数exit(int status)中的status值
例程:
int status;
wait(&status);
printf("status=%d\n", status);
int ret = WEXITSTATUS(status);
printf("ret = %d\n", ret);
WIFEXITED
判断子进程是否正常退出
- 返回1,代表子进程正常退出;调用函数exit或return退出的都是正常退出
- 返回0,代表子进程异常退出;由于信号而退出的是异常退出,例如kill -9 pid
例程:
int status;
wait(&status);
printf("status=%d\n", status);
int res = WIFEXITED(status);
if(res)
{
printf("正常退出\n");
}
else
{
printf("异常退出\n");
}
例程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void child_func();
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if(pid > 0)
{
//父进程
printf("parent.....\n");
sleep(1);
int status;
pid_t c_pid = wait(&status);
printf("回收子进程资源成功 %d\n", c_pid);
printf("status=%d\n", status);
int ret = WEXITSTATUS(status);
printf("ret = %d\n", ret);
int res = WIFEXITED(status);
if(res)
{
printf("正常退出\n");
}
else
{
printf("异常退出\n");
}
while(1)
sleep(1);
}
else if(0 == pid)
{
//子进程
child_func();
printf("child end\n");
}
else
{
perror("fork");
return -1;
}
printf("准备退出了\n");
return 0;
}
void child_func()
{
int i=0;
while(1)
{
printf("child....\n");
sleep(1);
i++;
if(i==1)
{
exit(-1);
//exit(0);
//exit(1);
}
}
}
结果
//exit(-1);时结果
parent.....
child....
回收子进程资源成功 6747
status=65280
ret = 255
正常退出
//exit(0);时结果
parent.....
child....
回收子进程资源成功 6756
status=0
ret = 0
正常退出
//exit(1);时结果
parent.....
child....
回收子进程资源成功 6763
status=256
ret = 1
正常退出
守护进程的创建
1、创建孤儿进程
2、创建新的会话,脱离父进程的控制
3、修改运行目录
4、修改文件权限掩码,一般保留文件原有权限
5、关闭所有文件描述符
setsid
头文件:
#include <unistd.h>
原型:
pid_t setsid(void);
返回值:
成功,返回新的会话id;
失败,返回-1,更新errno;
1.创建新的会话,成为会话组组长;
2.创建新的进程组,成为进程组组长;
3.脱离父进程的控制。
chdir
头文件:
#include <unistd.h>
原型:
int chdir(const char *path);
参数:
char *path:指定修改后的运行目录,如"/";
返回值:
成功,返回0;
失败,返回-1;
例程
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
//1、创建孤儿进程
pid_t pid = fork();
if(pid > 0)
{
}
else if(pid == 0)
{
//子进程
//2、创建新的会话,脱离父进程的控制
pid_t sid = setsid();
printf("sid = %d\n", sid);
//3、修改运行目录
chdir("/");
//从这一步往后的程序,当前进程运行在根目录下
//4、修改文件权限掩码
umask(0);
//5、关闭所有文件描述符
int i = 0;
for(i=0; i<1024; i++)
{
close(i);
}
while(1)
{
//程序功能
sleep(1);
}
}
else
{
perror("fork");
return -1;
}
return 0;
}
应用例程
拷贝一张图片,是先父进程拷贝前半部分,后子进程拷贝后半部分。
提示:用sleep(2);
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define ERR_LOG(msg) do{\
perror(msg);\
fprintf(stderr, "%d %s %s\n", __LINE__, __func__, __FILE__);\
}while(0)
int main(int argc, const char *argv[])
{
int fd_r = open("./1.png", O_RDONLY);
if(fd_r < 0)
{
ERR_LOG("open");
return -1;
}
int fd_w = open("2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664);
if(fd_w < 0)
{
ERR_LOG("open");
return -1;
}
int size = lseek(fd_r, 0, SEEK_END); //计算文件大小
lseek(fd_r, 0, SEEK_SET); //偏移回开头位置
pid_t pid = fork();
if(pid > 0)
{
//父进程
int i = 0;
char c = 0;
int res = -1;
for(i=0; i<size/2; i++)
{
res = read(fd_r, &c, 1);
if(-1 == res)
{
ERR_LOG("read");
exit(1);
}
//写入到2.png中 fd_w
if(write(fd_w, &c, 1) < 0)
{
ERR_LOG("write");
exit(1);
}
}
printf("前半部分拷贝完毕\n");
wait(NULL);
printf("子进程退出\n");
}
else if(0 == pid)
{
//子进程
sleep(2);
lseek(fd_r, size/2, SEEK_SET);
lseek(fd_w, size/2, SEEK_SET);
int i =0, res = -1;
char c = 0;
for(;;)
{
res = read(fd_r, &c, 1);
if(-1 == res)
{
ERR_LOG("read");
exit(1);
}
else if(0 == res)
{
break;
}
//写入到2.png中 fd_w
if(write(fd_w, &c, 1) < 0)
{
ERR_LOG("write");
exit(1);
}
}
printf("后半部分拷贝完毕\n");
}
else
{
ERR_LOG("fork");
return -1;
}
close(fd_r);
close(fd_w);
return 0;
}
本文链接:https://shengto.top/c/fork_routine.html
转载时须注明出处及本声明