fork和lockf应用
2015-10-13 21:15
633 查看
**
实验目的
进一步认识并发执行的实质;分析进程争用资源的现象
实验过程
一,将每个进程输出一个字符改为每个进程输出一句话。
编译并运行shiyan1_test2.c
gcc shiyan1_test2.c ./a.out
输出如下:
系统中一共有4个进程,shiyan1_test2.c中有一个父进程,和父进程2次调用fork创建的2个子进程,另外一个进程是linux的shell进程,shell就是解析用户输入命令的终端,也就是和用户交互的这个黑框。父进程输出”I am the parent\n”,第一个子进程输出”I am the first child\n”,第二个子进程输出”I am the second child\n”,shell进程以命令提示符的形式输出,[root@localhost os]#,代表在这个shell进程中,用户是超级用户root,主机名为本地主机localhost,当前路径是在os文件夹中,#是root用户的专用命令提示符,普通用户是美元符号$。
运行多次,会发现上述4句话的输出顺序是不固定的,也就是说进程调度的顺序是会变化的,并且不一定和代码中创建的顺序一致,我们可以看到shiyan1_test2.c三句话的先后顺序本应该是”I am the first child\n”,”I am the second child\n”,”I am the parent\n”,而实际运行顺序并非如此,进程调度的顺序主要由操作系统调度程序决定,而不是用户编写的代码决定。
二, 在父进程fork之前,输出一句话,这句话后面不加“\n”或加“\n”。
我们先来运行不带有“\n”的情况:
编译并运行shiyan1_test3.c
输出结果如下:
可以看到4个进程分别输出:
进程1:I am the sentence befork the parent fork a
进程2:[root@localhost os]#
进程3:I am the sentence befork the parent fork c
进程4:I am the sentence befork the parent fork b
即除了shell进程外,每个进程都打印了这句话。我们来分析一下代码,表面上看起来,子进程应该从fork()处继续往下执行,并不会执行打印I am the sentence befork the parent fork这句话,而事实并非如此。在父进程第1次调用fork()前,执行printf,由于输出时,我们要将内存中的数据输出到磁盘中,而磁盘读取速度远小于内存,为了解决磁盘与内存速度不匹配的问题,系统会先将要printf的内容存到磁盘缓冲区buffer,也就是将I am the sentence befork the parent fork这句话写入buffer。而子进程会继承父进程的buffer内容,于是在子进程中也会打印这句话,第1个子进程会输出,第2个子进程也会输出。
接下来,我们加上“\n”:
编译运行shiyan_test4.c
输出结果:
4个进程分别输出:
进程1:a
进程2:[root@localhost os]#
进程3:c
进程3:b
I am the sentence befork the parent fork这句话只被输出了一次,这是因为系统检测到“\n”会自动清空缓冲区,于是子进程继承父进程时,缓冲区里面没有要打印这句话的信息,于是在2个子进程中只分别输出c和b。
要清空缓冲区,还可以用fflush(stdout)函数,用以清空系统标准输出流。修改shiyan_test4.c
句子只被输出一次, fflush(stdout); //清空输出流;stdout是系统自动生成的指针标准输出流,与之对应的还有标准输入流stdin,清空输入流可以用fflush(stdin);
三,在程序中使用系统调用lockf来给每一个进程加锁,实现进程之间的互斥。
1.lockf放在循环内部,执行休眠
我们在shiyan_test4.c的基础上修改代码,分别将 a, b,c替换为 parent,daughter和son。子进程1输出5次daughter,并用lockf(1,1,0)给stdout加锁,lockf(1,0,0)解锁,解锁后令进程休眠3秒,即sleep(3);子进程2输出5次son,同理用lockf加锁和解锁, 解锁后也休眠3秒。parent不做处理,和shiyan_test4.c一致。
编译shiyan_test5.c并运行
gcc shiyan_test5.c
./a.out
运行结果如下:
可以看到子进程1和子进程2交替执行,5句daughter没有连续输出,5句son也没有连续输出。lockf()的基本功能是实现资源的互斥访问,即同一时刻同一个资源只允许一个进程访问,本次实验中的资源即系统的标准输出stdout,互斥的结果为同一时刻只可以有一个进程执行输出,并且不可以被打断。daughter进程先用lockf(1,1,0)锁上stdout,执行输出之后,立即用lockf(1,0,0)释放stdout,而此时进程没有立即进入下一次循环,而是休眠了3秒,也就是说没有立即又给stdout加锁,在休眠的这段时间,son可以获得stdout资源,于是son执行输出;同理,son加锁,输出,释放锁之后,和daughter一样,也执行了休眠,于是daughter可以获得stdout资源,于是就出现了两个进程交替执行输出的情况。
多运行几次,还会出现以下结果,不是严格的交替执行,但是都可以说明进程的输出循环会被打断,不会连续输出5次。
2.接下来我们继续修改代码,不执行休眠
再次编译并运行,输出结果如下
多执行几次,会发现5句daughter和5句son的输出都是连续的,son,daughter,parent和shell的顺序则有可能改变。这比较容易理解,拿daughter进程来解释,daughter进程开始循环,加锁,输出,然后释放锁,不进行休眠,立即进入下一次for循环,紧接着又加锁,也就是说释放锁之后理解又加锁,间隔时间极其短暂,忽略两次循环的切换时间,stdout资源相当于一直处于加锁状态,其他进程不可能获得stdout资源,必须等daughter5次循环完毕之后,才可以获得,同理,son进程运行的时候也是一样,循环不会被其他进程打断。
3,将lockf放在循环外面
编译运行,输出结果如下
我们可以推断出不论运行多少次,son和daughter的输出都是连续的。有了前面两个例子,这个例子就很容易理解了,在循环外部给stdout加上锁,整个循环过程中其他进程都不可以获得stdout资源,必须等该进程循环完毕,释放资源后其他进程才可以获得stdout,并执行输出。
同样,我们还可以继续修改代码,但总而言之,资源被锁住的时候,只允许为该资源上锁的进程访问,其他进程均不可以再访问。本例中的资源是标准输入输出,而文件的读取,程序脚本的运行,也可以用lockf来进行互斥访问,在实际应用中,lockf有在互斥访问方面具有举足轻重的作用。
五,当父进程fork子进程后,父进程和子进程如何执行程序的?
在父进程中,fork返回新创建子进程的进程ID;父进程紧接着fork语句的下一句话执行;子进程中,fork返回0;子进程会继承父进程大部分的资源,包括缓冲区等,只有少部分不会继承,每个进程都有唯一的进程id号,可以利用getpid()获得进程id,getppid()获得父进程的id; 如果出现错误,fork返回一个负值;表明子进程创建失败。
fork和lockf应用
**实验目的
进一步认识并发执行的实质;分析进程争用资源的现象
实验过程
一,将每个进程输出一个字符改为每个进程输出一句话。
/*shiyan1_test2.c *Created on 2015-10-07 *Author:Wanglin *output a sentence */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; p1=fork(); if(!p1) printf("I am the first child\n"); else{ p2=fork(); if(!p2) printf("I am the second child\n"); else printf("I am the parent\n"); } return 0; }
编译并运行shiyan1_test2.c
gcc shiyan1_test2.c ./a.out
输出如下:
[root@localhost os]# gcc shiyan3_test2.c [root@localhost os]# ./a.out I am the parent [root@localhost os]# I am the second child I am the first child ./a.out I am the parent [root@localhost os]# I am the second child I am the first child
系统中一共有4个进程,shiyan1_test2.c中有一个父进程,和父进程2次调用fork创建的2个子进程,另外一个进程是linux的shell进程,shell就是解析用户输入命令的终端,也就是和用户交互的这个黑框。父进程输出”I am the parent\n”,第一个子进程输出”I am the first child\n”,第二个子进程输出”I am the second child\n”,shell进程以命令提示符的形式输出,[root@localhost os]#,代表在这个shell进程中,用户是超级用户root,主机名为本地主机localhost,当前路径是在os文件夹中,#是root用户的专用命令提示符,普通用户是美元符号$。
运行多次,会发现上述4句话的输出顺序是不固定的,也就是说进程调度的顺序是会变化的,并且不一定和代码中创建的顺序一致,我们可以看到shiyan1_test2.c三句话的先后顺序本应该是”I am the first child\n”,”I am the second child\n”,”I am the parent\n”,而实际运行顺序并非如此,进程调度的顺序主要由操作系统调度程序决定,而不是用户编写的代码决定。
二, 在父进程fork之前,输出一句话,这句话后面不加“\n”或加“\n”。
我们先来运行不带有“\n”的情况:
/*shiyan1_test3.c *Created on 2015-10-07 *Author Wanglin *print a sentence without '\n'or with '\n' befork the parent fork */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; printf("I am the sentence befork the parent fork "); //printf("I am the sentence befork the parent fork\n"); p1=fork(); if(!p1) printf("b\n"); else{ p2=fork(); if(!p2) printf("c\n"); else printf("a\n"); } return 0; }
编译并运行shiyan1_test3.c
输出结果如下:
[root@localhost os]# ./a.out I am the sentence befork the parent fork a [root@localhost os]# I am the sentence befork the parent fork c I am the sentence befork the parent fork b
可以看到4个进程分别输出:
进程1:I am the sentence befork the parent fork a
进程2:[root@localhost os]#
进程3:I am the sentence befork the parent fork c
进程4:I am the sentence befork the parent fork b
即除了shell进程外,每个进程都打印了这句话。我们来分析一下代码,表面上看起来,子进程应该从fork()处继续往下执行,并不会执行打印I am the sentence befork the parent fork这句话,而事实并非如此。在父进程第1次调用fork()前,执行printf,由于输出时,我们要将内存中的数据输出到磁盘中,而磁盘读取速度远小于内存,为了解决磁盘与内存速度不匹配的问题,系统会先将要printf的内容存到磁盘缓冲区buffer,也就是将I am the sentence befork the parent fork这句话写入buffer。而子进程会继承父进程的buffer内容,于是在子进程中也会打印这句话,第1个子进程会输出,第2个子进程也会输出。
接下来,我们加上“\n”:
/*shiyan3_test4.c *Created on 2015-10-07 *Author:Wanglin *Version 1.0 * print a sentence with '\n' befork the parent fork */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; printf("I am the sentence befork the parent fork \n"); p1=fork(); if(!p1) printf("b\n"); else{ p2=fork(); if(!p2) printf("c\n"); else printf("a\n"); } return 0; }
编译运行shiyan_test4.c
输出结果:
[root@localhost os]# ./a.out I am the sentence befork the parent fork a [root@localhost os]# c b
4个进程分别输出:
进程1:a
进程2:[root@localhost os]#
进程3:c
进程3:b
I am the sentence befork the parent fork这句话只被输出了一次,这是因为系统检测到“\n”会自动清空缓冲区,于是子进程继承父进程时,缓冲区里面没有要打印这句话的信息,于是在2个子进程中只分别输出c和b。
要清空缓冲区,还可以用fflush(stdout)函数,用以清空系统标准输出流。修改shiyan_test4.c
/*shiyan3_test4.c *Created on 2015-10-07 *Author:Wanglin *Version 2.0 * using fflush(stdout) to clear the buffer */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; printf("I am the sentence befork the parent fork "); fflush(stdout); p1=fork(); if(!p1) printf("b\n"); else{ p2=fork(); if(!p2) printf("c\n"); else printf("a\n"); } return 0; }
[root@localhost os]# ./a.out I am the sentence befork the parent fork a [root@localhost os]# c b
句子只被输出一次, fflush(stdout); //清空输出流;stdout是系统自动生成的指针标准输出流,与之对应的还有标准输入流stdin,清空输入流可以用fflush(stdin);
三,在程序中使用系统调用lockf来给每一个进程加锁,实现进程之间的互斥。
1.lockf放在循环内部,执行休眠
我们在shiyan_test4.c的基础上修改代码,分别将 a, b,c替换为 parent,daughter和son。子进程1输出5次daughter,并用lockf(1,1,0)给stdout加锁,lockf(1,0,0)解锁,解锁后令进程休眠3秒,即sleep(3);子进程2输出5次son,同理用lockf加锁和解锁, 解锁后也休眠3秒。parent不做处理,和shiyan_test4.c一致。
/*shiyan1_test5.c *Created on 2015-10-07 *Author:wanglin *输出多条语句,flock,sleep(3) */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; p1=fork(); if(!p1) { int i; for(i=0;i<5;++i){ lockf(1,1,0); printf("daughter %d\n",i); lockf(1,0,0); sleep(3); } } else{ p2=fork(); if(!p2) { int j; for(j=0;j<5;++j) { lockf(1,1,0); printf("son %d\n",j); lockf(1,0,0); sleep(3); } } else printf("parent \n"); } return 0; }
编译shiyan_test5.c并运行
gcc shiyan_test5.c
./a.out
运行结果如下:
[root@localhost os]# ./a.out parent [root@localhost os]# son 0 daughter 0 son 1 daughter 1 son 2 daughter 2 son 3 daughter 3 son 4 daughter 4
可以看到子进程1和子进程2交替执行,5句daughter没有连续输出,5句son也没有连续输出。lockf()的基本功能是实现资源的互斥访问,即同一时刻同一个资源只允许一个进程访问,本次实验中的资源即系统的标准输出stdout,互斥的结果为同一时刻只可以有一个进程执行输出,并且不可以被打断。daughter进程先用lockf(1,1,0)锁上stdout,执行输出之后,立即用lockf(1,0,0)释放stdout,而此时进程没有立即进入下一次循环,而是休眠了3秒,也就是说没有立即又给stdout加锁,在休眠的这段时间,son可以获得stdout资源,于是son执行输出;同理,son加锁,输出,释放锁之后,和daughter一样,也执行了休眠,于是daughter可以获得stdout资源,于是就出现了两个进程交替执行输出的情况。
多运行几次,还会出现以下结果,不是严格的交替执行,但是都可以说明进程的输出循环会被打断,不会连续输出5次。
2.接下来我们继续修改代码,不执行休眠
/*shiyan1_test5.c *Created on 2015-10-07 *Author:wanglin *输出多条语句,flock,sleep(3) */ # include<stdio.h> # include<unistd.h> int main() { int p1,p2; p1=fork(); if(!p1) { int i; for(i=0;i<5;++i){ lockf(1,1,0); printf("daughter %d\n",i); lockf(1,0,0); //sleep(3); } } else{ p2=fork(); if(!p2) { int j; for(j=0;j<5;++j) { lockf(1,1,0); printf("son %d\n",j); lockf(1,0,0); //sleep(3); } } else printf("parent \n"); } return 0; }
再次编译并运行,输出结果如下
多执行几次,会发现5句daughter和5句son的输出都是连续的,son,daughter,parent和shell的顺序则有可能改变。这比较容易理解,拿daughter进程来解释,daughter进程开始循环,加锁,输出,然后释放锁,不进行休眠,立即进入下一次for循环,紧接着又加锁,也就是说释放锁之后理解又加锁,间隔时间极其短暂,忽略两次循环的切换时间,stdout资源相当于一直处于加锁状态,其他进程不可能获得stdout资源,必须等daughter5次循环完毕之后,才可以获得,同理,son进程运行的时候也是一样,循环不会被其他进程打断。
3,将lockf放在循环外面
编译运行,输出结果如下
./a.out parent [root@localhost os]# son 0 son 1 son 2 son 3 son 4 daughter 0 daughter 1 daughter 2 daughter 3 daughter 4
我们可以推断出不论运行多少次,son和daughter的输出都是连续的。有了前面两个例子,这个例子就很容易理解了,在循环外部给stdout加上锁,整个循环过程中其他进程都不可以获得stdout资源,必须等该进程循环完毕,释放资源后其他进程才可以获得stdout,并执行输出。
同样,我们还可以继续修改代码,但总而言之,资源被锁住的时候,只允许为该资源上锁的进程访问,其他进程均不可以再访问。本例中的资源是标准输入输出,而文件的读取,程序脚本的运行,也可以用lockf来进行互斥访问,在实际应用中,lockf有在互斥访问方面具有举足轻重的作用。
五,当父进程fork子进程后,父进程和子进程如何执行程序的?
在父进程中,fork返回新创建子进程的进程ID;父进程紧接着fork语句的下一句话执行;子进程中,fork返回0;子进程会继承父进程大部分的资源,包括缓冲区等,只有少部分不会继承,每个进程都有唯一的进程id号,可以利用getpid()获得进程id,getppid()获得父进程的id; 如果出现错误,fork返回一个负值;表明子进程创建失败。
相关文章推荐
- Linux socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝
- vivi下重新调整分区