第一次代码优化

前言

之前大部分代码都是写好的,只是偶尔修改一下bug,添加新需求。只要代码不报错,没有语法错误,就能入库,现在新公司提交代码后,CTO也就是技术总监(现在软件部有一个BSP负责驱动和编译,两个嵌入式开发负责写代码,还有一个CTO负责整个团队)会仔细的review代码,并给出修改意见,这才是review的作用,原公司的review感觉只是个形式。

这第一次代码优化就是根据CTO的意见进行的,非常感谢他,如果没有人提出意见我可能一直不会有新的提升与收获。

下面就以此记录修改的地方

宏定义

现状

由于项目中的类很多,有很多类都使用了同一个文件、程序、字符串、字符长度等,在使用时为了图方便都写到了对应的cpp文件上方,造成了有很多意义重复的宏定义

优化

创建一个公有的头文件,里面只放宏定义,有两个及以上的类要用到的宏定义都放到这个头文件中,需要使用时包含这个头文件即可

枚举

现状

代码中有很多绝对数字,让人费解,如下,是判断当前网络设置的代码,对于数字让其他人很难理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (mode == 11)
{
/*TO DO*/
}
else if (mode == 18)
{
/*TO DO*/
}
else if (mode == 19)
{
/*TO DO*/
}
else
{
/*TO DO*/
}

优化

使用枚举让代码更加便于阅读,更方便维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum MS_NETWORK_MODE
{
MODE_LTE_ONLY=11,
MODE_LTE_5GNR=18,
MODE_5GNR_SA=19,
MODE_AUTO=40
};

if (mode == MODE_LTE_ONLY)
{
/*TO DO*/
}
else if (mode == MODE_LTE_5GNR)
{
/*TO DO*/
}
else if (mode == MODE_5GNR_SA)
{
/*TO DO*/
}
else
{
/*TO DO*/
}

system()

在代码中经常使用system()完成一些Linux的操作,如复制,移动,删除等。我一直认为这是很正常的,直到CTO给我说了system()的调用过成我才知道为什么要优化他。

system()的调用过程为:

  1. 当参数为NULL时直接返回,不为NULL时fork()一个子进程
  2. 父进程等待,子进程使用execl 启动shell命令
  3. shell去执行我们下发的命令
  4. 执行完成,父进程等待结束,返回结果

第三步是多余的操作,相当于shell又fork了一个进程去执行我们的命令(不知道这样理解是否正确),完全可以跳过这步,于是有了自己实现的do_system(),也就是上篇服务管理系统的核心函数,如下:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
int do_system(unsigned char wait, int *svc_pid, const char *exec_file, list<string> arg_list)
{
pid_t pid;
int status;
sigset_t chldmask, savemask;
list<string>::iterator list_iterator;

if (exec_file == NULL)
{
return (-1);
}

/* now block SIGCHLD */
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &chldmask, &savemask))
{
return (-2);
}

pid = fork();
if (pid < 0)
{
/* Error. */
sigprocmask(SIG_SETMASK, &savemask, NULL);
return (-3);
}
else if (pid == 0)
{
/* Child. */
int n = 0;
char **argv;
int argc = 0;
char **p;
n = arg_list.size();
p = argv = (char **)malloc((n + 1) * sizeof(char *));
p[argc] = (char *)exec_file;
if (n > 0)
{
for (list_iterator = arg_list.begin(); list_iterator != arg_list.end(); list_iterator++)
{
p[++argc] = (char*)(*list_iterator).c_str();
ALOGI("param:%s",p[argc]);
}
}

p[++argc] = NULL;

n = execvp(exec_file, argv);
/* on error */
free(argv);
exit(n);
}
else
{
/* Parent. */
if (svc_pid)
{
*svc_pid = pid;
}

if (wait)
{
while (waitpid(pid, &status, 0) < 0)
{
if (errno != EINTR)
{
sigprocmask(SIG_SETMASK, &savemask, NULL);
return (-1);
}
}
}
else
{
sigprocmask(SIG_SETMASK, &savemask, NULL);
return (0);
}
}

sigprocmask(SIG_SETMASK, &savemask, NULL);
return WEXITSTATUS(status);
}

其中参数列表list可以换成C语言的多参数,以便在C中使用,关于多参数我后面也会单独记录

多命令连续使用system()

现状

在代码中有连续的system()调用,即使换成我们自己实现的system()也会fork很多进程,例如备份文件的代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
system("rm -f /usr/www/Backup.zip");
system("mkdir /usr/www/backup");
system("mkdir /usr/www/backup/etc");
system("mkdir /usr/www/backup/data");
system("mkdir /usr/www/backup/cgi");
system("cp -p /data/cgi/admin_config /usr/www/backup/cgi/admin_config");
/*
...cp file more
*/
system("cp -p /etc/timezone /usr/www/backup/timezone");

system("tar -zcf /usr/www/Backup.zip /usr/www/backup/*");
system("rm -rf /usr/www/backup");

优化

将命令全部写到文件中,然后调用shell命令运行文件,避免多次调用system(),简单实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define SHELL_FILE   /tmp/cmd_shell.sh

FILE *fp;
char line[128] = {0};

if ((fp = fopen(SHELL_FILE, "w")) != NULL)
{
sprintf(line, "rm -f /usr/www/Backup.zip\n");
fwrite(line, sizeof(char), strlen(line), fp);
sprintf(line, "mkdir /usr/www/backup\n");
fwrite(line, sizeof(char), strlen(line), fp);
sprintf(line, "mkdir /usr/www/backup/etc\n");
fwrite(line, sizeof(char), strlen(line), fp);
sprintf(line, "mkdir /usr/www/backup/data\n");
/*
more sprintf
*/
fflush(fp);
fclose(fp);
}
system("sh "SHELL_FILE);

fprintf()

现状

在写文件时,不止上面那种执行命令的情况,包括修改配置文件需要写很多行,使用sprintf(),不仅要多调用一次fwrite,还要担心定义的数组line是否会越界,所以可以直接使用fprintf()

优化

fprintf()我之前也是没有用过的,也是看到建议后才知道,真惭愧,优化后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define SHELL_FILE   /tmp/cmd_shell.sh

FILE *fp;
if ((fp = fopen(SHELL_FILE, "w")) != NULL)
{
fprintf(fp, "rm -f /usr/www/Backup.zip\n");
fprintf(fp, "mkdir /usr/www/backup\n");
fprintf(fp, "mkdir /usr/www/backup/etc\n");
fprintf(fp, "mkdir /usr/www/backup/data\n");
/*
more fprintf
*/
fflush(fp);
fclose(fp);
}
system("sh "SHELL_FILE);

总结

第一次优化的点就是这些了,后续有继续优化会及时更新新的文章.

我买一本 《Effective Modem C++》,抽空阅读后也会将收获(笔记)记录下来


第一次代码优化
https://carl-5535.github.io/2021/10/24/工作总结/第一次代码优化/
作者
Carl Chen
发布于
2021年10月24日
许可协议