Linux进程管理

一、fork函数

功能说明

一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
当程序执行到fork函数时会复制一份原来的进程(创建一个新的进程),旧进程(父进程)和新进程(子进程)会继续执行fork之后的代码,父子进程执行的顺序未知。

简单例子

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
/*简单了解fork函数的功能*/
/*父进程与子进程的执行顺序未知*/
#include <stdio.h>
#include <unistd.h>
int main()
{
int flag;//flag用于区别创建的子进程
flag = fork();//创建子进程
if (flag == 0)
{
//子进程child_1
printf("This is child_1 process %d\n",flag);
flag++;
if (fork() == 0) //在子进程下创建新的子进程child_2
printf("This is child process of child_1\n");
//进程 child_1
else printf("This is child_1 process %d\n",flag);
}
else
{
//父进程
printf("This is parent process\n");
}

return 0;
}

运行结果如下:

二、exec函数族

功能说明

fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程不共享这些存储空间。linux将复制父进程的地址空间内容给子进程,因此,子进程由了独立的地址空间。),也就是这两个进程做完全相同的事。

正因为fork函数创建的子进程几乎等同于父进程的副本,所以我们更希望子进程可以执行不一样的操作,可以装入和运行其它程序(子进程替换原有进程,和父进程做不同的事),使用exec函数就可以执行和父进程不一样的操作。

使用方法

头文件 #include <unistd>
函数说明 执行文件
函数原型 int execl(const char *pathname, const char *arg, ...)
int execv(const char *pathname, char *const argv[])
int execle(const char *pathname, const char *arg, ..., char *const envp[])
int execve(const char *pathname, char *const argv[], char *const envp[])
int execvp(const char *filename, char *const argv[])
int execlp(const char *filename, const char *arg, ...)
函数返回值 成功:函数不会返回
出错:返回-1,失败原因记录在error中

命名语法

前4位 统一为:exec
第5位 l:参数传递为逐个列举方式 execl、execle、execlp
v:参数传递为构造指针数组方式 execv、execve、execvp
第6位 e:可传递新进程环境变量 execle、execve
p:可执行文件查找方式为文件名 execlp、execvp
简单例子
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*程序执行execlp函数和execv函数*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;

if((pid = fork()) < 0)//创建子进程child_1
{
//错误处理
perror("fork");
exit(0);
}
if(pid == 0)
{
//子进程child_1
printf("\nThis is child_1 process\n");
if(execlp("pwd","pwd",NULL) < 0)//转入执行pwd命令
{
perror("execlp");
exit(0);
}
printf("never be displayed");
}

//创建以NULL结尾的字符串数组指针以便execv函数使用
char *arg[] = {"ls",NULL};
if((pid = fork()) < 0)//创建子进程child_2
{
perror("fork");
exit(0);
}
if(pid == 0)
{
//子进程child_2
printf("\nThis is child_2 process\n");
if(execv("/bin/ls",arg) < 0)//转入执行ls命令
{
perror("execv");
exit(0);
}
printf("never be displayed");
}

return 0;
}

运行结果如下:

三、singal函数

功能说明

通过signal函数可以设置系统对于某一信号的对应的操作
所在函数库:#include <signal.h>
使用原型:signal(int signum,sighandler_t handler)
参数说明:
signum:信号编号
handler的取值:
忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:信号处理函数名

简单例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//注册printsignum函数以便使用
void printsignum(int SignNum)
{
printf("The signal number is %d\n",SignNum);
}
int main()
{
printf("This is a process\n");
signal(SIGINT,printsignum);//注册SIGINT信号,键接收到该信号后会执行printsignum函数
while(1) //键入ctrl+c 执行pirntsignum函数
sleep(1);
printf("Never be displayed\n");

return 0;
}

运行结果如下:

四、make函数的使用

功能说明

使用make命令,系统会在当前目录下寻找Makefile的文件,并对它进行解释,处理,并执行相关动作。

简单例子

主程序main通过调用函数function_A执行相关操作

1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
//主函数中调用函数function_A
function_A();

return 0;
}

函数function_A在同一目录下的另一c文件function_A.c中

1
2
3
4
5
6
#include <stdio.h>
void function_A()
{
//输出相关文字
printf("This is function_A\n");
}

在同一目录下的makefile文件内容如下

1
2
all: main.c function_A.c
gcc -o all main.c function_A.c #次行以tab开头

在此目录下键入make命令,发现目录下生成了名为all的可执行文件,执行这个文件,观察到所需的操作已经执行,结果如下: