1. fork函数介绍
在linux中fork函数是非常重要的函数,它可以从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
fork函数的返回值如下:
1、在父进程中,fork返回新创建的子进程的PID号。
2、在子进程中,fork返回0;
3、如果出现错误,fork返回一个负值。
因此可以通过返回值来判断是父进程还是子进程。
fork函数创建子进程的过程:
使用fork函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的所有资源,相当于就是父进程的一个副本。
#include <unistd.h>
pid_t fork(void); 制作分身
函数功能: 创建新的子进程. 子进程是父的进程一个副本. (分身)
返回值: >0表示父进程 ==0表示子进程
示例代码:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int a=888;
//创建新的子进程
pid_t pid=fork();
if(pid==0) //子进程
{
a=999;
printf("子进程_a=%d\n",a);
printf("子进程的PID号:%d\n",getpid());
printf("当前子进程的父进程PID号:%d\n",getppid());
}
else
{
sleep(1);
}
return 0;
}
2. wait函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
函数功能: 随机等待一个子进程退出.
如果等待的子进程正常结束,返回值就是该子进程的pid号
如果父进程没有子进程正在运行,wait函数会立即返回,返回值为-1
pid_t waitpid(pid_t pid, int *status, int options);
函数功能: 等待指定的子进程退出.
正常的多进程并发设计,父进程要负责清理子进程的空间:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid=fork();
if(pid==0)
{
printf("子进程运行成功.pid=%d\n",getpid());
}
else
{
pid=wait(NULL); //等待子进程退出,清理子进程的空间
printf("退出的子进程的pid=%d\n",pid);
}
return 0;
}
3. exec系列函数
exec系列函数是用于启动一个新的进程,将新的进程启动成功之后会覆盖原进程.
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
p表示支持从环境变量里搜索可执行文件.
示例代码:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
//ls -l
//execl("/bin/ls","ls","-l",NULL);
//execlp("ls","ls","-l",NULL);
char *cmd[]={"ls","-l",NULL};
//execv("/bin/ls",cmd);
//execvp("ls",cmd);
char *cmd_path[]={"wbyq=666","abcd=888",NULL};
execle("/bin/ls","ls","-l",NULL,cmd_path);
printf("exec函数执行失败.\n");
return 0;
}
4. system函数
system函数用于启动新的子进程,这个函数内部就是使用fork+exec+wait函数组合实现的。
说明这个system函数是阻塞的,必须等待子进程执行完毕之后才会执行父进程的代码。
#include <stdlib.h>
int system(const char *command);
示例代码:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
printf("开始执行.\n");
system("sleep 5");
printf("执行结束.\n");
return 0;
}
5. popen函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
函数功能: 启动一个新的进程--类似于fopen --dup2函数
启动成功的返回值的指针就指向该进程的标准输出。
int pclose(FILE *stream);
函数功能: 释放空间
示例代码:
#include <stdio.h>
int main()
{
FILE *fp;
char buff[1024+1];
int cnt;
fp=popen("/bin/ls *.sh","r");
cnt=fread(buff,1,1024,fp);
buff[cnt]='\0';
printf("buff=%s\n",buff);
pclose(fp);
return 0;
}
6. pkill 命令
(1) pkill 命令支持一次性杀死某用户的所有进程。
$ pkill -u <用户名>
(2) pkill 命令支持一次杀死指定名称的所有进程。
$ pkill <应用名称>
(3) 使用 killall 命令一次杀死指定名称的所有进程
$ killall <应用名称>
(4) 杀死父进程创建的所有子进程
pkill -9 -P <父进程 PID>
7. 案例: 使用fork函数创建5个子进程同时运行
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
pid_t pid;
for(i=0;i<5;i++)
{
pid=fork(); //创建一个子进程
if(pid==0)break; //如果是子进程就直接退出循环
}
if(i==5) //父进程
{
sleep(5);
char cmd_buff[100];
sprintf(cmd_buff,"pkill -9 -P %d",getpid());
system(cmd_buff);
while(1)
{
pid=wait(NULL);
if(pid==-1)break; //当父进程没有子进程的时候该函数就返回-1
else printf("%d 子进程退出成功.\n",pid);
}
printf("父进程正常结束.\n");
}
else //子进程
{
while(1)
{
sleep(1);
printf("当前运行的子进程pid=%d\n",getpid());
}
}
return 0;
}
8. 案例: 实现自动切换(2秒)显示指定目录下的所有图片
利用eog命令,配合今天学习的进程知识点,做出一个ppt播放效果。
思路: 父进程扫描目录,得到目录下的文件名称,在传递给子进程,子进程调用eog命令实现图片显示,父进程里2秒钟之后就杀死子进程,再读取目录下下一个文件,再传递给子进程………
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./a.out <图片目录/>\n");
return 0;
}
/*1. 打开目录*/
DIR *dir=opendir(argv[1]);
if(dir==NULL)
{
printf("%s 目录打开失败.\n",argv[1]);
return 0;
}
/*2. 创建管道文件*/
int fds[2];
pipe(fds);
pid_t pid;
struct dirent *dir_info;
AA:
/*循环遍历目录*/
dir_info=readdir(dir);
if(dir_info==NULL)
{
printf("图片显示完毕.\n");
exit(0); //表示图片显示完毕
}
/*创建子进程*/
pid=fork();
if(pid==0) //子进程
{
char file_name[100+1];
int cnt;
//从管道的读端读取数据
cnt=read(fds[0],file_name,100);
file_name[cnt]='\0';
printf("正在显示的图片:%s\n",file_name);
//启动新的进程
execlp("eog","eog",file_name,NULL);
}
else //父进程
{
//判断是否是图片
if(strstr(dir_info->d_name,".jpg"))
{
char *p;
//组合图片的路径
p=malloc(strlen(argv[1])+strlen(dir_info->d_name)+1);
sprintf(p,"%s%s",argv[1],dir_info->d_name);
//向管道的写端写数据
write(fds[1],p,strlen(p));
//延时2秒
sleep(2);
//杀死父进程创建的所有子进程
char cmd_buff[100];
sprintf(cmd_buff,"pkill -9 -P %d",getpid());
system(cmd_buff);
//清理子进程的空间
wait(NULL);
}
//继续显示下一张图片
goto AA;
}
return 0;
}