操作系统的并发机制实例

UNIX/Linux系统中,调用fork的进程成为父进程,新创建的进程叫做子进程;fork会完成下列操作:

  • 为子进程赋一个唯一的PID
  • 做一个父进程上下文的逻辑副本
  • 增加与该进程相关联的文件表和索引节点表的引用数。例如:父进程打开的文件子进程可继续使用
  • 对父进程返回子进程的PID,对子进程返回0,出错返回-1

1.下面的代码通过父进程创建了一个子进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <sys/types.h> /* 是Unix/Linux系统的基本系统数据类型的头文件,含有seze_t, time_t, pid_t等类型 */
#include <stdio.h>
#include <unistd.h> /* 定义了 getpid() getppid() 等函数 */
#include <stdlib.h> /* 定义了 exit() 等函数 */
#include <sys/wait.h> /* wait() waitpid() */

/* getpid()返回值为调用getpid()进程的PID,getppid()返回调用这个函数进程的父进程的PID */

void main()
{
if(fork()==0)
{
printf("Child process2! PID=%d PPID=%d\n", getpid(), getppid());
}
else
{
printf("Parent process! PID=%d PPID=%d\n", getpid(), getppid());
}
}

运行结果:

可以看到,先输出了else分支的语句,再输出了if分支的语句。因为fork会创建一个和原来进程几乎完全相同的进程,fork对父进程返回子进程的PID,因此首次父进程执行会进入到else语句,对子进程返回0,那么子进程会执行if语句

2.下列的代码,创建了两个子进程,父进程和这两个子进程被调用时会分别输出他们的PID和PPID:

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> /* 是Unix/Linux系统的基本系统数据类型的头文件,含有seze_t, time_t, pid_t等类型 */
#include <stdio.h>
#include <unistd.h> /* 定义了 fork() getpid() getppid() 等函数 */
#include <stdlib.h> /* 定义了 exit() 等函数 */
#include <sys/wait.h> /* wait() waitpid() */

/* getpid()返回值为调用getpid()进程的PID,getppid()返回调用这个函数进程的父进程的PID */

void main()
{
printf("Parent process! PID=%d PPID=%d\n", getpid(), getppid());

if(fork()==0)
{
printf("Child process1! PID=%d PPID=%d\n", getpid(), getppid());
}

if(fork()==0)
{
printf("Child process2! PID=%d PPID=%d\n", getpid(), getppid());
}

int status;
wait(&status); /* 避免父进程在子进程终止之前终止 */
}

可是运行结果确创建了三个子进程:

为什么会输出两个process2?而且最后一个process2的父进程ID是process1的进程ID?

假定当前进程为A

第一个fork()会产生第一个子进程A1

第二个fork()会产生第二个子进程A2

子进程A1调用最后的那个fork(),产生第三个子进程A11

解决方法:在第一个if语句中添加一个return

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
#include <sys/types.h> /* 是Unix/Linux系统的基本系统数据类型的头文件,含有seze_t, time_t, pid_t等类型 */
#include <stdio.h>
#include <unistd.h> /* 定义了 fork() getpid() getppid() 等函数 */
#include <stdlib.h> /* 定义了 exit() 等函数 */
#include <sys/wait.h> /* wait() waitpid() */

/* getpid()返回值为调用getpid()进程的PID,getppid()返回调用这个函数进程的父进程的PID */

int main()
{
printf("Parent process! PID=%d PPID=%d\n", getpid(), getppid());

if(fork()==0)
{
printf("Child process1! PID=%d PPID=%d\n", getpid(), getppid());
return 0;
}
if(fork()==0)
{
printf("Child process2! PID=%d PPID=%d\n", getpid(), getppid());
}

int status;
wait(&status); /* 避免父进程在子进程终止之前终止 */
return 0;
}

3.下面代码创建了三个进程,执行同一个函数:

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
#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h>
#define NUM 5

void print_msg(char *m)
{
int i;
for(i = 0; i<NUM; i++){
printf("%s", m);
fflush(stdout);
sleep(1);
}
}

int main(void)
{
if(fork()==0){
print_msg("Good ");
return 0;
}
if(fork()==0){
print_msg("Morning ");
return 0;
}
if(fork()==0){
print_msg("gtfly\n");
}
return 0;
}

linux下time命令可以获取到一个程序的执行时间,上面打印出了程序运行在用户态的时间、内核态的时间和总时间。但为什么总时间才0.004?

因为上面主进程执行完后并没有等待子进程,直接运行完毕;time获取的是主进程的执行时间,所以才不到1s

修改:

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
#include <stdio.h> 
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define NUM 5

void print_msg(char *m)
{
int i;
for(i = 0; i<NUM; i++){
printf("%s", m);
fflush(stdout);
sleep(1);
}
}

int main(void)
{
if(fork()==0){
print_msg("Good ");
return 0;
}
if(fork()==0){
print_msg("Morning ");
return 0;
}
if(fork()==0){
print_msg("gtfly\n");
}
int status;
wait(&status);

return 0;
}

正常的结果:

如果不用多进程的话,会耗费15s的时间执行代码