系统编程一些提高小总结

系统编程一些提高小总结

使用缓冲提高效率

采用缓冲原因

在编写cp命令的过程中,采用了缓冲区的机制,那么为什么要采用缓冲区呢?

先从cp命令中看缓冲区的作用,首先实现cp命令是调用了read(),write()等内核函数进行实现,这里采取对缓冲区规定了大小,每次缓冲区中的数据被写入新文件后,都需要调用内核函数再往缓冲区中填充,可以想到如果缓冲区过小,需要不断地调用内核函数,cpu也要不断地切换内核态与用户态,想象一下极端的例子,甚至系统调用产生的开销会超过程序本身的执行。所以可以得出缓冲区可以明显的提升cpu的效率。

对于输入端,如果没有数据在缓冲区,每次读取都需要切换CPU等,十分浪费,相反如果将数据都存在缓冲区里面,等要读取的时候就可以一次性读取多数的数据。大大地提高了CPU的效率。 但是也同时会引出一个问题就是,当紧急情况断电,那么缓存中数据可能会因此丢失。

对于输出端,假如要将数据打印出来,打印机是低速的,CPU是高速的。将数据放入缓冲区,避免数据被分为多次打印,就此解放了CPU,使它能去处理其他任务。

所以缓冲对于I/O的处理效率提高是十分关键的。

提高who命令效率

由此想到编写who命令时,我们采用了每次读取一个数据结构的方法,那么能不能往里面添加一个缓冲区,一次读出多个utmp数据结构,要做的剩下只是显示这些数据结构,读完了再调用内核函数去取呢?

想想需要怎么实现,先一次性取出固定数量的utmp数据结构用数组保存,接着每次读出一个就把计数器加1,当计数器与取出的个数相等的时候就再去取。

 5 #define NRECS 16
 6 #define NULLUT (struct utmp *)NULL
 7 #define UTSIZE (sizeof (struct utmp))
 8 
 9 static char utmpbuf[NRECS * UTSIZE];    
10 static int num_recs;           
11 static int cur_rec;            
12 static int fd_utmp = -1;   


15 struct utmp* utmp_next() {
16     struct utmp* recp;
17     if (fd_utmp == -1) {
18         return NULLUT;
19     }
20     if (cur_rec == num_recs && utmp_reload() == 0) {
21         return NULLUT;
22     }
23     recp = (struct utmp*) &utmpbuf[cur_rec * UTSIZE];
24     cur_rec ++;
25     return recp;
26 }
27 
28 int utmp_reload() {
29     int aimt_read;
30     aimt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE);
31     num_recs = aimt_read / UTSIZE;
32     cur_rec = 0;
33     return num_recs;
34 }

这里的utmp_next函数是作为读取缓冲区的存在,它的参数为之前定义的struct utmp *,它的返回值也是struct utmp *,它以之前定义的结构体的指针作为参数,对缓冲区保存的结构体进行输出,直接对结构体指针进行操作,同时每次输出一个就把计数器加1,而函数返回后,再把计数器cur_rec重置为1,。

当然utmp_reload函数就是我们采用的缓冲区机制了,一次性读出16个utmp结构体放入数组进行存放,这里直接通过除以utmp所占字节来确定读取到的个数非常好,自己编写我一定会多一个中间变量同时多一步取余运算来确定当前读取的个数,其实是要摆脱这种自己思维上的固化,需要在自己写之前写步骤的清楚,而代码尽量做到简洁。

而main函数几乎不用变:

32 int main() {
33     struct utmp* utbufp;
34     if (utmp_open(UTMP_FILE) == -1) {
35         perror(UTMP_FILE);
36         exit(1);
37     }
38     while ((utbufp = utmp_next()) != (struct utmp*)NULL) {
39         show_info(utbufp);
40     }
41     utmp_close();
42     return 0;
43 }

可以看到只是需要多一步判断,检查读数计数器与加载计数器是否相同,如果相同说明已经没有数据可用了,而在show_info则实现了调用函数接口的操作,这样根本不需要改写show_info函数就可以完成需要的功能。

内核中的缓冲

不光切换su与普通用户需要消耗时间,为了提高磁盘的I/O操作的效率,内核也采用缓冲技术提高访问速度。linux系统中,每个进程有自己独立的缓冲区,叫做进程缓冲区,而系统内核也有个缓冲区叫做内核缓冲区。内核会对磁盘上的数据块作为缓冲,把磁盘数据块复制到内核缓冲区,当用户空间的进程需要从磁盘读数据时,若读取的数据刚好在内核缓冲区中时,内核不会直接读取磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。当所需要数据不在内核缓冲区时,内核会把相应的数据块加入到请求数据列表,然后把进程挂起,先为其他进程服务,而当执行请求数据时,内核也先会把数据块从磁盘读入内核缓冲区,再把数据复制到进程缓冲区后,最后把挂起的进程唤醒。

我们常用的read,write等,也是在做内核缓冲区和进程缓冲区的交换工作,read是从内核缓冲区到进程缓冲区,write则是相反,并不是我们所想的在直接和磁盘进行交互。

##对文件的改写操作

之前我们所做的许多工作都是在读取文件中数据,然后使用这些数据(显示或是写入其他文件),而这里说的改写操作并不是写入数据,否则我就会用写操作这一词了,而是直接对Linux文件的信息进行改写,例如之前编写了who命令,显示出了当前的登录用户信息,那么如果要改变存储utmp中用户的信息该怎么做呢?

第一想到的是write,但是稍微思考下就知道这是不可能的,因为直接使用write就算我们改写了文件,那也是更新了的下一个文件,不能直接改写当前文件,而这时候就要用到另一个系统调用lseek。

在UNIX以及类UNIX系统中,内核为每个打开的文件保存一个位置指针,所有打开的文件都有一个当前文件偏移量(current file offset)。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。使用 lseek 函数可以改变文件的 cfo 。

当我们从文件读数据时,内核从指针标明地方开始,读取指定字节,这也就是read函数要写清读取字节的原因,之后会移动文件指针,指向下一个未读取的字节,写文件操作也类似。

例如我们之前想做的对utmp文件进行改写操作,我们只需要lseek(fd,n*sizeof(struct utmp),SEEK_SET)即可,查看utmp文件显示信息后,需要去除第几个n就为几就行了,相当于把它的文件指针向后移动几个结构体,再向其中write即可。

系统调用的错误

内核通过全局变量errno指明错误类型

errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义。查看errno是调试程序的一个重要方法。当linux C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义。

在系统编程中出现错误时,我们可以通过查看errno的值推测出错的原因。这样不至于报错摸不着头脑。

#define EPERM        1  /* Operation not permitted */
#define ENOENT       2  /* No such file or directory */
#define ESRCH        3  /* No such process */
#define EINTR        4  /* Interrupted system call */
#define EIO          5   /* I/O error */
#define ENXIO        6  /* No such device or address */
#define E2BIG        7  /* Argument list too long */
#define ENOEXEC      8  /* Exec format error */
#define EBADF        9  /* Bad file number */
#define ECHILD      10  /* No child processes */

这里指摘取了10条,而所有的错误定义有100多条,我们可以根据不同错误做不同处理,平时在自己写代码时我们也完全可以使用errno,同时配合perror(),这样会让我们的代码更加规范。

发布了15 篇原创文章 · 获赞 13 · 访问量 9059

猜你喜欢

转载自blog.csdn.net/weixin_43122409/article/details/84535025
今日推荐