3.6 创建进程:fork
头文件:#include #include 原型:pid_t fork(void); 功能:创建一个子进程 参数:无 返回值: 成功:返回给父进程子进程的ID号,返回给子进程 0 失败返回-1; fork:失败的条件只有一个,内存不足。
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char const *argv[]) { int fd = open("./1.txt",O_RDWR|O_CREAT,0666); if(-1 == fd) { perror("open"); return -1; } pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { printf("我是子进程\n"); write(fd,"hello world",11); } else if(pid > 0) { printf("我是父进程,我的子进程ID号为%d\n",pid); char buf[123] = {0}; sleep(1); lseek(fd,0,SEEK_SET); //close(fd); //fd = open("./1.txt",O_RDONLY); read(fd,buf,sizeof(buf)); printf("buf = %s\n",buf); } return 0; }
3.7 写时拷贝
fork函数创建子进程时要复制父进程的资源,但是子进程并不一定会用到这些资源, 所以说: 采用一种方式:当创建完子进程后,子进程先共享父进程的资源,如果双方有一方去修改内容,修改之前先复制一份到子进程中,这就叫做写时拷贝技术。
3.8 文件共享
如果fork之间打开了一些文件,获取了一些文件描述符,那么fork之后父进程和子进程公用这些fork之前获取的文件描述符。那么在操作过程中,有可能造成读写指针相互影响。
12345 6789
3.9 获取进程ID接口
3.9.1 getpid
头文件:#include #include 原型:pid_t getpid(void); 功能:获取自己的进程ID 参数:无 返回值: 成功:返回自己的进程ID 无失败
3.9.2 getppid
头文件:#include #include 原型:pid_t getppid(void); 功能:获取父进程的进程ID 参数:无 返回值: 成功:返回父进程ID 无失败
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char const *argv[]) { pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(0 == pid) { printf("我是子进程\n"); printf("我是子进程,我的进程号为:%d\n",getpid()); printf("我是子进程,我的父进程号为:%d\n",getppid()); } else if(pid > 0) { sleep(1); printf("我是父进程\n"); printf("我是父进程,我的子进程号为:%d\n",pid); printf("我是父进程,我的进程号为:%d\n",getpid()); printf("我是父进程,我的父进程号为:%d\n",getppid()); } return 0; }
3.10 三个结束进程的函数
exit
头文件:#include 原型:void exit(int status); 功能:结束一个进程,先释放缓冲区 参数:status:结束进程时的状态,同return 正常结束用0,非正常结束用-1 返回值: 无
_exit
头文件:#include 原型:void _exit(int status); 功能:结束一个进程,不会释放缓冲区,直接结束 参数:status:结束进程时的状态,同return 正常结束用0,非正常结束用-1 返回值: 无
atexit
头文件:#include 原型:int atexit(void (*function)(void)); 功能:注册一个进程结束后的运行函数 (当进程结束时会调用function这个函数) 参数:指向返回值为void 类型参数为void的函数指针 返回值: 成功返回0 失败返回非0值
#include <stdio.h> #include <stdlib.h> #include <unistd.h> void fun(void) { printf("hello world!\n"); } int main(int argc, char const *argv[]) { //开始注册函数 atexit(fun); sleep(3); return 0; }
3.11 孤儿进程:
父进程优先于子进程结束,子进程失去父亲后,子进程会认1号进程是自己的父进程。那么1号进程负责回收和管理子进程。如果说很多子进程都人1号进程为他的父进程,1号进程的负担会很大,所以我们在编写代码的时候,尽量让父进程回收完子进程资源之后再结束。孤儿进程是没有危害的。
3.12 僵尸进程
子进程优先于父进程结束,子进程会认为该父进程会回收自己的资源,但是父进程没有回收子进程资源的功能,或者说父进程一直在忙于自己的事情,未曾去回收子进程资源,子进程资源就得不到回收,但是子进程任务已经结束了,所以说子进程就变成了僵尸进程,僵尸是有危害。注意:以后在写
fork时都需要回收子进程资源。
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> int main(int argc, char const *argv[]) { pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { exit(0); //子进程结束,父进程未结束,子进程就变成了僵尸进程 } else if(pid > 0) { while(1) { sleep(1); } } return 0; }
3.12.1 处理僵尸进程
wait
头文件:#include 原型:pid_t wait(int *stat_loc); 功能:阻塞等待回收任意一个子进程资源 参数:stat_loc:进程结束的状态(一般不考虑进程结束的状态直接写NULL) 返回值: 成功会返回接受到的进程的ID号 失败返回-1 注意:如果父进程调用wait来回收资源,那么会阻塞等待,会降低父进程的工作效率
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { exit(0); //子进程结束,父进程未结束,子进程就变成了僵尸进程 } else if(pid > 0) { printf("父进程运行中...\n"); sleep(5); printf("接受成功,接受到子进程的ID号为%d\n",wait(NULL)); sleep(5); } return 0; }
waitpid
头文件:#include #include 原型:pid_t waitpid(pid_t pid, int *wstatus, int options); 功能:回收子进程资源,可以不阻塞回收也可以指定回收哪一个 参数: pid: pid == -1:回收任意一个子进程,如果采用阻塞方式与wait一样 pid == 0: 回收同进程组中的任意一个子进程 pid < -1;回收同进程组中,等于pid绝对值的子进程 注意: -:代表同组,对pid进行取绝对值,|pid| == 正数 wstatus:回收到的子进程结束时返回的状态 options:操作方式 0:阻塞回收 WNOHANG:非阻塞方式回收(如果去回收,没有紫禁城结束,立马返回,如果说已经有子进程结束, 立马回收) 返回值: 成功会返回接受到的进程的ID号 失败返回-1 注意:需要频繁的去调用函数去查看子进程结束与否
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { sleep(5); exit(0); //子进程结束,父进程未结束,子进程就变成了僵尸进程 } else if(pid > 0) { pid_t pid1; while(1) { printf("父进程运行中...\n"); if(0 < (pid1 = waitpid(-1,NULL,WNOHANG))) { printf("会受到子进程资源,子进程的ID号为: %d\n",pid1); } sleep(1); } } return 0; }
3.13 守护进程
是一种特殊的进程机制,默默地对我的工作进行服务的进程。是一个后台进程。init进程就是一个守护进程。后台进程:只允许向终端写入数据,不允许从终端读取数据。如果一旦对终端输入进行获取,那么就会立即终止后台进程。要脱离终端的管理,脱离回话的管理。守护进程一般用于:服务器,http,tftp,ftp。
附加:ctrl + c给前台所有的进程发一个终止信号
创建守护进程是有固定步骤的:
变成孤儿进程 kill:给进程发一个信号
创建一个新的会话,变成会首进程,setsid
头文件:#include #include 原型: pid_t setsid(void); 功能:创建一个新的会话,并且创建者称为会话的首进程 参数:无 返回值: 成功会返回一个新的会话ID号 失败返回-1 修改默认工作目录文件 chdir 头文件:#include 原型: int chdir(const char *path); 功能:修改工作路径 参数:路径 返回值: 成功会返回0 失败返回-1 给与最高文件权限 umask 关闭文件描述符 close(0);注意:如果说也不会对终端进行操作,请关闭所有的文件描述符 开始做守护事件,如日志文件的写入。
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { //子进程 //创建一个会话,自己成为自己的主人 if(-1 == setsid()) { perror("setsid"); return -1; } //修改工作目录 if(-1 == chdir("/")) { perror("chdir"); return -1; } umask(0); close(0); close(1); close(2); FILE * fp = fopen("./1.txt","w+"); while(1) { printf("11112\n"); //写日志文件 fprintf(fp,"helloworld\n"); fflush(fp); sleep(1); } } //不去管父进程,父进程会自动结束 return 0; }
练习:使用两个进程共同去完成复制一个文件,将一个文件拷贝到另一个文件中
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char const *argv[]) { //使用文件IO打开文件,获取文件大小 int fd = open(argv[1],O_RDONLY); if(-1 == fd) { perror("open"); return -1; } int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666); if(-1 == fd1) { perror("open1"); return -1; } //获取文件大小,利用偏移量 int len = lseek(fd,0,SEEK_END); printf("len = %d\n",len); //需要一个从开头,一个从中间开始获取 len = len / 2; pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } if(pid == 0) { lseek(fd,len,SEEK_SET); lseek(fd1,len,SEEK_SET); char buf[123] = {0}; //接收读到的输入,写入数据时使用 ssize_t ret =0; while((ret = read(fd,buf,123))) { if(-1 == write(fd1,buf,ret)) { perror("write"); return -1; } } close(fd); close(fd1); } else if(pid > 0) { //父进程 ----》从头开始 lseek(fd,0,SEEK_SET); lseek(fd1,0,SEEK_SET); char buf[123] = {0}; ssize_t ret = 0; while(len) { if(len >= 123) { ret = read(fd,buf,123); } else { ret = read(fd,buf,len); } write(fd1,buf,ret); len = len - ret; } } return 0; }