1:$和括号的用途:
a为参数,b为命令,c为参数
$a,即引用a的值
$(b)和$B,输出b命令的结果
$(($a+1)),对a进行数值计算
$ac输出ac的值
${a}c输出a的值的参数和c组合一起符号
${a:c}这是模式匹配,中间符号可以由:、%、%%、#、##
2:c的主函数的参数传递
一般申明如int main(int argc, char* argv[])
argc是指参数的个数,argv是指参数的字符串数组,其中包括程序名自身比如函数名如hello,在shell下输入hello left right 'and me'
main()中带的参数就是
argc:4
argv:{'hello' 'left' 'right' 'and me'}
另外,linux的shell一般会设置argc和argv前对文件名参数进行通配符扩展
3:linux下的DBM数据库的编译
下载安装包之后,先./configure 再make 再make install 最后make install-compat!这样在
/usr/include/会有dbm.h、gdbm.h、ndbm.h的头文件,然后在程序里包涵ndbm.h的头文件,gcc -o dbm1 –I/usr/include/gdbm dbm1.c -lgdbm_compat –lgdbm
之后就能成功编译
4:C的指针传递sizeof
当把一个指向数组的指针传递给sizeof函数时,计算的不是数组的单个元素的大小,而是整个数组的大小
5:新进程的创立
创建新进程需要调用fork()函数,fork()函数复制当前进程(即父进程),创建一个新的进程(即子进程),子进程和父进程几乎一模一样,包括优先级、执行代码等,但是有自己的PID、数据空间、文件描述符和环境变量。因此一旦调用了fork()函数,父进程和子进程就会一起交叉运行(因为优先级、时间片都相同),所以只能通过fork()函数的返回值来判断,子进程的fork()函数返回0,父进程的fork()函数返回子进程的PID,如果失败,返回-1。另外fork()函数和exec()函数结合,就是可以创建新进程,并切换到新进程.
6:exec程序切换
在使用exec()进行函数切换的时候,只是单纯的重新运行要切换的程序,并不改变进程PID、父进程和文件流等资源,因此原先的打开的文件流等还是可以直接使用,另外,原来被切换的程序不可能再继续执行,只能重新开始了
7:Fedora安装GNOME/GTK+开发库
首先确认系统中的pkg-config版本,pkg-config --modversion pkg-config ,此辅助软件通过存放在标准目录下的文件来查找所需库文件目录。需要安装glib-devel、atk-devel、pango-devel和cairo-devel之后,再安装gtk2-devel
8:make depend的解释
C 源码的开头经常有一系列被包含的头文件,例如 stdio.h。有一些是系统级的头文件,有一些是你正在写的项目的头文件:
#include <stdio.h>
#include "foo.h"
int main(....
要确定在你的 foo.h 被改变之后,这个文件也会被重新编译,就要在你的 Makefile 这样写:
foo: foo.c foo.h
当你的项目变得越来越大,你自己的头文件越来越多的时候,要追踪所有这些头文件和所有依赖它的文件会是一件痛苦的事情。如果你改变了其中一个头文件,却忘了重新编译所有依赖它的源文件,结果会是很失望的。这就是depend 的作用,如果不使用这个功能, 则当你修改一个头文件时, 必须重新把所有的源文件都编译一次, 使用depend ,就可只编译包含此头文件的源文件,可大大节省时间。gcc 有一个选项可以分析你的源文件然后产生一个头文件的列表和它的依赖关系: -MM。
如果把下面的内容加到你的 Makefile 里面:
depend: gcc -E -MM *.c > .depend
然后运行 make depend,就会产生一个 .depend,里面包含了目标文件,C 文件和头文件的列表:
foo.o: foo.c foo.h
如果你改变了 foo.h,下一次运行 make 的时候,所有依赖 foo.h 的文件就会被重新编译。
每一次你增加一个头文件的时候,别忘了运行一次 make depend。
另外,现阶段,很多软件已经不需要或者不支持make depend了,比如编译linux 2.6的时候,根本不需要make depend,而早期的linux 2.4,则一般需要make depend这一步。
9:修改PATH变量的3个方法
方法1:
在用户主目录下有一个 .bashrc或者.profile 文件(有的系统是.bash_profile),可以在此两文件中加入 PATH 的设置如下:
export PATH="$PATH:/path1/:/path2/….."
注意:每一个 path 之间要用 “:“分隔,不要有不该有的空格,否者就无效,引号必须要有,否者bash就会出错。此方法之适合单个用户
方法2:
在 /etc/profile中增加。
PATH="$PATH:/path1/:/path2/….."
export PATH
或者添加方法1的添加行也可,并无实质区别
注意:每一个 path 之间要用 “:“分隔,不要有不该有的空格,否者就无效,引号必须要有,否者bash就会出错。此方法之适合整个系统,一般不建议直接改变系统文件!
方法3:
直接在终端下输入
export PATH=$PATH:/path1/:/path2/…..
注意:方法3不用引号,另外方法1和方法2都需注销,方法3不用,但是只是在单次运行中有效
10:linux的4种IO调度策略
策略1:anticipatory调度器使用“anticipatory”的算法:简单的说,有个 IO 发生的时候,如果又有进程请求IO 操作,则将产生一个默认的 6 毫秒猜测时间,猜测下一个 进程请求 IO 是要干什么的。这对于随即读取会造成比较大的延时。因此这算法是面向低速磁盘的,因为那个"猜测"实际上的目的是为了减少磁头移动时间。
策略2:Complete Fair Queuing(CFQ)调度器是Red Hat Enterprise Linux使用的标准算法。CFQ调度器对每个
进程维护一个 IO 队列,各个进程发来的 IO 请求会被 cfq 以轮循方式处理。也就是对每一个 IO 请求都是公平的。这使得 cfq 很适合离散读的应用,适用于有大量计算进程的多用户系统。它试图避免进程被饿死和实现了比较低的延迟。
策略3:deadline调度器是使用deadline 算法保证对于既定的 IO 请求以最小的延迟时间,提供对I/O子系统接近
实时的操作,deadline调度器提供了很小的延迟和维持一个很好的磁盘吞吐量。如果使用deadline算法需要确保进程资源分配不会出现问题,因为有可能会有IO请求一直得不到响应而被饿死。
策略4:NOOP调度器是一个简化的调度程序它只作最基本的合并与排序:对所有的 IO请求都用 FIFO 队列形式处理,
默认认为 IO 不会存在性能问题。这也使得 CPU 也不用那么操心,主要用在一些特殊的软件与硬件环境下,这些软件与硬件一般都拥有自己的调度机制对内核支持的要求很小,这很适合一些嵌入式系统环境。作为桌面用户我们一般不会选择它。
11:do while(0)用于宏定义
do while(0)主要用于宏定义,假设:
#define SALE(p) do{ free(p);p=NULL;} while(0)
……
if(p!=NULL)
SALE(P)
else
……
如果不用do while(0),展开后就会成为
……
if(p!=NULL)
free(p);p=NULL;
else
……
显然是不对的!如果用{},
#define SALE(p) { free(p);p=NULL;}
……
if(p!=NULL)
SALE(P)
else
……
这展开也没有问题;但是有的使用者如果在宏后面加";",那就是
……
if(p!=NULL)
{ free(p);p=NULL;};
else
……
这显然是又有问题了,但是do while(0)不会;展开后
……
if(p!=NULL)
do{ free(p);p=NULL;} while(0)";"(无论加不加都可以,不受限制)
else
……
12:linux文件权限的掩码
有两种数字表示方式:
方式1:用三位数字表示,读为4,写为2,可执行为1;每位数字为三者之和;第一位表示用户自身,第二位表示用户组,第三位表示其他;
方式2;在三位的基础上载前面加2位,第一位为0或1,表示是否设置用户ID,第二位表示是否设置用户组ID。
13:contain_of在linux kernel中的应用
container_of在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址;可以用来通过结构中的某个变量获取结构本身的指针。原型如下:
#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); /
(type *)( (char *)__mptr - offsetof(type,member) );})
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,
并初始化为ptr。typeof不是标准C,是在gcc中定义,可以获得一个变量的类型。
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是member的入口地址了,即通过结构体成员member获得了结构体的地址。
offsetof可以获得一个结构体成员的偏移值。具体可以看一个例子,如下:
container_of宏定义在[include/linux/kernel.h]中:
offsetof宏定义在[include/linux/stddef.h]中:
#include<stdio.h>
struct student{
char name[20];
char sex;
}stu={"zhangsan",'m'};
main()
{
struct student *stu_ptr; //存储container_of宏的返回值
int offset; //存储offsetof宏的返回值
//下面三行代码等同于 container_of(&stu.sex,struct student, sex )参数带入的情形
const typeof(((struct student*)0)->sex) *_mptr = &stu.sex;
//首先定义一个 _mptr指针, 类型为struct student结构体中sex成员的类型
//typeof 为获取(((struct student*)0)->sex)的类型,此处此类型为char
offset = (int)(&((struct student *)0)->sex);
/*((struct student*)0)为 把0地址强制转化为指向student结构体类型的指针,该指针从地址 0 开始的21个字节
用来存放name 与 sex(char name〔20〕与 char sex共21字节)
sex存放在第20个字节出(从0字节开始)
&((struct student *)0)->sex 取出sex地址(此处即为20) 并强制转化为整形所以offset为20,后面的printf结果将证明这一点*/
stu_ptr = (struct student *)((char*)_mptr - offset);
/*((char*)_mptr - offset)此处先把_mptr指针转化为字符形指针
(为什么这么做呢? 如果_mptr为整形指针 _mptr - offset 相当于减去 sizeof(int)*offset个字节)减去 offset值,相当于得到_mptr所在结构体的首地址(即stu的地址)然后我们把 该地址 强制转化为 struct student类型即可正常使用了*/
printf("offsetof stu.sex = %d/n",offset);
printf("stu_ptr->name:%s/tstu_ptr->sex:%c/n", stu_ptr->name, stu_ptr->sex);
return 0;
}
14:gcc的__attribute__编绎属性
GNU C的一大特色就是__attribute__机制。__attribute__,即特殊属性,允许申明函数、变量和类型的特殊属性,以便进行手工的代码优化和定制代码检查的方法;如果存在多个属性,则以逗号分割。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束:放于声明的尾部“;”之前。
附属属性有:
noreturn:作用于函数,表示该函数从不返回。
format:也作用于函数,可以让编译器根据格式检查参数类型。format格式:
format (archetype, string-index, first-to-check)
即告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规则对该函数的参数进行检查。“archetype”指定是哪种风格,例如printf之类;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数进行检查。注意:如果函数是成员函数,需要注意参数个数,this指针是隐形的第一个函数参数,也要算上。
unused属性作用于函数和变量,表明其可能不会被用到。
aligned:作用于变量、结构体或联合体,指定其的对齐方式,以字节为单位,如
struct example {
char a;
int b;
long c;}__attribute__ ((aligned(4)));
packed:用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结
构体或联合体时表示使用最小内存,即紧凑型分布,不使用对齐。
const:只能用于带有数值类型参数的函数上;当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外,其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。事实上,const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。并且,带有该属性的函数不能有任何副作用或者是静态的状态,所以,类似getchar()或time()的函数是不适合使用该属性的。其实并不建议使用这个参数,直接定义一个变量并赋值后使用就可以了。
另外__attribute__设计的非常巧妙,很容易作到和其它编译器保持兼容,也就是说,如果工作在其它的非GNU编译器上,可以很容易的忽略该属性。即使__attribute__使用了多个参数,也可以很容易的使用一对圆括弧进行处理,例如:
/* 如果使用的是非GNU C, 那么就忽略__attribute__ */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
更详细的解释可以参考:
http://blog.chinaunix.net/u1/33226/showart_491473.html
15:数据的对齐
体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。比如说读写时,大多数情况下需要读写w位数据,那么数据通道就会是w 位。如果所有的数据访问都以w位对齐,那么访问还可以进一步加快,因为需要传输的地址位减少,寻址可以加快。大多数体系结构都是按照字长来对齐访问数据的。很明显,不对齐的时候,执行效率肯定要低,不如对于32位机,读取一个32位对齐的数据只需读取一次,而不对齐的数据,就需要读取2次,甚至有的体系结构上会因为不支持而出错。
对齐问题主要有3点:变量对齐、结构对齐和数据对齐。前两点是编译器决定的变量映射和结构布局。最后一点与CPU的架(CISC/RISC)有关。2的整数倍的地址记作2n边界,将4的整数倍的地址记作4n边界,依此类推。显然每个地址都是1n边界,每个4n边界都是2n边界,每个8n边界都是4n边界。所谓“对齐”就是把变量放在什么样的地址边界上。
注:结构体中按该结构最大成员的大小来进行字节对齐,比如说最大的是double类型,那么结构按8字节对齐。
更详细的解释可以参考:
http://blog.sina.com.cn/s/blog_66f4c1bc0100ichy.html
16:IO端口、IO内存
CPU和外设交流的一个渠道,主要分为两种,一种是Port I/O,一种是MMIO(Memory mapping I/O)。前者就是我们常说的I/O端口,它实际上的应该被称为I/O地址空间。I/O地址空间和CPU的物理地址空间是两个不同的概念,例如I/O地址空间为64K,一个32bit的CPU物理地址空间是4G。MMIO占用CPU的物理地址空间,对它的访问可以使用CPU访问内存的指令进行。一个形象的比喻是把文件用mmap()后,可以像访问内存一样访问文件、同样,MMIO是用访问内存一样的方式访问I/O资源,如设备上的内存。MMIO不能被cache。
设备驱动程序要直接访问外设或其接口卡上的物理电路,这部分通常都是以寄存器的形式出现。外设寄存器也称为I/O端口,通常包括:控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式,可以把CPU分成两大类。一类CPU(如M68K,Power PC等)把这些寄存器看作内存的一部分,寄存器参与内存统一编址,访问寄存器就通过访问一般的内存指令进行,所以,这种CPU没有专门用于设备I/O的指令。这就是所谓的“I/O内存”方式。另一类CPU(典型地如X86)将外设的寄存器看成一个独立的地址空间,这也就是“IO空间”,所以访问内存的指令不能用来访问这些寄存器,而要为对外设寄存器的读/写设置专用指令,如IN和OUT指令。这就是所谓的” I/O端口”方式 。但是,用于I/O指令的“地址空间”相对来说是很小的,比如x86的I/O空间只有64KB(0-0xffff),连续两个8bit的端口可以组成一个16bit的端口,连续4个组成一个32bit的端口。Linux将基于I/O映射方式的或内存映射方式的I/O端口通称为“I/O区域”(I/O region)。
但是随着计算机技术的发展,单纯的I/O端口方式无法满足实际需要了,因为这种方式只能对外设中的几个寄存器进行操作。而实际上,需求在不断发生变化,例如,在PC上可以插上一块图形卡,有2MB的存储空间,甚至可能还带有ROM,其中装有可执行代码。自从PCI总线出现后,不管是CPU的设计采用I/O端口方式还是I/O内存方式,都必须将外设卡上的存储器映射到内存空间,实际上是采用了虚存空间的手段,这样的映射是通过ioremap()来建立的。
Port I/O和MMIO的主要区别在于
1)前者不占用CPU的物理地址空间,后者占有(这是对x86架构说的,一些架构,如IA64,port I/O占用物理地址空间)。
2)前者是顺序访问。也就是说在一条I/O指令完成前,下一条指令不会执行。例如通过Port I/O对设备发起了操作,造成了设备寄存器状态变化,这个变化在下一条指令执行前生效。uncache的MMIO通过uncahce memory的特性保证顺序性。
3)使用方式不同,由于port I/O有独立的64KI/O地址空间,但CPU的地址线只有一套,所以必须区分地址属于物理地址空间还是I/O地址空间。
17:字节序
字节序存放方式分类:
a) Little-Endian,即小端字节序就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,就是我们俗称的“高高低低”原则,好像也就只有Intel x86体系结构采用小端字节序;
b) Big-Endian,即大端字节序,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端,sun、moto等体系结构用的是bigendian。
字节序应用分类:
a) 主机序:不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序,因此按着不同的体系结构,采用不同的存放顺序原则
b) 网络字节序:为了便于通信,所以网络的存放字节序必须一致,TCP/IP各层协议将字节序定义为Big-Endian,故在电脑间通信时需要将主机序和网络序进行相互转化。