0%

库函数和系统调用的区别

库函数是更高级别的,完全在用户空间里运行,并为程序员提供了更方便的做实际工作的函数接口。Higher level,run in user space,more convenient。

系统调用代表用户以内核模式工作,由操作系统本身的内核提供。In kernel mode。

库函数printf看上去类似于一般输出函数,但是它实际上只是格式化你提供给字符串的数据,并用低级系统调用write编写字符串数据,然后将数据发送到一个与终端的标准输出关联的文件中。

DiFX问题集锦

DiFX 使用中找不到fl

DiFX使用中遇到/usr/bin/ld can not find –fl

这个主要是因为安装DiFX时没有把安装包装全。

可以参考CentOS 安装DiFX

解决方法为安装flex*,全包(包括开发库)而不只是flex。

DiFX-HOPS出现的lpng12问题

在使用difx的install附加安装HOPS的时候,报错说是-lpng12没有找到,自己看了一下,丫的libpng我装了呀,使用yum search了一下,发现libpng还有libpng10、libpng12,囧。

解决方法很简单:

1
yum install libpng12*

当然,对于centos而言,如果找不到这个package,可以去http://fr2.rpmfind.net/linux/rpm2html/search.php?query=libpng12.so.0&submit=Search+…&system=centos&arch=进行下载,直接安装rpm包也是ok的。

unable to find rpm tool

在安装DiFX的时候出现这个问题。

运行install.sh出现:

ERROR: Unable to find rpm tool, please add its location to your PATH and restart installation
Installation failed.
Please see /var/log/ipp_em64t_install.log for details.

Installation is complete:
Thank you for using Intel Software Development Products, tools for improving application performance.

解决方法为: /install/install –nonrpm

这个bug在ipp5.3和ipp6.0都是存在的,从ipp6.1开始修复,对于全新的IPP,多个版本可以共存安装。

ImportError:No module named mx.dateTime

DiFX中getEOP.py问题

使用getEOP.py的时候出现

1
ImportError:No module named mx.dateTime

缺少mx包,解决方法使用yum install python-egenix-mx-base即可。

DiFX 使用中RPC无法使用的问题及解决方法

最新更新,直接执行startCalcServer即可解决这个问题了。

我记得去年貌似就碰到这个问题,不过只是不同的发行版而已,今天是Fedora 17,Pro.Z的机器。

问题就是difx的calcserver程序是需要调用rpc的,而大部分情况下,出错的原因是因为rpc支撑环境没有安装,其实如果使用了NFS,这个问题一般是没有问题的。

解决方法少许有些不同,不过还是可以只使用的:即安装portmap程序包,然后尝试service rpcbind start

1
2
$ apt-get install portmap
$ service rpcbind start

这里很高兴还记得ALT+F2,然后r重启X11 shell的技巧。

关于difx的troubleshooting也有提及RPC service unavailable

If an RPC error is encountered when starting the calcserver program, it is possible that the portmap service is either not installed or not running. Please consult your OS distribution documentation for rectifying this. In the case of Debian or Ubuntu Linux, the solution may be as simple as:

关于portmap端口映射

端口映射是一个服务器,将RPC程序号转换为DARPA的协议端口号。在使用RPC调用时它必须运行。

portmap进程的主要功能是把RPC程序号转化为Internet的端口号。

当一个RPC服务器启动时,会选择一个空闲的端口号并在上面监听(每次启动后的端口号各不相同),同时它作为一个可用的服务会在portmap进程注册。一个RPC服务器对应惟一一个RPC程序号,RPC服务器告诉portmap进程它在哪个端口号上监听连接请求和为哪个RPC程序号提供服务。经过这个过程,portmap进程就知道了每一个已注册的RPC服务器所用的Internet端口号,而且还知道哪个程序号在这个端口上是可用的。portmap进程维护着一张RPC程序号到Internet端口号之间的映射表,它的字段包括程序号、版本号、所用协议、端口号和服务名,portmap进程通过这张映射表来提供程序号-端口号之间的转化功能

如果portmap进程停止了运行或异常终止,那么该系统上的所有RPC服务器必须重新启动。首先停止NFS服务器上的所有NFS服务进程,然后启动portmap进程,再启动服务器上的NFS进程。

RPC

远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。

Linux RPM软件包

软件包比较通用的有两种,如下:

  • Redhat系列的rpm包
  • Debian系列的软件包格式dpkg,即deb包

可以使用工具alien(不是异形,^_^)来转换rpm包和deb包

RPM软件包的优点

  • 使用广泛
  • 允许你只用一条命令来安装软件
  • 只需要处理一个文件
  • RPM自动处理软件包之间的依赖性检查
  • RPM软件包被设计为由“最干净”的源代码而来从而允许你对它重新进行编译

创建RPM包

  • 收集需要打包的软件
  • 创建SPEC文件,该文件描述了如何建立软件包
  • 用rpmbuild命令创建软件包

  • indent : changes the appearance of a C program by inserting or deleting whitespace.能够按照预先定义好的或者自定义的标准调整源代码以及代码缩进的格式以达到所需要的风格;
  • etags, ctags – generate tag file for Emacs, vi。生成的文件能够增强编辑器浏览和分析源代码的风格;

语句

C语言中有很多的语句:空语句、表达式语句、代码块、if语句、while语句、break和continue语句、for语句、do语句、switch语句、switch中的break语句、default子句、goto语句。

基本上,C语言实现了其他现代高级语言所具有的所有语句。

if语句

在C的if语句和其他语言的if语句中,只存在一个差别:就是C不具备布尔类型,而是用整型来代替。

对于if-else配对的问题,else字句从属于最靠近它的不完整的if语句。

break和continue语句

在while语句中,使用break语句可以永久终止循环,而使用continue语句,用于永久终止当前的那次循环。这两条语句的任何一条如果出现于嵌套的循环内部,它只对最内层的循环起作用,你无法使用break或者continue语句影响外层循环的执行。

while语句的执行过程

有时while语句在表达式中就可以完成整个语句的任务,于是循环体就无事可做,此时单独用一行表示一条空语句是比较好的做法,比如:

1
2
while((ch = getchar() ) != EOF && ch != ‘\n’)
;

for语句

for语句的形式为:

1
2
for (expression1; expression2; expression3)
statement;

其中expression1称为初始化部分,expression2称为条件部分,expression3称为调整部分。
与上述for语句相同意义的while语句:

1
2
3
4
5
6
expression1;
while(expression2)
{
statement;
expression3;
}

从上面两个语句的对比,我们可以看出,for循环有一个风格上的优势,就是把所有用于操纵循环的表达式收集在一起,放在同一个地点,便于寻找。
do语句

   当你需要循环体至少执行一次时,选择do语句。

switch中的break语句

switch语句中的执行中遇到了break语句,执行流就会立即跳到语句列表的末尾。而break语句的实际效果是把语句列表划分为不同的不同,这样,switch语句就能够按照更为传统的方式工作。其实如果这样的话,那么最后一个语句就不用添加break了,但是还是加上了break,主要是在以后的维护中,如果维护人员添加代码而忘记了break就有可能影响程序的效果。
default子句

对于switch中的default语句,我一直认为要放在最后,但是测试了一下,发现default可以出现在任何一个case标签出现的地方而不仅仅是最后。

switch语句的执行过程

对于符合多个case的情况,最好写上注释,防止在检查的时候额外加上了break而导致出现bug。

goto语句

goto语句可以出现在函数中的任何一个位置,这种无序的跳转是很危险的,所以,如果有其他方法搞定,最好少用goto语句。

当然,针对跳转出多层嵌套,goto确实是一种比较方便的方法。当然可以使用一个标志值,来跳转出所有的循环,或者将所有的循环放在一个函数中,直接使用return来跳出。对于设置标志值,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum {EXIT,OK} status;

status = OK;
while(status == OK && condition1)
{
while(status == OK && condition2)
{
while(status == OK && condition3)
{
if(something wrong)
status = EXIT;
break;
}
}
}

总结

另外还需要注意:C并不具备任何的输入输出语句,IO是通过调用库函数实现的,C也不具备任何异常处理语句,它们也是通过调用库函数来完成的。

结构体无法比较

由于结构体的成员不一定是连续地存储在内存中,所以不能用运算符==或!=来对结构体进行比较,事实上,在一个结构体的存储区域内可能会出现一些“空洞”,这是由于计算机是按照一定的边界,例如半字、字、双字边界来存储不同数据类型的变量。

对结构体的访问

  • 结构体成员运算符structure member operator(.),也被称为圆点运算符
  • 结构体指针运算符structure pointer operator(->),也被称为箭头运算符

指针方式的结构体

在传递一个结构体时,用传地址的方式比采用传值的方式效率高,因为传值会要求复制整个结构体。

多重结构体变量访问

这里由引出来另一个比较有意思的问题,既然结构体的变量可以不同类型的,当然结构体也可以作为内部变量,如下所示,shao_struct_v2.c

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
#include <stdio.h>
#include <string.h>

struct Date{
int year;
int month;
int day;
};

struct Student{
char id[5];
char name[10];
int age;
char sex[10];
struct Date date;
};

int main(int argc, char const *argv[])
{
struct Student s1 = {"0001", "Little Bob", 20, "male"};
strcpy(s1.name, "Big Bob");
s1.date.year = 1990;
s1.date.month = 1;
s1.date.day = 12;

printf("ID \t Name \t AGE \t\t SEX \t\n");
printf("%s\t%s\t%d(%d-%d-%d)\t%s\n",s1.id, s1.name, s1.age, s1.date.year, s1.date.month, s1.date.day, s1.sex);

return 0;
}

输出为

1
2
3
4
5
6
➜  struct git:(master) ✗ gcc shao_struct_in_struct.c
➜ struct git:(master) ✗ ls
a.out shao_struct_in_struct.c shao_struct_simple.c
➜ struct git:(master) ✗ ./a.out
ID Name AGE SEX
0001 Big Bob 20(1990-1-12) male

根据“用户输入”初始化结构体

用户输入信息需要访问到结构体变量所在的内存空间地址

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
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Student
{
char id[5];
char name[10];
int age;
char sex[3];
};

int main()
{
struct Student s1;
printf("input id:>");
scanf("%s",s1.id);
printf("input age:>");
scanf("%d",&s1.age);
}
​```

## 定义结构体类型数组,通过数组同时初始化多个结构体

​```c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct Student
{
char id[5];
char name[10];
int age;
char sex[3];
};
void main()
{
struct Student S[2]={{"0001","Newton",35,"男"},
​ {"0002","Lagrange",30,"男"}}; //通过数组同时初始化多个结构体
for(int i=0;i<2;++i)
​ {
printf("id=%s,name=%s,age=%d,sex=%s\n",
​ S[i].id,S[i].name,S[i].age,S[i].sex);
​ }
}

定义结构体指针,通过指针指向符->访问结构体成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct Student
{
char id[5];
char name[10];
int age;
char sex[3];
};

void main()
{
int a =10;
int *pa=&a;
​ Student s1={"0001","Euler",32,"男"};
printf("id=%s,name=%s,age=%d,sex=%s\n",s1.id,s1.name,s1.age,s1.sex);

struct Student *ps=&s1; //指向结构体的“结构体类型指针”
printf("id=%s,name=%s,age=%d,sex=%s\n",ps->id,ps->name,ps->age,ps->sex);
//通过指针指向符->访问结构体成员变量
}

线性链表

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
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h>
#define ElemType int
struct Node
{
​ ElemType data;
struct Node *next;
};

typedef Node* List;

void InitList(List *head)
{
​ *head=NULL;
}

void CreateList(List *head)
{
​ *head=(Node *)malloc(sizeof(Node));
​ (*head)->data=1;
​ (*head)->next=NULL;

Node *p=*head;
for(int i=2;i<=10;++i)
{
Node *s=(Node *)malloc(sizeof(Node));
s->data=i;
s->next=NULL;

p->next=s;
p=s;
}
}

void ShowList(List head)
{
​ Node *p=head;
while(p!=NULL)
​ {
printf("%d-->",p->data);
​ p=p->next;
​ }
printf("Over!\n");
}

void main()
{
​ List mylist;
​ InitList(&mylist);
​ CreateList(&mylist);
​ ShowList(mylist);
}

关键词typedef

  • 关键字typedef提供了一种为已定义好的数据类型创建同义词的机制,为了创建更简短的类型名,通常使用typedef来为结构体类型起名字;
  • 关键字typedef还长用来为基本数据类型创建一个别名,用于提高程序的可移植性;

释放内存

Linux采取的是最快速操作,就算程序关闭掉,为了后面的可能会用的概率而保证这些数据可能还位于内存中,所以导致4G的内存,随便跑跑程序就到了3.6G了,只剩下几百兆,就算是很大的内存也是如此,我一个同事用的是24G的内存,跑网络程序,随便一跑就到了20多G,这里有个比较简单的释放内存的方法:

1
2
sync
echo 3 > /proc/sys/vm/drop-cache

这样就会释放出很大一部分的内存,但是可能对于网络数据的接收发送有影响,对于实时收发的程序,可以在程序中采取一定的策略。

怎样从函数返回多个值

有下面几种方法用于返回从函数返回多个值

  1. 传入多个指针指向不同的地址,让函数填入需要返回的值
  2. 让函数返回包含需要值的结构
  3. 结合指针和结构,让函数接收结构指针,然后再填入需要的数据
  4. 不得已的时候,可以使用全局变量,但是这个方法不推荐,GOD晓得有没有可能在多线程的其他程序中会修改全局变量,所以这是迫不得已时采用的方法。

启动命令如何不输入./

执行当前文件夹下的可执行程序,不加”./”的方法:

直接执行当前目录下的程序可以使用一下方法:

  1. 将export PATH=$PATH:. 语句(冒号后加一个点)写到用户主目录的.bashrc中;
  2. 将可执行程序拷贝至/usr/bin或者/usr/local/bin (如果程序需要经常修改,还是采用第一种方法较好)

执行当前可执行程序加”./”的原因:

主要是安全原因,因为在linux中执行程序时,会先搜索当前目录然后是系统目录,所以如果当前目录中有与系统可执行程序重名的程序,比如cp,她就会优先执行当前目录中的cp,但是如果当前目录的cp是木马,就会威胁到系统安全,所以这是Linux的一种安全策略,所以默认并没有把当前目录加到环境变量PATH中去。