一、exec 函数族
作用:调用该函数,可以在进程中执行另外的程序
头文件:
#include <unistd.h>
l ----->list:参数以列表方式呈现
p ------>path:环境变量
1. int execl(const char *path, const char *arg, ...);
2. int execlp(const char *file, const char *arg, ...);
3. int execv(const char *path, char *const argv[]);
4. int execvp(const char *file, char *const argv[]);
返回值:
*成功: 返回当前程序调用结果
*失败:-1
参数:
*path : 执行程序的名称(包含路径)
参数传递:以函数的第5位字母来区分,exec 后面一位:
1.若是子母 l (list):逐个列举参数 ,类型为:const char *arg 命令参数列表必须以NULL结尾
2.若是字母 v (vertor):指针数组传递,类型为 : char *const argv[]
————————————————————————————————————————————
- int execl(const char *path, const char *arg, …);
例:
- ls -l /etc
使用完整的文件目录来查找对应的可执行文件
相当于执行ps -ef 命令/需要给出ps所在程序的完整路径
execl("/bin/ps","ps","-ef","NULL");
相当于执行 ls -l 命令/需要给出ls -l 所在程序的完整路径
execl ("/bin/ls","ls","-l","/ect",NULL);
注意
: 目录必须以 ”/ “ 开头
,否则将其视为文件名 ,excel 的参数, 以NULL结尾
- int execlp(const char *file, const char *arg, …)
参数:- file : 要执行文件的名称
2 .使用文件名的方式查找可执行文件
相当于执行 ls -l 命令
execlp("ls", "ls", "-l", "/home/farsight",NULL);//ls---文件名
相当于执行 ps -ef 命令
excelp("ps","ps","-ef","NULL");
与在shell中直接输入命令是一样
- int execv(const char *path, char *const argv[]);
参数通过指针数组来传递
char *buf[] = {"ls", "-l", "/etc", NULL};
ret = execv("/bin/ls", buf);
- int execle(const char *path, const char *arg,…,char const envp[] );
将环境变量添加到新建的子进程中,env ----->查看当前进程环境变量的命令
将环境变量添加到新建的子进程中
char *envp[] = {"PATH = /tem","USER = sunny",NULL};
if (fork() == 0)
{
...execle("usr/bin/env","env",NULL,envp);//env --->需要完整路径
}
—————————————————————————————————————————————
二、system —在进程中执行其他命令
头文件:
#include <stdlib.h>
system (const char *command);
参数:
*command : 要执行的命令
/在main函数中执行ls命令/
int main(int argc, char *argv[])
{
system("ls");
return 0;
}
三、vfork
特点:
- 通过vfork创建的子进程,父子进程共用同一片地址空间
- 子进程优先于父进程运行
- 子进程调用exec函数族中的函数或者子进程调用exit()函数后,父进程才有可能执行
vfork 的子进程调用execl 函数后(子进程空间被其他进程替换),此时父进程不会再等待子进程结束,而是和子进程抢空间
四、wait() 和waitpid()
1.wait()—父进程等待子进程结束,回收其资源
头文件:
#include <sys/types.h>
#include <sys/wait.h>
原型;
pid_t wait(int *status);
返回值:
*成功:等待子进程的进程号
*失败:-1
参数;
- status ;保存进程退出的属性
一般是父进程使用wait函数等待回收子进程
特点:
- 在父进程等待子进程结束的过程中,父进程会阻塞直到子进程退出或者接受到指定信号为止
- wait函数一次只能回收一个子进程,哪个先结束,就先回收哪个
- 如果该父进程没有子进程,或者子进程已经结束,则wait 会立即返回-1
WIFEXITED(status) --- 判断子进程是否正常退出 1-正常退出 0-非正常 WEXITSTATUS(status) --- 获取正常退出的返回值
WIFSIGNALED(status) --- 判断是否因为信号退出
WTERMSIG(status) --- 返回具体是哪一个信号导致子进程退出
2. waitpid()-----等待接收指定的任意子进程
头文件:
#include <sys/types.h>
#include <sys/wait.h>
原型:
pid_t waitpid(pid_t pid, int *status, int options);
参数:
*pid:
-1 : 回收任意子进程
指定pid号 : 接收等待回收指定子进程
*status :保存进程退出的属性
*option :
0:阻塞方式回收
WNONHANG:非阻塞方式,不会等待子进程先结束
例:
waitpid(-1, &stauts, 0);
waitpid(-1, &stauts, WNOHANG);
waitpid(pid, &stauts, 0);
waitpid(pid, &stauts, WNOHANG);
/***调用waitpid,进程阻塞***/
pid_t ret = waitpid (pid,NULL,0);
/***调用wait ,父进程不阻塞***/
pid_t ret = waitpid (pid,NULL,WNOHANG);
五、守护进程
守护进程一般在系统启动时运行,系统关闭时结束------一般用于后台服务器
特点:
- 始终在后台运行,不会影响前台进程的运行
- 独立于任何终端,不能输入,也不能输出
- 周期性地执行程序,或者等待某些事件的响应
相关概念:
进程组:
- 一个或者多个进程的集合,进程组又进程ID唯一标识
- 当用户运行一个进程时,就创建了一个进程组,运行进程中创建的所有具有亲缘关系的进程都属于当前进程组
- 每个组都又一个组长进程,组长进程的ID号就是进程组的ID号;但是进程组的ID号不会收组长进程的退出的影响
会话:
- 是一个或者多个进程组的集合,当用户打开一个终端时,就会产生一个会话
- 会话和终端相关,打开终端,创建一个会话,关闭终端,会话中所有进程会被强行结束
创建守护进程步骤
1. 创建一个子进程,父进程退出
父进程退出的原因:使子进程变为孤儿进程,被init进程收养,由前台进程变为后台进程
pid_t pid = fork);
if (pid > 0)
{
exit (0);//父进程退出
}
2. 创建一个新的会话------setsid()
目的: 让子进程脱离原来的会话,成为新的会话组长
setsid():创建一个新的会话,并担任该会话组的组长进 3个作用:
- 让进程摆脱原会话的控制
- 让进程摆脱原进程组的控制
- 让进程摆脱原控制终端的控制
头文件:
#include <sys/types.h>
#include <unistd.h>
原型:
pid_t setsid (void);
返回值:
*成功:返回进程组ID
*失败: -1
3. 更改工作目录
要么设置为根目录,要么设置为"/tmp"
chdir("/");
chdir ("/tem");
4. 重设文件权限掩码
由于用fork 函数创建的 子进程继承了父进程的文件权限掩码吗,这给孩子进程使用文件带来了一定的影响。因此把文件权限掩码设置为0 (~&后是 其本身)
umask(0)
5. 关闭打开的文件描述符------getdtabesize()
int i = 0
for(i = 0; i < getdtablesize(); i++)
{
close(i);
}
while(1)
{
任务;
}
练习:创建守护进程,周期性地向time.log文件中输入当前时间
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <strings.h>
16
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <time.h>
20
21
22 int main()
23 {
24 FILE *fp ;
25
26 int i;
27 time_t t;
28 time(&t);
29 struct tm *ltime;
30 //创建子进程,并退出父进程
31 pid_t pid = fork();
32
33 if (pid > 0)
34 exit (1);
35
36 //创建一个新的会话
37
38 int ret = setsid();
39 if (ret < 0)
40 exit (2);
41
42 //更改目录
43 chdir("/tem");
44
45
46 //设置文件权限掩码
47 umask(0);
48
49 /关闭文件描述符
50 int num = getdtablesize();
51 for (i = 0; i< num;i++)
52 {
53 close(i);
54 }
55
56 fp = fopen("time.log","a+");//追加:另起一行
57 if ( fp == NULL)
58 exit (3);
59 while (1)
60 {
61
62
63 fprintf(fp,"%s",ctime(&t));
64 fflush(fp);
65 sleep(1);
66 }
67
68 return 0;
69 }
——————————————————————————————————————————————————————
小点点:
/***main 函数 参数的用法 ***/
int main (int argc,char *argv[]) //命令行参数
{
if (argc < 3)
{
printf(“Uage : %s ”);
execl (-1);// execl (-1) == return -1;
}
…
}
如输入命令 :./a.out
Usage : ./a.out //提示语句
命令:./a.out can1 can2
…<运行结果>
参数:
argc :命令行参数个数 (3)
argv[]: argv[0]----><命令行的第一个参数>(./a.out) ;argv[1]----><命令行的第二个参数 > (can1);argv[2]---->命令行的第三个参数 (can2)