- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Linux 创建子进程执行任务的实现方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
linux 操作系统紧紧依赖进程创建来满足用户的需求。例如,只要用户输入一条命令,shell 进程就创建一个新进程,新进程运行 shell 的另一个拷贝并执行用户输入的命令。linux 系统中通过 fork/vfork 系统调用来创建新进程。本文将介绍如何使用 fork/vfork 系统调用来创建新进程并使用 exec 族函数在新进程中执行任务.
fork 系统调用 。
要创建一个进程,最基本的系统调用是 fork:
1
2
3
|
# include <
unistd.h
>
pid_t fork(void);
pid_t vfork(void);
|
调用 fork 时,系统将创建一个与当前进程相同的新进程。通常将原有的进程称为父进程,把新创建的进程称为子进程。子进程是父进程的一个拷贝,子进程获得同父进程相同的数据,但是同父进程使用不同的数据段和堆栈段。子进程从父进程继承大多数的属性,但是也修改一些属性,下表对比了父子进程间的属性差异:
。
继承属性 | 差异 |
uid,gid,euid,egid | 进程 id |
进程组 id | 父进程 id |
session id | 子进程运行时间记录 |
所打开文件及文件的偏移量 | 父进程对文件的锁定 |
控制终端 | |
设置用户 id 和 设置组 id 标记位 | |
根目录与当前目录 | |
文件默认创建的权限掩码 | |
可访问的内存区段 | |
环境变量及其它资源分配 |
。
下面是一个常见的演示 fork 工作原理的 demo(笔者的环境为 ubuntu 16.04 desktop):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <
sys
/types.h>
#include <
unistd.h
>
#include <
stdio.h
>
#include <
stdlib.h
>
int main(void)
{
pid_t pid;
char *message;
int n;
pid = fork();
if(pid < 0)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{
printf("this is the child process. my pid is: %d. my ppid is: %d.\n", getpid(), getppid());
}
else
{
printf("this is the parent process. my pid is %d.\n", getpid());
}
return 0;
}
|
把上面的代码保存到文件 forkdemo.c 文件中,并执行下面的命令编译:
1
|
$ gcc forkdemo.c -o forkdemo
|
然后运行编译出来的 forkdemo 程序:
1
|
$ ./forkdemo
|
fork 函数的特点是 "调用一次,返回两次":在父进程中调用一次,在父进程和子进程中各返回一次。在父进程中返回时的返回值为子进程的 pid,而在子进程中返回时的返回值为 0,并且返回后都将执行 fork 函数调用之后的语句。如果 fork 函数调用失败,则返回值为 -1。 我们细想会发现,fork 函数的返回值设计还是很高明的。在子进程中 fork 函数返回 0,那么子进程仍然可以调用 getpid 函数得到自己的 pid,也可以调用 getppid 函数得到父进程 pid。在父进程中用 getpid 函数可以得到自己的 pid,如果想得到子进程的pid,唯一的办法就是把 fork 函数的返回值记录下来。 注意:执行 forkdemo 程序时的输出是会发生变化的,可能先打印父进程的信息,也可能先打印子进程的信息.
vfork 系统调用 。
vfork 系统调用和 fork 系统调用的功能基本相同。vfork 系统调用创建的进程共享其父进程的内存地址空间,但是并不完全复制父进程的数据段,而是和父进程共享其数据段。为了防止父进程重写子进程需要的数据,父进程会被 vfork 调用阻塞,直到子进程退出或执行一个新的程序。由于调用 vfork 函数时父进程被挂起,所以如果我们使用 vfork 函数替换 forkdemo 中的 fork 函数,那么执行程序时输出信息的顺序就不会变化了.
使用 vfork 创建的子进程一般会通过 exec 族函数执行新的程序。接下来让我们先了解下 exec 族函数.
exec 族函数 。
使用 fork/vfork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往需要调用一个 exec 族函数以执行另外一个程序。当进程调用 exec 族函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的起始处开始执行。调用 exec 族函数并不创建新进程,所以调用 exec 族函数前后该进程的 pid 并不改变.
exec 族函数一共有六个:
1
2
3
4
5
6
7
|
#include <
unistd.h
>
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[]);
int execve(const char *path, char *const argv[], char *const envp[]);
|
函数名字中带字母 "l" 的表示其参数个数不确定,带字母 "v" 的表示使用字符串数组指针 argv 指向参数列表。 函数名字中含有字母 "p" 的表示可以自动在环境变量 path 指定的路径中搜索要执行的程序。 函数名字中含有字母 "e" 的函数比其它函数多一个参数 envp。该参数是字符串数组指针,用于指定环境变量。调用这样的函数时,可以由用户自行设定子进程的环境变量,存放在参数 envp 所指向的字符串数组中.
事实上,只有 execve 是真正的系统调用,其它五个函数最终都调用 execve。这些函数之间的关系如下图所示(此图来自互联网):
exec 族函数的特征:调用 exec 族函数会把新的程序装载到当前进程中。在调用过 exec 族函数后,进程中执行的代码就与之前完全不同了,所以 exec 函数调用之后的代码是不会被执行的.
在子进程中执行任务 。
下面让我们通过 vfork 和 execve 函数实现在子进程中执行 ls 命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#include <
sys
/types.h>
#include <
unistd.h
>
#include <
stdio.h
>
#include <
stdlib.h
>
int main(void)
{
pid_t pid;
if((pid=vfork()) < 0)
{
printf("vfork error!\n");
exit(1);
}
else if(pid==0)
{
printf("child process pid: %d.\n", getpid());
char *argv[ ]={"ls", "-al", "/home", null};
char *envp[ ]={"path=/bin", null};
if(execve("/bin/ls", argv, envp) < 0)
{
printf("subprocess error");
exit(1);
}
// 子进程要么从 ls 命令中退出,要么从上面的 exit(1) 语句退出
// 所以代码的执行路径永远也走不到这里,下面的 printf 语句不会被执行
printf("you should never see this message.");
}
else
{
printf("parent process pid: %d.\n", getpid());
sleep(1);
}
return 0;
}
|
把上面的代码保存到文件 subprocessdemo.c 文件中,并执行下面的命令编译:
1
|
$ gcc subprocessdemo.c -o subprocessdemo
|
然后运行编译出来的 subprocessdemo程序:
1
|
$ ./subprocessdemo
|
总结 。
fork/vfork 函数和 exec 族函数都是 linux 系统中非常重要的概念。本文试图通过简单的 demo 来演示这些函数的基本用法,为理解 linux 系统中父进程与子进程的概念提供一些直观的感受.
原文链接:https://www.cnblogs.com/sparkdev/p/8214455.html 。
最后此篇关于Linux 创建子进程执行任务的实现方法的文章就讲到这里了,如果你想了解更多关于Linux 创建子进程执行任务的实现方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!