上篇我们说到什么是进程,获取进程的id,那么怎么创建一个进程呢,我们可以使用fork函数。
一、fork又叫分叉函数。
它将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。
二、函数有以下特点:
①当fork()顺利完成任务时,就会存在两个进程,每个进程都从fork()返回处开始继续执行。
②两个进程执行相同的代码(text)段,但是有各自的堆栈(stack)段、数据(data)段以及堆(heap)。
③子进程的stack、data、heap segments是从父进程拷贝过来的。
④fork()之后,哪一个进程先执行(scheduled to use the CPU)不确定。
三、函数原型:(我们同样可以使用man一下)
pid_t fork( void);
(pid_t 是一个宏定义, 被定义在#include<sys/types.h>中)
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
四、fork有三个返回值 。由fork创建的进程称为子进程,原来的是父进程。两次返回的唯一区别是子进程中返回0值,而父进程中返回子进程ID。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
// fork : 创建一个子进程
// 如果创建失败 返回 -1
// 成功返回两个值,如果是在父进程中,返回子进程的ID
// 如果是在子进程,返回值是0
pid_t pid = fork();
if (pid == -1)
{
perror ("fork");
return -1;
}
if (pid > 0) // 父进程
{
printf ("我是父进程,pid = %d\n", getpid());
}
else if (pid == 0) // 子进程
{
printf ("我是子进程,pid = %d\n", getpid());
}
while (1);
return 0;
}
我们可以这样理解fork函数返回的值为什么在父子进程中不同。其实就相当于链表,进程形成了链表,父进程的fork函数返回的值指向子进程的进程id, 因为子进程没有子进程,所以其fork函数返回的值为0.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int count = 0;
int main()
{
// int count = 0;
pid_t pid = fork();
count++;
printf ("count = %d\n", count);
return 0;
}
程序中,因为fork会复制两个栈堆数据,所以count的值都是为0,count++;分别在两个进程各加一次,均为1..
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
fork();
fork() && fork() || fork();
fork();
printf("a\n");
while (1);
return 0;
}
第一个fork和最后一个fork肯定是会执行的。主要在中间3个fork上,可以画一个图进行描述。
这里就需要注意&&和||运算符。
A&&B,如果A=0,就没有必要继续执行&&B了;A非0,就需要继续执行&&B。
A||B,如果A非0,就没有必要继续执行||B了,A=0,就需要继续执行||B。
fork()对于父进程和子进程的返回值是不同的,按照上面的A&&B和A||B的分支进行画图,可以得出5个分支。
int execl(const char * path, const char* arg1,...)
参数:
path : 被执行程序名(含完整路径)。
arg1 - argn: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束
int main()
{
// 第一个参数需要一个路径,能够找到需要执行的文件
// 后面的参数表明执行的方式,和在终端方式类似,如果程序执行需要
// 其他的参数 ls -l, 参数要作为函数的实参传过去
// 最后需要补一个 NULL
// 产生一个新的程序 ,替换了原有的程序,原有的进程id是不变的
// int ret = execl("/bin/ls", "ls", "-l", NULL);
int ret = execl("/bin/ps", "ps", "-ef", NULL);
if (ret == -1)
{
perror("execl");
return -1;
}
printf ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");
return 0;
}
int execlp(const char * path, const char* arg1,...)
参数:
path : 被执行程序名(不含路径,将从path环境变量中查找该程序)。
arg1 - argn: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束
int main()
{
int ret = execlp("ls", "ls", "-l", NULL);
if (ret == -1)
{
perror("execl");
return -1;
}
printf ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");
return 0;
}
int execv(const char * path, const char *argv[])
参数:
path : 被执行程序名(含完整路径)。
argv[]: 被执行程序所需的命令行参数数组
int main()
{
char *a[] = {"file", NULL};
int ret = execv("file", a);
if (ret == -1)
{
perror("execl");
return -1;
}
return 0;
}
int system(const char* string)
功能:
调用fork产生子进程,由子进程来调用 /bin/sh -c string来执行参数string所代表的命
int main1()
{
printf ("hellp world\n");
sleep(1);
// 在内部fork()一个子进程,调用 /bin/sh -c string来执行
system("ps -ef | grep a.out");
printf ("aaaaaaaaaaaaaaaaaaaaa\n");
return 0;
}
终止函数:
详细说:
https://baike.baidu.com/item/fork/7143171
http://blog.sina.com.cn/s/blog_99f5031a0101lshp.html