《linux操作系统-第6讲c语言编程剖析》由会员分享,可在线阅读,更多相关《linux操作系统-第6讲c语言编程剖析(42页珍藏版)》请在金锄头文库上搜索。
1、第6讲 C语言编程 gcc、gdb的使用 与Linux进程创建,Linux的程序文件名后缀,GCC编译器,GCC是GNU C Comiler的缩写,是GNU推出的 完全免费、功能强大,支持多种语言(C,C+,FORTRAN,PASCAL,ADA)的编译器。 支持多种硬件平台,执行效率快,比一般的编译器的执行效率高20%30% 。 查看gcc版本:gcc -v,GCC编译过程,预处理:调用linux的CPP预编译程序,生成.i的临时文件。 将#include 文件拷贝到要编译的源文件中。 用实际值替代#define文本。 在调用宏的地方进行宏替换。 命令#gcc E o test.i test.
2、c或#cpp test.ctest.i 汇编:调用linux的as汇编编译程序,生成.s的汇编文件。 命令#gcc S test.c 编译:调用linux的cc1编译程序,生成.o的目标文件。 命令#gcc c test.c 链接:调用linux的ld链接程序,生成最后的可执行文件。 命令#gcc o test test.c 执行 #./test,Gcc的基本选项,Gcc options filenames Options有如下常用选项: -S:对源程序进行预处理和编译,不进行汇编和链接,自动生成汇编文件。 -c:执行预处理、编译、汇编而不链接,生成.o为后缀的目标文件。通常用于编译没有主程序
3、的子程序文件。 -o output_filename:指定输出文件名为output_filename,不指定时缺省文件名为a.out -g:产生调试信息。 -x:指定待编译程序的语言。如gcc xc+ test.cpp -O0:不优化。 -O1:第一级优化。允许与-g共用,编译链接过程稍慢。 -O2:第二级优化。特别对于大型程序,可以大幅度提高运行速度。 -O3:第三级优化。与O2的区别在于它允许把函数直接插入到调用的代码处 。,Gcc函数库,Gcc options 选项: -lm 指定函数库 -L 指定函数库的搜寻目录 -I指定include 所在目录。,Gcc函数库,共享函数库的格式fun
4、.so.x.x.x,其中x.x.x指版本号,共享函数库通常会有符号链接指向静态函数库或相关so的文件。 命令ldd用于查出程序所用的共享函数库。 例1: ldd test 例2: ldd sin,分别编译main()和其他函数,程序维护工具make,Linux 系统的许多软件都是使用make程序和 makefile来维护而实现自动编译的, make程序自动确定需要重新编译的文件,只对他们进行重新编译,然后链接生成可执行文件。 make程序是在程序调试、改进过程中必不可少的工具。 make程序可以自动对已修改的源程序进行编译,而对未改变的部分则跳过编译步骤,从而大大提高效率,是源程序的编译、链接
5、、管理更加规范和有条理。 make程序需要两方面的信息:一是可执行文件和各个程序模块之间的关系,二是文件的修改日期。 可执行文件和各个程序模块之间的关系通常记录在makefile或Makefile中。,makefile语法,target(目标名):dependency(依赖模块) command(命令行) 依赖关系一行放不下时,续行用“”标记。 命令行command之前不可有空格,只是加制表符tab 命令行command行中的命令若不想显示在屏幕上则在其前加 例如makefile文件为 test : testmain.o testfun.o gcc -o test testmain.o tes
6、tfun.o testmain.o : testmain.c gcc -c testmain.c testfun.o: testfun.c gcc -c testfun.c clean: rm f test*.o,写makefile.,注意:命令前用键,GDB调试器,GDB是GNU自带的调试工具,GDB支持C,C+,Modula-2等。 编译程序时加调试开关 #gcc g -o test test.c 进入调试状态# gdb test,GDB常用命令,GDB其他使用技巧,在GDB下执行shell命令 ( gdb)shell commandline 重复上次命令的执行直接回车 指令级的调试 st
7、epi/nexti (指令级单步执行) disp/x $寄存器 (显示寄存器的值) 出错后的调试。程序发生错误时,会在当前目录下产生一个名为core的内存映像文件。 #gdb a.out core 获得帮助 Shell状态下,运行info gdb 或 man gdb GDB状态下,运行(gdb)help或(gdb)help GDB命令名,Linux中的进程,进程这个概念是伴随着Unix的产生而出现的,Unix之父Dennis Ritchie当初用来发表Unix的论文的时候就提出了用进程的观点来看待整个操作系统,随着操作系统理论的发展,进程作为程序执行的实体和资源分配的单位的观念也在变化。线程的
8、出现,改变了进程的传统概念。但是在linux中,进程仍然保留着传统的意义,它包括四个要素: 程序段。 系统堆栈空间。 task_struct结构(PCB)。 专有内存空间。,Linux中的线程,linux的线程模型是一种一对一模型,也就是每个线程实际上在核心是一个单独的进程,核心的调度程序负责线程的调度。,进程控制块和堆栈,作为描述进程信息,操作系统感知进程存在的进程控制块(PCB),在linux中是由结构task_struct来实现的。task_struct的定义在include/linux/sched.h中给出。 当产生一个的进程时,在内核空间中会分配一个8K的空间来记录新进程的信息,其中
9、包括task_struct结构和为进程分配的内核堆栈,当进程由于系统调用转到内核时,就要切换到该进程的内核堆栈了。 task_struct结构可以被系统中的许多模块访问,如调度程序、资源分配程序、中断处理程序等。由于task_struct结构经常被访问,它常驻内存。,linux进程的创建与执行,linux进程创建用以下系统调用完成: 系统调用fork():父进程的所有资源通过数据结构复制全部给子进程。 fork()用于创建进程。 后增设的系统调用vfork(): 除了task_struct和系统空间堆栈以外的资源全部都通过数据结构指针的复制“遗传”。所以vfork创建的是线程,而不是进程。 系
10、统调用clone():进程的资源有选择地复制给子进程,而没有复制的数据结构(资源如内存空间)通过指针的复制让子进程共享。 用clone既可以创建一个进程,也可以创建一个线程。,创建子进程后父进程的选择,创建子进程后,父进程可以有以下几种选择: 父进程不受影响,继续执行,也称“异步”方式。 父进程停下来,等待子进程完成后,再继续执行,也称“同步”方式。用系统调用wait()和 waitpid()实现。 wait()是等待所有子进程完成; waitpid()等待特定子进程完成。 父进程创建了子进程后,马上结束自己。用系统调用exit()实现。,进程的撤消,用户程序中好像没有调用exit()就结束了
11、,实际上当用户程序编译时,编译器在程序的结尾处加上一个exit()系统调用的,所以当进程完成任务时,就会调用exit()来结束自己。用户程序也可以主动地调用exit()来显式的结束。,fork(),#include #include pid_t fork(void); fork()函数是一个单调用双返回函数。调用后,子进程是父进程的一个复制,父子进程都从fork()语句的一条语句开始执行。 调用正确完成时, 给父进程返回的是被创建子进程的进程标识符(0)。 给子进程返回的是0。 调用失败时给父进程返回-1。,fork()使用举例,/fork . #include #include #inclu
12、de int glob = 3; int main(void) pid_t pid; int loc = 3; printf(“Before fork :glob=%d,loc=%dn“,glob,loc); pid = fork(); if (pid 0) printf(“fork errorn“); exit(0); else if (pid = 0) glob = glob + 1; loc= loc - 1; / sleep(1); printf(“Now child process changes glob=%d and loc=%dn“,glob,loc); else printf
13、(“parent process doesnt changes the glob=%d and loc=%d n“,glob,loc); printf(“End pid=%d,:glob=%d,loc=%dn“,pid,glob,loc); exit(0); ,fork()运行结果分析,fork()可能产生两种运行结果: 一种为:子进程先运行,父进程后运行 另一种为:父进程先运行,子进程后运行,vfork()使用举例,/vfork . #include #include #include int glob = 3; int main(void) pid_t pid; int loc = 3;
14、printf(“Before vfork :glob=%d,loc=%dn“,glob,loc); pid = vfork(); if (pid 0) printf(“vfork errorn“); exit(0); else if (pid = 0) glob = glob + 1; loc= loc - 1; sleep(1); printf(“child process changes glob=%d and loc=%dn“,glob,loc); exit(0); else printf(“parent process doesnt changes the glob=%d and lo
15、c=%d n“,glob,loc); printf(“End:glob=%d,loc=%dn“,glob,loc); exit(0);,vfork()运行结果分析,vfork()可能产生两种运行方式,但变量最后结果是一样的(因为是指针复制):,clone(),#include #include int clone(int(*fn)(void*),void *child_stack, int flags,void *arg); 与fork()不同的是,clone()函数创建的线程或进程,另立门户。 当线程被创建后,执行fn(arg) 指定的函数。 fn是指向执行函数的指针,arg是传递给含数的参
16、数。当 fn(arg) 执行终止时,线程撤消。 fn返回的是整型值,即线程的退出码。线程也可以调用exit()终止自己。 child_stack说明线程使用的堆栈,它是指向堆栈的指针。 当线程终止时,用flags的低字节给父进程返回信号, 调用正确完成时,与fork()一样,给父进程返回的是被创建线程的标识符。 调用失败时给父进程返回-1。 clone()函数是Linux专用的。,clone()使用举例,#include #include int myfun() printf(“Now in thread!n“); exit(0); int main() int fd; void *child_stack; int flag; typedef int (*fn)(void *); void *arg; int p20; child_stack = p; fd = clone(fn)myfun,c