一、进程
1.1 wait / waitpid 函数的使用
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *wstatus);
功能:阻塞等待子进程结束,为子进程回收资源
参数:wstatus:子进程退出的状态--如果不关注,可以传NULL0~6:bit位,保存的是中断进程的信号号8~15:bit位,保存的是进程退出的状态WIFEXITED(wstatus):如果进程是正常退出(是否是通过调用exit()函数退出的),返回真WEXITSTATUS(wstatus):获取进程退出的状态,0~255,必须在WIFEXITED返回为真的情况下使用WIFSIGNALED()wstatus:如果进程是被信号中断,这个宏返回trueWTERMSIG(wstatus):返回中断信号进程的信号号,必须在WIFSIGNALED返回为真的情况下使用
返回值:成功返回回收到的子进程的pid,失败返回-1,置位错误码pid_t waitpid(pid_t pid,int *wstatus,int options);
功能:回收子进程的资源
参数:pid:< -1:回收组id为pid的绝对值的子进程的资源传参:-123456,那么会回收组id为123456的子进程的资源-1:回收任意子程序的资源‘0:回收同一进程组的子进程的资源> 0 :回收进程pid == 参数pid,的子进程的资源通过进程的pid,指定回收某个子进程的资源wstatus:子进程退出的状态 -- 如果不关注,可以传NULL0~7:bit位保存的是中断进程的信号号8~15:bit位保存的是进程退出的状态。WIFEXITED(wstatus):如果进程是正常退出(是否是通过调用exit()函数退出的)),返回真WEXITSTATUS(wstatus):获取进程退出的状态,0~255,必须在WIFEXITED返回真的情况下使用WIFSIGNALED(wstatus):如果进程是被信号中断,这个宏返回trueWTERMSIG(wstatus):返回中断进程的信号号,必须在WIFSIGNALED返回真的情况下使用
options:0:表示阻塞回收子进程的资源WNOHANG:非阻塞回收子进程的资源返回值:成功返回回收到的子进程的pid,失败返回-1wait(&wstatus)等价于waitpid(-1,&wstatus,0)
wait(NULL)等价于waitpid(-1,NULL,0)
wait函数的使用实例(回收资源不关注子进程的退出状态)
#include <my_head.h>int main(int argc,const char *argv[]){pid_t ret = 0;ret = fork();if(-1 == ret){PRINT_ERR("fork error");}else if(0 == ret){//子进程sleep(20);printf("子进程,要死了\n");exit(100);//退出的状态为100}else{//父进程//回收资源,不关注子进程的退出状态printf("父进程\n");wait(NULL);//传参NULL,阻塞回收子进程的资源,不关注子进程的状态printf("父进程回收子进程资源完毕\n");sleep(5);printf();}return 0;
}
运行结果
wait函数的使用实例(回收资源关注子进程的退出状态)
#include <my_head.h>int main(int argc,const char *argv[]){pid_t ret = 0;ret = fork();if(-1 == ret){PRINT_ERR("fork error");}else if(0 == ret){//子进程sleep(5);printf("子进程,要死了\n");exit(100);//退出的状态为100}else{//父进程//回收资源,关注子进程的退出状态int wstatus = 0;pid_t ret;int status,signo;ret = wait(&wstatus);printf("回收到的子进程pid = %d\n",ret);if(WIFEXITED(wstatus)){status = WEXITSTATUS(wstatus);printf("子进程退出的状态值是%d\n",status);}if(WIFSIGNALED(wstatus)){signo = WTERMSIG(wstatus);printf("中断子进程的信号号%d\n",signo);}}return 0;
运行结果:
waitpid使用实例(阻塞方式回收子进程资源,不关注进程退出状态)
#include <my_head.h>int main(int argc,const char *argv[]){pid_t ret = 0;ret = fork();if(-1 == ret){PRINT_ERR("fork error");}else if(0 == ret){//子进程sleep(5);printf("子进程,要死了\n");exit(100);//退出的状态位100}else{//父进程//回收资源,不关注子进程退出状态printf("我是父进程\n");waitpid(-1,NULL,0);//传参1NULL,不关注子进程退出状态,阻塞回收子进程的资源printf("父进程回收子进程资源完毕\n");sleep(5);printf("父进程死了\n");}return 0;
}
运行结果:
waitpid使用实例(非阻塞方式回收子进程资源,关注进程退出状态)
#include <my_head.h>int main(int argc,const char *argv[]){pid_t ret = 0;ret = fork();if(-1 == ret){PRINT_ERR("fork error");}else if(0 == ret){//子进程sleep(5);printf("子进程,要死了\n");exit(100);//退出的状态位100}else{//父进程int wstatus = 0;pid_t ret;int sstatus,signo;while(1){printf("我是父进程\n");//非阻塞回收子进程资源,WNOHANGret = waitpid(-1,&wstatus,WNOHANG);if(ret != 0&&ret != -1){printf("回收的子进程pid为%d\n",ret);if(WIFEXITED(wstatus)){sstatus = WEXITSTATUS(wstatus);printf("子进程退出的状态值%d\n",wstatus);}if(WIFSIGNALED(wstatus)){signo = WTERMSIG(wstatus);printf("中断子进程信号号%d\n",signo);}}sleep(1);} }return 0;
}
运行结果:子进程是否运行完不影响父进程的运行,父进程反复判断,当子进程死了,父进程就回收子进程资源,然后继续运行
1.2 system函数的使用
#include <stdlib.h> --- 所需头文件
int system(const char *command);
功能:通过fork创建一个子进程,在子进程中执行command命令
参数:command:要执行的命令,执行shell脚本,shell命令,可执行程序等
返回值:1、如果command为NULL,或者shell可用,返回0,如果不可用返回非0的数值2、如果子进程无法被创建,那么返回-1,置位错误码3、如果command不可以被执行,返回状态码1274、如果所有的command都执行成功,会返回最后一个shell的返回值。
system使用实例
#include <my_head.h>int main(int argc,const char *argv[]){int ret = 0;//使用system执行命令ret = system("ls");return 0;
}
运行结果:
#include <my_head.h>int main(int argc,const char *argv[]){int ret = 0;//使用system执行命令ret = system("lcdsc");//shell不可用,返回非0数值printf("ret = %d\n",ret);return 0;
}
运行结果:
#include <my_head.h>int main(int argc,const char *argv[]){int ret = 0;//使用system执行多个命令system("ls;pwd");//方式1:使用;作为分隔符,多个命令都会被执行(不论成功失败)return 0;
}
运行结果:
#include <my_head.h>int main(int argc,const char *argv[]){int ret = 0;//使用system执行多个命令system("lssq&&pwd");//方式2:使用&&作为分隔符,多个命令都会被执行,//只要有一个失败,后面的命令都不会被执行return 0;
}
运算结果:
#include <my_head.h>int main(int argc,const char *argv[]){int ret = 0;//使用system执行多个命令system("lssq||pwd||ls");//方式2:使用||作为分隔符,//只要有一个命令执行成功,后面命令则不会再执行return 0;
}
运行结果:
1.3 守护进程
1.3.1 守护进程的概念
随着系统的启动而启动,睡着系统的终止而终止,脱离中断运行。
就是系统的服务。
1.3.2 守护进程(daemon)的创建流程
1、创建一个孤儿进程(为了脱离终端)
2、设置会话id,创建一个新的会话,新会话即新终端,此终端在后台运行不可视。(脱离终端)
3、设置工作目录(把此目录设置在根目录,为了保证工作目录一直存在)
4、设置掩码(设置创建文件的掩码)
5、对文件描述符重定向(将输入输出重定向到某个文件)
6、开启自己的服务
1.3.3相关API分析
1.创建一个孤儿进程(为了脱离终端)
使用fork创建一个子进程之后,让父进程直接结束,子进程不要结束
2.设置会话id,创建一个新的会话。
(脱离目前终端,再在后台开一个新的终端)
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
功能:创建一个新的会话,调用者会称为这个会话的组长
参数: 空
返回值:成功返回新的会话id,看不到这个终端,只知道新终端的id 失败返回(pid_t) -1 ,置位错误码
3.设置工作目录(为了保证工作目录一直存在)
把目录设置到了根目录,根目录一直存在,也是为了保证新开的终端一直存在。
#include <unistd.h>
int chdir(const char *path);
功能:改变进程的工作目录
参数: path:新的工作目录
返回值:成功返回0,失败返回-1,置位错误码
4.设置掩码(设置创建文件的掩码)
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask); 功能:设置进程创建文件的掩码
参数: mask:新的掩码
返回值:这个函数总是调用成功,返回之前的掩码数值
5.对文件描述符重定向(将输入输出重定向到某个文件)
像prinf,scanf等输入输出函数,都输出到终端,在终端输入,重定向到某个文件之后,输入输出的操作现象就在这个文件中显现了。
#include <unistd.h>
int dup(int oldfd);
功能:复制文件描述符,将oldfd复制产生一个新的fd并作为返回值返回
参数: oldfd:旧的文件描述符
返回值:成功返回复制出的新的文件描述符,失败返回-1,置位错误码 int dup2(int oldfd, int newfd);
功能:复制oldfd生成新的newfd
参数: oldfd:旧的文件描述符 newfd:新的文件描述符
返回:成功返回复制出的新的文件描述符,失败返回-1,置位错误码
dup的使用
#include <my_head.h>
int main(int argc, const char *argv[])
{
// 1.打开一个文件 int oldfd, newfd; oldfd = open("./a.txt", O_RDWR | O_CREAT, 0666); if (-1 == oldfd)PRINT_ERR("open error"); //
//2.复制文件描述符 // newfd = dup(oldfd); // write(oldfd,"hello",strlen("hello")); // write(newfd," world",strlen(" world"));//都会写入a.txt文件中,oldfd,newfd代表的是同一个文件 close(1); // 关闭标准输出 newfd = dup(oldfd);//复制出的新的文件描述符遵循最小分配原则,由于标准输出1,被关掉了,所以复制出的新的文件描述符是1 printf("11111111111111111\n"); return 0;
}
dup2使用实例
#include <my_head.h>
int main(int argc, const char *argv[]) {// 1.打开一个文件 int oldfd, newfd; oldfd = open("./a.txt", O_RDWR | O_CREAT, 0666); if (-1 == oldfd) PRINT_ERR("open error"); newfd = 6; // //2.复制文件描述符 dup2(oldfd,newfd);//将oldfd复制,生成新的文件描述符6 write(oldfd,"hello",strlen("hello")); write(newfd," world",strlen(" world"));//都会写入a.txt文件中,oldfd,newfd代表的是同一个文件 dup2(oldfd,1);//复制出一个新的文件描述符1,旧的1(标准输出)会被关闭 printf("11111111111111111\n"); return 0;
}
6.开启自己的服务
守护进程创建实例
注意:创建完成之后,看到现象了,九八创建的守护进程杀死
kill -9 进程pid
#include <my_head.h>int main(int argc,const char *argv[]){//1.创建一个孤儿进程pid_t ret;ret = fork();if(-1 == ret){PRINT_ERR("fork error");}else if(0 == ret){//子进程//2.设置会话setsid();//3.设置工作目录chdir("/");//4.设置掩码umask(0);//5.输入输出重定向//创建一个新的文件,作为日志文件int fd = open("/log.txt",O_RDWR|O_CREAT,0666);if(-1 == fd)PRINT_ERR("open log.txt error");//将输入输出重定向到log.txtdup2(fd,0);dup2(fd,1);dup2(fd,3);//开启自己的服务while(1){printf("hello world\n");fflush(NULL);sleep(1);}}else{//父进程exit(EXIT_SUCCESS);//父进程直接退出}return 0;
}
运行现象: