文章目录
一、代码替换函数
(一)system函数
#include <stdlib.h>
int system(const char *command);
功能:
使用fork创建一个子进程 在子进程中执行shell命令
参数:
command:要执行的命令字符串 例如:"ls -l" "./b.out"
返回值:
如果command为NULL 终端可用返回非0 终端不可用返回0
如果子进程创建失败 返回 -1 重置错误码
如果命令都执行成功了返回的就是子进程退出的状态值
- 注:调用system函数时,进程会阻塞等待system函数调用结束后再继续执行
#include <my_head.h>
int main(int argc, const char *argv[])
{
printf("----start----\n");
//执行shell命令
system("ls -l");
printf("-----end-----\n");
return 0;
}
输出结果:
(二)exec函数族
int execl(const char *pathname, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *pathname, const char *arg, ...);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
二、守护进程
相当于系统的服务,它会随着系统启动而启动,随着系统的终止,脱离了终端
(一)创建
1. 脱离父进程影响
创建子进程,父进程退出
使子进程变成孤儿,被init进程收养
2. 脱离原会话组和进程组的影响
调用setsid来创建新的会话组和进程组
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
功能:
如果调用进程不是进程组组长,会创建一个新的会话和进程组
调用进程变成新会话和进程组中的唯一进程
新的会话id和进程组id 都等于调用进程的pid
参数:无
返回值:
成功 新的会话id
失败 -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. 关闭从父进程继承的文件描述符
一般通过遍历并关闭文件描述符表中的所有项来实现
for(int i=0;i<1024;i++){
close(i);
}
6. 将标准输入、标准输出、标准出错都重定向到文件中
使守护进程脱离终端
#include <unistd.h>
int dup(int oldfd);
功能:重定向文件描述符 将 返回值(遵循最小原则)产生新文件描述符
重定向到oldfd中
参数:oldfd:旧的文件描述符
返回值:
成功 产生新文件描述符(遵循最小原则)
失败 -1 重置错误码
#include <unistd.h>
int dup2(int oldfd, int newfd);
功能:将newfd重定向到oldfd中
参数:
oldfd:旧的文件描述符
newfd:新的文件描述符
返回值:
成功 newfd
失败 -1 重置错误码
7. 让守护进程随系统启动而启动
如果想让自己的守护进程随着系统的启动而启动
可以 把执行进程的命令 放在系统启动的过程中会执行的脚本中 如 /etc/profile
- 补:
/etc/profile
主要是用于设置系统级的环境变量和启动程序. - 它是在用户登录时由登录shell读取的,而不是在系统启动时由init系统(如systemd或SysVinit)读取的。因此,如果将守护进程的启动命令放在/etc/profile中,那么这个守护进程只会在用户登录后,且该用户的shell被加载时才会启动。
(二)守护进程代码示例
#include <my_head.h>
int main(int argc, char const *argv[])
{
pid_t pid = 0;
//1. 脱离父进程
pid = fork();
if(-1 == pid){
ERR_LOG("pid error");
}else if(0 < pid){//父进程
exit(0);//退出父进程
}else if(0 == pid){//子进程
//2. 脱离原来的会话组
setsid();//调用进程成为一个新的会话组组长
//3. 修改进程的工作目录
chdir("/home/linux/");
//4. 修改进程创建文件的最大权限
umask(0002);
//5. 关闭文件描述符
for(int i=0;i<1024;i++){
close(i);
}
int log_fd = open("/home/linux/05work/07day/log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
if(-1 == log_fd) ERR_LOG("open error");
//6. 标准输入、标准输出、标准出错都重定向到文件中
dup2(log_fd,0);
dup2(log_fd,1);
dup2(log_fd,2);
//开启自己的服务
while(1){
printf("hello world\n");
fflush(stdout);
sleep(1);
}
return 0;
}
return 0;
}
- 注:将stdin和stdout重定向到文件后,不再适用于行缓冲,而是全缓冲,因此此时遇到’\n’不能刷新缓冲区,所以此处采用手动刷新的方式
上图可见,该进程的父进程为1号init进程;
依赖终端"?"代表不依赖终端;
进程状态为Ss,休眠态,且是进程组组长;