linux函数分析查询工具
1.优先推荐linux 中man命令
2.一个不错的中文Linux手册:http://cpp.ezbty.org/manpage
3.在线查英文Man手册:
http://www.kernel.org/doc/man-pages/
http://man7.org/linux/man-pages/dir_all_alphabetic.html
http://linux.about.com/od/commands/l/blcmdl.htm
http://linux.die.net/man/
http://www.linuxmanpages.com/
man 命令
部分 | 内容 |
---|---|
man1 | 一般命令。这个部分中的命令通常不需要超级用户(即管理员)特权。ls 、cat 和 passwd 放在这里,还有 shell。例如,请试试 man bash 。 |
man2 | 用来访问 UNIX 内核提供的服务的系统调用或函数。例如 fork 系统,它从一个现有的进程生成一个新进程。输入 man fork 显示它的手册页。使用系统软件的程序员常常参考这个部分。 |
man3 | C 库函数。许多软件包提供功能丰富的代码库,让开发人员可以创建新软件来补充现有的特性或开发全新的特性。每个库通常有一个手册页;一些库(比如系统的 libc)太大了,所以各个函数或一组相关函数有单独的文档。 |
man4 | 特殊文件,比如设备和驱动程序。 |
man5 | 文件格式。UNIX 几乎完全使用文本配置文件定制系统的操作。有大量配置文件,包括网络服务的列表 (/etc/services) 和可用的 shell 列表 (/etc/shells) 等等。 |
man6 | 游戏和屏幕保护程序。 |
man7 | 杂类文件。这是一个包罗万象的类别。在传统的系统上,可以了解 glob 操作符、正则表达式等方面的信息。 |
man8 | 系统管理命令,超级用户很可能要使用它们。 |
在某些情况下,不同部分中的组件可能名称相同。这种现象很常见,尤其是在一个软件包有多个部分的情况下。例如,第一部分中有 crontab
命令,它提交要调度的作业。同时,第五部分中有 crontab 文件格式,它描述要运行的作业。
为了区分不同部分中的同名组件,应该在第一个参数中提供部分号:
$ man 1 crontab $ man 5 crontab |
前一个命令显示 crontab 命令的手册页;后一个命令显示 crontab 文件格式。如果一个软件在多个部分中存在,而您没有指定部分号,man
就会显示在编号最低的部分中找到的匹配。
使用-k的man命令可根据关键字搜索
#man -k fork
看第一行就是我们需要的信息,加上小节号
#man 2 fork
从这里我们知道在unistd.h中有fork
该头文件在/usr/include中
#/usr/include
#vim unistd.h
库函数是高层的,完全运行在用户空间,为程序员提供调用真正的在幕后完成实际事务的系统调用的更方便的接口。系统调用在内核态运行并且由内核自己提供。标准C库函数printf()
可以被看做是一个通用的输出语句,但它实际做的是将数据转化为符合格式的字符串并且调用系统调用 write()
输出这些字符串。
是否想看一看printf()
究竟使用了哪些系统调用? 这很容易,编译下面的代码。
#include <stdio.h> int main(void) { printf("hello"); return 0; }
用命令
#strace ./hello
或者
#strace ./a.out
跟踪该可执行文件。
每一行都和一个系统调用相对应。 strace是一个非常有用的程序,它可以告诉你程序使用了哪些系统调用和这些系统调用的参数、返回值。 这是一个极有价值的查看程序在干什么的工具。在输出的末尾,你应该看到这样类似的一行 write(1, "hello", 5hello)
。这就是我们要找的。藏在面具printf()
的真实面目。既然绝大多数人使用库函数来对文件I/O进行操作(像 fopen, fputs, fclose)。 你可以查看man说明的第二部分使用命令man 2 write 。man说明的第二部分专门介绍系统调用(像kill()
和read()
)。 man说明的第三部分则专门介绍你可能更熟悉的库函数(像cosh()
和random()
)。
1.getenv
getenv, secure_getenv - get an environment variable
#include <stdlib.h> char *getenv(const char *name); char *secure_getenv(const char *name); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): secure_getenv(): _GNU_SOURCE
http://man7.org/linux/man-pages/man3/getenv.3.html
函数说明 getenv()用来取得参数name环境变量的内容。参数name为环境变量的名称,如果该变量存在则会返回指向该内容的指针。环境变量的格式为name=value。
返回值:执行成功则返回指向该内容的指针,找不到符合的环境变量名称则返回NULL。
2.putenv
putenv - change or add an environment variable
#include <stdlib.h> int putenv(char *string); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): putenv(): _SVID_SOURCE || _XOPEN_SOURCEhttp://man7.org/linux/man-pages/man3/putenv.3.html
函数说明:putenv()用来改变或增加环境变量的内容。参数string的格式为name=value,如果该环境变量原先存在,则变量内容会依参数string改变,否则此参数内容会成为新的环境变量。
返回值:执行成功则返回0,有错误发生则返回-1。
错误代码:ENOMEM 内存不足,无法配置新的环境变量空间。
3.setenv
setenv - change or add an environment variable
#include <stdlib.h> int setenv(const char *name, const char *value, int overwrite); int unsetenv(const char *name); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): setenv(), unsetenv(): _BSD_SOURCE || _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600http://man7.org/linux/man-pages/man3/setenv.3.html 函数说明: setenv()用来改变或增加环境变量的内容。
参数 name为环境变量名称字符串。
参数 value则为变量内容。
参数 overwrite用来决定是否要改变已存在的环境变量。如果overwrite不为0,则改变环境变量原有内容,原有内容会被改为参数value所指的变量内容。如果overwrite为0,且该环境变量已有内容,则参数value会被忽略。
返回值: 执行成功则返回0,有错误发生时返回-1。
错误代码: ENOMEM 内存不足,无法配置新的环境变量空间
#include <stdio.h> #include <stdlib.h> main() { char *p; if((p = getenv("USER"))) printf("USER=%s\n",p); putenv("USER=root"); printf("USER=%s\n",getenv("USER")); setenv("USER","test",1); printf("USER=%s\n",getenv("USER")); unsetenv("USER"); printf("USER=%s\n",getenv("USER")); }
USER=root
USER=test
USER=test
USER=(null)
4.printf的buffer缓冲
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("-"); } return 0; }
1.fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。
2.还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。
因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出。在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。
Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。
对于上面的问题,我们如果修改一下上面的printf的那条语句为:printf
(
"-\n"
);
或是
printf
(
"-"
);
fflush
(stdout);
就没有问题了(就是6个“-”了),因为程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。
参考:http://coolshell.cn/articles/7965.html
fflush用于清空缓冲流,虽然一般感觉不到,但是默认printf是缓冲输出的。 fflush(stdout),使stdout清空,就会立刻输出所有在缓冲区的内容。 fflush(stdout)这个例子可能不太明显,但对stdin很明显。 如下语句: int a,c; scanf("%d",&a); getchar(); 输入: 12(回车) 那么 a=12 ,c= '\n' 而: int a,c; scanf("%d",&a); fflush(stdin); getchar(); 输入: 12(回车) 那么a=12, c暂时未得到输入值,还需要再输入c,因为getchar也是缓冲输入,'\n'本还在缓冲区,但是被清空了。 另外fflush不能作用于重定向输入流。
1.字符设备只能以字节为最小单位访问,而块设备以块为单位访问,例如512字节,1024字节等
2.块设备可以随机访问,但是字符设备不可以
3.字符和块没有访问量大小的限制,块也可以以字节为单位来访问
参考:http://www.cnblogs.com/qlee/archive/2011/07/27/2118406.html