给Linux开发者提出的一些建议
2011-01-14 10:19:56 阿炯

译者提示—这篇文章目的在于建议Linux开发者应更加注意方便用户的使用,改进软件包管理器,使得用户在安装Linux软件时少一些迷惑。

在Linux下安装软件能够引起新手的迷惑,同样也会使有经验的朋友或喜或悲。下面是在Linux下安装软件我们需要改变的20件事。

1.开源并不仅仅是源代码
“它是开源的,这是源代码。”可能会被忽略。多数用户实际上并不需要源代码,他们想要一个二进制文件。开发者应该提前将他们程序打包,确实需要鼓励开发者这样做。

2.如何运行
“我已经安装了Foo,但是如何运行呢?”在相关论坛上没有看到类似提问的恐怕没有几人吧。所有遵循Freedesktop.org 标准的窗口管理器,都会遵循标准XDG 关于菜单入口的桌面文件规定。安装一个图形化程序就不用抱怨了。

3.标准化界面
忘记关于文件包格式的争论吧,它将永远不会发生。我们需要一个标准软件包图形界面管理器,可以安装所有的软件包。设想一下,Synaptic在Ubuntu和 Fedora上运行,知道是采用Debs包还是RPMs软件包格式,那该多好啊。

4.更容易地添加软件仓库repositories
添加repositories,经常是从浏览器复制粘贴很长、很神秘的文本字符串到终端。一个标准的repository文件会使浏览器启动合适的包管理器将其添加到repository,就是出现一个对话框“are you sure/do you trust this”。

5.更简单地源代码编译
多少程序没有编译器和安装说明呢?很多都有通用的自动生成工具。这很容易呀。那为什么不给用户生成一个Install.sh脚本呢?同时检查一下依赖关系嘛。

6.Autotools = yuck
Autotools 很慢,看起来有一种神秘感。开发者主要使用Autotools。终端用户不应该看到这种东西。

7.降低文件系统杂乱程度
真有必要把文件安装到眼花缭乱的目录中吗?从软件包管理器安装程序是个不错的建议,卸载时候也可以知道把谁给清除了。构建源代码可能在卸载或从系统中移除时不够人性化,尤其是开发者不提供卸载文件时。

8.标准综合包
若是我们不能在单文件包格式上达成协议,标准包管理又从何谈起呢?

9.标准软件包名字
为什么不同的发行版命名同一个软件包会有不同的名字?如果在发行版本之间有一致的命名,解决软件包的依赖关系是不是会更容易些呢?

10.标准软件包拆分
不仅是软件命名需要统一,在每个发行版本里,次软件包也需命名一致。对上游开发者来说,一致性还有一段路要走。

11.去除 -dev软件包
当我们尝试编译源代码时,包含库头文件的-dev 或 -devel软件包会带来无穷的迷惑,比如经常出现像”libfoo not found”这样的信息。当安装GCC或Autotools时,自动安装相关的 -dev 软件包,将会减少我们的痛苦。

12.自动完成源代码软件包的安装
如果每个发行版需要不同的软件包,或许单源软件包能够解决这一情况。但是如果软件包管理器能够自动下载、编译、安装源代码,这不就解决不同包需求了吗?

13.基于浏览器的软件包管理
现在,软件包管理器图形化界面已经很棒了,但是远程安装又得回到命令行下。运行在网络浏览器上的软件包管理器将会使得浏览和升级远程电脑上的软件更加方便。

14.我们需要这么多的软件包吗?
一些项目有源代码,也提供Deb和RPM包文件下载。对每个Ubuntu衍生版本来说,都有自己的软件包,别说SUSE和Fedora的衍生版了。开发者们,真的有必要让可怜的终端用户堕入深渊吗?

15.非单一目录安装
有时,软件在自己的目录里安装的想法会冒出来。嗯,看起来很有吸引力。但对我们用户来说,单击“安装”按钮运行程序,然后在菜单启动就行了。

16.从网页链接到软件管理器
一般来说,当发现想尝试软件所在的一个网址后,接着你开始在软件管理器里面寻找软件包,或冒险使用一个未经发行版本验证的网址的软件包。是不是,从URL启动软件包管理器进而寻找软件包,这样会不会更加方便一些呢?

17.安装后运行
如果安装一份非后台运行的软件,有可能一安装完成,就运行它。要是当安装完成后你喜爱的软件包管理器出现一个核对窗口,是不是更加方便?不必从菜单启动,直接单击“安装并运行”,就这么点事儿。

18.确保源代码在包数据库构建
不仅是从源代码安装有点痛苦,其实,包管理器也不知道你究竟已经安装了什么,所以依赖总是出现缺失,解决不好。要是有一个包管理器,能够从源码包构建,不仅缓解安装的痛苦,也能让我们知道安装了什么。

19.非全包软件包
应用程序和库文件分成单独的包,引起了依赖和其他的问题,但是这被大多数软件包管理器所有效解决。我们也可以通过窗口把所有的东西放在一个包里,这就意味着把分散在文件系统里不同版本的相同库文件聚合到了一起。

20.清除旧的依赖
当你安装软件时,它的依赖也被安装上了。但是当你移除软件包时,这些依赖还呆在系统里,逐渐填满你的硬盘。软件包管理器不仅应该移除不需要的依赖,还应该随时清理系统。


系统调用简介


系统调用(英语:system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态运行。如设备IO操作或者进程间通信。

用户空间(用户态)和内核空间(内核态)
操作系统的进程空间可分为用户空间和内核空间,它们需要不同的执行权限。其中系统调用运行在内核空间。

库函数
系统调用和普通库函数调用非常相似,只是系统调用由操作系统内核提供,运行于内核核心态,而普通的库函数调用由函数库或用户自己提供,运行于用户态。

典型实现(Linux)

Linux 在x86上的系统调用通过 int 80h 实现,用系统调用号来区分入口函数。操作系统实现系统调用的基本过程是:
应用程序调用库函数(API);
API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;
内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数;
中断处理函数返回到 API 中;
API 将 EAX 返回给应用程序。

应用程序调用系统调用的过程是:
把系统调用的编号存入 EAX;
把函数参数存入其它通用寄存器;
触发 0x80 号中断(int 0x80)。


系统调用(system calls),Linux内核,GNU C库(glibc)。


一位嵌入式开发大佬点评其行业与互联网行业的开发异同

本质型和应用型知识技能该如何掌握

本质型知识技能是一个人自身的基础和修养,最典型的就是我们说的基础知识、基本原理、基本技能。譬如一个软件工程师,编程语言的掌握就属于本质型知识技能;而应用型知识技能就是那些针对特定任务和需求的解决能力,譬如熟练使用word来排版文字就属于应用型知识技能。

程序员这个行业来说,互联网软件开发就比嵌入式软件开发更容易被时代淘汰。为什么?因为这两类开发虽然都属于软件开发,但是它们有本质的不同,这个本质不同就是开发工作中本质型知识技能和应用型知识技能占比差异很大。

互联网行业本身变化大,发展更快速,甚至每隔几年就会有革命性的变化。大家你去看看这几年新出现的编程语言(譬如GO、Dart、Cotlin、Swift等)都是互联网行业的。连语言都经常变,更不用说各种框架那简直是一年三变。所以这些行业技能中应用型技术的占比很高,你刚熟悉了一种语言或框架,结果时代迅速变化这东西迅速被淘汰,新东西迅速火爆开来,你如果没有及时赶上那就惨了,被快速行驶的时代列车抛下了。所以互联网开发就是个高速快节奏舞池,这也是为什么互联网行业普遍996普遍加班严重的原因。当整个舞台都在高节奏运转时,你要么咬牙快速跟上,要么被快速淘汰出局。更不用说还有很多新人每年进入,后浪随时准备把前浪拍死在沙滩上的。

嵌入式开发有他自己的游戏规则,嵌入式开发的知识和技能中,本质型的占比非常高,而应用型的占比低。这就造成嵌入式开发的特点是:很难学会,但是一旦学会很难被淘汰;互联网开发行业刚好相反,很好学会但是也很容易被淘汰。

譬如编程语言,嵌入式底层开发这么多年了还是C语言挑大梁。而C语言现在在用的主流还是C89标准(1989年制定的C标准),连C99都没几个用的。而嵌入式应用层开发主体是C++语言,虽然出了不少新标准(C++11、14、17、20等)但是实际差异很小,90%的主要特性和难度基调还都是多年前定下来那些。所以你想想,如果你做嵌入式开发,你一旦深度掌握了C/C++语言,多少年你都可以笑傲江湖。

再看看互联网开发,前些年“PHP是最好的语言”,现在已经没几个用了。Java前些年一统江湖,现在经常被Python压着打。谷歌、facebook、苹果等动不动还发明点自己的语言和编程框架,基本上你做下个项目会用到什么都不知道......

再看操作系统。嵌入式开发只要你学会了linux这一套,你在去看什么都发现似曾相似。所以对于一个熟悉linux内核知识体系的人,会从中非常受益。这就是嵌入式开发,它的难不在于变化,而在于它不变但是本身难度高,你很难掌握,但是你掌握了它之后就舒服了。因为就算有一个东西能取代linux,那么它必然很类似linux,其设计和使用到的技巧仍然是linux这一套。因为这些东西是本质型的,是由电子计算机系统的基本设计原理出发而来的。想要打破这些东西,除非彻底颠覆当前的计算机技术。譬如说以后发明的计算机不需要内存,譬如说量子纠缠之类的技术彻底革命了计算机。

但是前提是:你得先迈过那道坎,你得先掌握那么多难度高的本质型知识和技能,做个掌握本质型知识和技能的本质型人才吧!


成功软件开发者的9种编程习惯

有些人会想:只要程序运行结果好,就不管原程序编得怎样。但绝对不是这样的。软件不是一次性就做完的,有必要做修改,扩展等管理。所以原程序要尽量做成易看懂,管理方便。这样做,第一是为了软件开发者方便,其次还会影响到软件的性能。管理不方便的程序不会做出好的软件。

希望通过这篇文章能学到好的编程习惯。要理解这文章的内容,你至少要懂得1个开发工具语言。这里举例说明的都是C语言,但你对C语言没有了解也不要担心。这里说明的是原理而不是特定的语言。

1. 语句要结束得彻底---(冒号;)

程序员经常有的失误之一是忘记在语句结束后加一个冒号。这样的问题点不易发现,时而让程序员不知所措。编程时要时时注意每个语句是否以冒号结束,虽然不是所有语言都以冒号结束。下面有忘记点冒号的例子。

int main(void){
  /* 没有冒号,导致问题*/
  printf("Hello World!\n")
  return(0);
}

很多的人犯这样的错误。不到几条的程序是不难发现这样的问题,但1000条以上的程序里呢?查找那忘记写冒号了的语句不会是很容易的事。记住,结束一条语句,一定要写冒号,如同一般文章结束后点句号一样。

还有一种关于冒号的失误是不该写冒号的时候写冒号。有经验的程序员看到下面例子会觉得好笑,但笔者确实看到了很多这样的失误。

/* main() 后面不该写冒号 */
int main(int argc, char *argv[]);{
  printf("Hello World");
  return(0);
}

函数或Method后面是不该写冒号的。

2. 要适合使用空格和tab键

C语言是不分辨空格的,因此程序也可以不需要空格一直写下去,但这样的程序会是谁都看不懂的"很有难度"的程序,请看以下例子:
if(x==0) {a=b=c=d=MAX; x++;}

这样写,也许会节省空间,但不仅别人,编程的本人也会很难看懂。程序要写得容易看懂!
if(x == 0){
  a = b = c = d = MAX;
  x++;
}

这样写,看起来不很清楚吗?程序要有确切的空格才容易看得懂。

3. 统一使用大括号和切断方式

每个程序员使用大括号({})和改行的方式都有自己的习惯,这样把程序移交给别人继续做的时候,会出现混乱。比如像以下例子:
int main(){
  int x = 1;
  int y = 10;
  while(x < y ){
    printf("Value of x is %d\n", x);
   x++;
  }
}

有些程序员会这样写大括号:
int main(){
  int x = 1;
  int y = 10;
  while(x < y ){
    printf("Value of x is %d\n", x);
    x++;
  }
}

笔者是喜欢第二种方式。因为一段语句的开始和结束很明显。我们不能要求每个程序员都用某一种方式来编程,但一个程序里一定要统一。还有,看别人编的程序时要想到他人编程的习惯也许与你不同。

4. 不乱用if语句

有些人很喜欢用“if”语句,如下:
if(a == 0){
  a++;
  return(a);
}

if(a == 1){
  a += 5;
  return(a);
}

if(a == 2){
  a += 10;
  return(a);
}

if(a == 3){
  a += 20;
  return(a);
}

if(a == 4) exit(1);

有没有比这更好的办法呢?else if语句?不是。好的方法是用“switch-case”语句来写简便的程序:

switch(a){
  case 0: a++;
    return(a);

  case 1: a += 5;
    return(a);

  case 2: a += 10;
    return(a);

  case 3: a += 20;
    return(a);

  default: exit(1);
}

如果没有与a一致的值,会执行default里定义的作业,上面的例子是要执行结束。

5. 不乱用程序切断Block

很多人经常乱用程序切断。使用三个以上的切断是比较难以看懂的程序。请看下面例子:
int a = 10;
int b = 20;
int c = 30;
int d = 40;

if(a == 10){
  a = a + d;
  if(b == 20){
    b = b + a;
    if(c != b){
      c = c + 1;
      if(d > (a + b))
        printf("Made it all the way to the bottom!\n");
    }
  }
}

这也许是夸张了,但确实有很多人真的这样做。那如何写得更好一点呢?一种方法是用函数来分写:

void next(int a, int b, int c, int d){
  if(c != b){
    c = c + 1;
    if(d > (a + b))
      printf("Made it all the way to the bottom!\n");
  }
}

int main(){
  int a = 10;
  int b = 20;
  int c = 30;
  int d = 40;

  if(a == 10){
    a = a + d;
    if(b == 20){
      b = b + a;
      next(a, b, c, d);
    }
  }
  return(0);
}

要这样写,也许会增加工作量,但程序编得结构化,容易看懂,而且如果函数做得更好,也可以在其他地方再使用。

6. 写好注释

要养成写注释的习惯。特别是别人难以理解的程序或变量一定要注释,一个月后,也许你自己也需要看那注释呢。
int x = 100;
int y = 1000;

if(x < y)
  a = 0;
else
  a = 1;

你能知道上面的程序意味着什么?如果不知道变量x,y和a指的是什么,那么很难理解。让我们给它标个注释再看吧:

/*
* 检测损益的程序
*/

int x = 100;
/* x 是卖书的总额*/
int y = 1000
/* y是做书本的费用*/
int a;
/* 确认是否有赢利*/

/* 比较x和y: */
if(x < y)
/* 1指损失 */
  a = 1;
else
/*0指赢利 */
  a = 0;

这样注释,不懂C语言的人也会看懂各语句意味着什么,各变量指的是什么。写注释是好习惯,但不要什么都注释。注释是为了好理解,不是为了写长编文章。

int profit = 1;
/* 赢利等于1 ?? */
int loss = 0;
/* 亏损等于 0 ?? */

/* 如果赢利等于1 */
if(profit == 1)
  /* 打印出“得赢利啦”??*/
  printf("We made a profit!\n");
/* 如果不是 */
  else
  /*打印出“我们亏损啦”*/
  printf("We made a loss!\n");

这样注释是浪费时间。一般是定变量或变量值的时候需要注释,还有说明程序的目的,使用某个函数,procedule等的时候也需要。

7. 起名要合理

程序,变量,procedure,structure等名一定要跟它的内容联系起来,变量不要起名如”x”,”y”,”z”,也许你会说笔者在上面举的例子里也用了x,y等,可笔者这样做的理由是他不牵涉到其他程序,实际开发软件的时候,笔者当然会使用有意义的变量名。请看下面例子:
void x(int a, int b){
  int z;
  z = a + b;
  printf("z is %d\n", z);
}

在这,我们可以知道x做什么,但不知道它意味着什么。a,b,z也一样。让我们稍微改过来再看吧:
void sum_of_ages(int jacks_age, int jills_age){
  int total_age;
  total_age = jacks_age + jills_age;
  print("total_age is %d\n", total_age);
}

虽然没有注释,但容易看出来要做什么。起名起得好,没有注释也可以。

8. 确认Buffer

要时时确认对设定的排列或变量的大小,以避免数据混乱或导致系统出问题。请看下面使用者输入数据的例子:
char city[10];
/* 为都市名称的排列 */
printf("Enter a city name: ");
scanf("%s", city);
printf("City is %s\n", city);

在这里,都市名称设定为10字英文字以内。如果使用者输入10字以上的都市名称会怎么样呢?可以说程序失败或覆盖Buffer里面的数据。不管怎样,不要做冒险的事,你要检查文字的长度:
char city[10];
/*为都市名称的排列*/
printf("Enter a city name: ");
fgets(city, sizeof(city), stdin);
printf("City is %s\n", city);

上面已修改的程序是:如果使用者输入10字以上的都市名,只接受Buffer能接受的长度为止,其外的会不接受。

9. 绝对不相信使用者

这是很重要的规律。千万不要相信将要使用你的软件的人,不要认为使用者会按你希望的方式来操作,反而把他们想成专门发现程序毛病的人。举例说,上面的程序就是为了那些输入都市名更长的的使用者而确认文字长度的。像C语言需要明确的数据形式的开发工具使用者,要记住确认输入数据形式和程序数据形式是否一致,不然会出现问题。

磨刀不误砍材工

以上的编程习惯是为你自己的方便。实际编程之前,要多做准备分析,设计。虽然稍麻烦,但为了编出易懂整齐的程序,不要舍不得花时间准备,不然以后会需要更多的时间来修改,扩展你的程序的。