08 int main() 09 {10 int num, fd,i;// num变量用于存放从buff转换而来的二进制形式的整数 // fd是打开data文件之后的描述符,用于下文对data文件的读写操作 11 char buff[100];// 定义的buff字符数组用于存放从文件中读出的数字字符形式的整数 12 fd = open(\"data\//打开文件 13 if(fd<=0){//如果文件不存在 14 printf(\"open error\\n\"); 15 return 1; 16 }
17 for(i=1;i<=1000000;i++){//循环一百万次循环循环 18 lseek(fd, 0, SEEK_SET);// 将文件的读写指针归零
19 int len = read(fd, buff, 100);//len存放从文件读入的数字符的长度 20 buff[len]='\\0';//在字符串后添加空格代表结束
21 num = atoi(buff);// 调用atoi函数,将字符串转换为二进制形式,并存放在num 22 sprintf(buff, \"%d\\n\//num+1,循环10000次后就是1000000+num 23 lseek(fd, 0, SEEK_SET);// 将文件的读写指针归零即置于起始字节之处 24 write(fd, buff,strlen(buff));//将更新后的数重新写入文件中 25 }
26 close(fd);//关闭文件 27 return 0;//返回 28 }
问题:
1. 请在linux中编译、链接、执行这个程序。如果该程序只作为单个进程执行,请观察运
行结果,并分析代码。
首先向data文件中输入1107,并进行保存。 该程序分析如下:
第10行中定义变量num存放从buff的字符串中转换而来的二进制形式的整数,变量fd存放打开data文件之后的描述符;第11行中定义长度为100的字符数组,用于存放从data文件中读出的数字字符形式的整数;第12行打开data文件;第13-16行判断文件是否打开,如果没有,则输出open error提示;第17-25行,循环1000000次操作,第18行,在每次循环都将文件的读写指针指向文件的起始位置;第19行,len保存读入字符串的长度;第20行,在字符串结尾加入空格符以示结束;第21行,调用atoi()函数,将buff的字符串转变为二进制数;第22行,对转变得二进制数进行+1操作;第23行,把文件的读写指针至于文件起始位置;第24行,将更新的数据重新写回data文件中。如此循环一百万次,相当于data中存放的数据加上一百万,所以得出图中所示结果。
2.在不改变程序代码的情况下,怎样把上述程序作为多个进程来并发执行?请观察并发执行情况下的运行结果,分析运行结果错误的具体原因。
答:要实现多进程并发执行,可多个窗口对同一程序进行运行。
由于data文件初始值为1234,所以两个进程在并发执行的情况下,正确结果应该为2001234,可是最后结果却为1509625。显然与正确结果不符。此主要原因在于,在两个进程同时执行的时候,可能会在同一时间对num进行操作,导致重读,如原本进程1对2加1后为3,进程而对3加1后为4.而现实中可能产生的情况是进程1和进程2同时对2进行加1操作,所以得到最终结果是3而不是4;另一种情况是,进程1已进行了加1操作但在保存到文件之前,进程2执
行到读取文件数据,因此,进程2读取的还是原本数据,进行加1操作后仍然是进程1执行操作后的数据。所以当多进程并发执行时,最后得出的结果与预想中的结果不同。
3.请修改上述代码,采用上锁方式,以保证并发执行时运行结果的正确性,并分析得到了正确结果的原因。
修改后,代码如下: #include #include #include #include #include #include #include int main() {struct flock fl;//定义结构体flock,设置锁 int num, fd,i; char buff[100];
fd = open(\"data\ if(fd<=0){
printf(\"open error\\n\"); return 1; }
fl.l_whence = SEEK_SET;//设置文件读写位置 fl.l_start = 1;//开始位置为1 fl.l_len = 1;//长度为1
fl.l_pid = getpid();//获取进程 for(i=1;i<=1000000;i++){
fl.l_type = F_WRLCK;//对文件加锁
fcntl(fd, F_SETLKW, &fl);//对文件逐个扫描
lseek(fd, 0, SEEK_SET); int len = read(fd, buff, 100); buff[len]='\\0'; num = atoi(buff);
sprintf(buff, \"%d\\n\ lseek(fd, 0, SEEK_SET); write(fd, buff,strlen(buff));
fl.l_type = F_UNLCK;//解锁文件 fcntl(fd, F_SETLKW, &fl); }
close(fd); return 0; }
该程序分析结果如下:当进程1对文件进行操作时,对文件进行加锁,防止其他进程对文件同时操作,也就是,其他进程阻塞。当进程1完成操作对文件解锁后,进程2被唤醒,进而对文件进行操作,在操作前,同样对文件进行加锁。如此类推,对个进程交替执行,保证了结果的正确性。
实验心得:
写出你对单次执行和并发进程的感受,操作系统对并发进程控制的重要性。
在数据量比较少的情况下,单次执行的速度比并发执行的速度占有一定的优势。但在数据量的达到百万上亿的时候,单次执行的速度明显不尽人意。而并发执行能多个进程同时对一个程序进行操作,就如,工厂里生产汽车零件,一个人一天的产量必定少于多个人的共同完成生产零件一个的产量。当然,物极必反,并不是说进程越多越好,当进程增多,就容易出现进程空闲的情况,导致性能的下降。操作系统每天都要处理庞大的数据,若使用单次执行方式,不单会导致系统运行速度慢,还会让使用者陷于崩溃的等待中,所以,操作系统对并发执行的应用与控制显得更重要。