南京天石软件技术有限公司 陈锺 (QQ:31423047 MSN:cz_888@hotmail.com) Solaris 10 C编程 南京天石软件技术有限公司 陈锺 (QQ:31423047 MSN:cz_888@hotmail.com)
Solaris—软件开发环境的搭建 添加必须的路径信息到环境变量 添加如下信息到/.profile PATH=$PATH:/usr/sfw/bin:/usr/local/bin:/usr/local/lib:/usr/ccs/bin;export PATH 复制必须的lib以使gcc工作正常 执行如下命令:cp /usr/sfw/lib/libstdc++.so.6 /lib/和cp /usr/sfw/lib/libgcc_s.so.1 /lib/ 为了编译gtk软件,例如制图包和一次图: 将/usr/lib/pkgconfig/gthread-2.0.pc和/usr/lib/pkgconfig/ORBit-2.0.pc 中的-mt替换成-D_REENTRANT 重新启动
Solaris—Sun Studio安装 安装Sun Studio11 安装包为studio11-sol-x86.tar.bz2 bzip2 -d studio11-sol-x86.tar.bz2 解压 tar xf studio11-sol-x86.tar 解包 ./installer 进行安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装
Solaris—Sun Studio安装 为Sun Studio配置环境 在/.profile文件的PATH变量赋值中加入/opt/SUNWspro/bin
Solaris—gcc 在为Unix开发应用程序时,绝大多数情况下使用的都是C语言,因此几乎每一位Linux程序员面临的首要问题都是如何 目前Linux下最常用的C语言编译器是GCC(GNU Compiler Collection),它是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序. GCC不仅功能非常强大,结构也异常灵活.最值得称道的一点就是它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等. 开放、自由和灵活是gcc的魅力所在,程序员通过它能够更好地控制整个编译过程. 各操作系统的gcc略有不同。
Solaris—gcc 使用GCC编译程序时,编译过程可以被细分为四个阶段: ◆ 预处理(Pre-Processing) ◆ 编译(Compiling) ◆ 汇编(Assembling) ◆ 链接(Linking)
Solaris—软件开发模式 模式一 每个程序员机器上安装Solaris,使用gedit编写程序或者Sun studio集成开发。 优点:直接编写、编译、调试。 缺点:Solaris下其他辅助工具少,Solaris桌面提供的软件相对匮乏,word文档等在windows与Solaris间无法完全兼容。
Solaris—软件开发模式 模式二 每个程序员机器上使用windows,在一台编程调试服务器上安装Solaris。程序员使用Xmanager登陆到服务器的Solaris进行程序编写、编译。 优点:双操作系统并存,windows资源丰富,在Solaris下直接编写、编译、调试。 缺点:对调试服务器性能要求较高,否则交互速度太慢。
Solaris—软件开发模式 模式三 每个程序员机器上使用windows,在一台编程调试服务器上安装Solaris。使用windows环境编写程序,通过ftp工具将代码传输到服务器的Solaris,通过telnet远程登陆编译。 优点:程序编写工具丰富。 缺点:必须经过代码传输过程,远程字符模式的编译、修改、调试。
Solaris—helloworld.c gcc helloworld.c -o helloworld.o ./helloworld.o #include <stdio.h> int main(void) { printf ("Hello world, Solaris programming!\n"); return 0; } gcc helloworld.c -o helloworld.o ./helloworld.o Hello world, Solaris programming!
Solaris—dbx dbx程序调试工具—程序崩溃 例子:crash.c 1:#include <stdio.h> 2:int main(void) 3:{ 4: char * ss=NULL; 5: sprintf(ss,"helloworld!\n"); 6: printf(ss); 7: return 0; 8:}
Solaris—dbx 使用-g编译程序,以使编译出来的程序带调试环境 gcc -g crash.c –o crash.o bash-3.00# ./crash.o 段错误 (core dumped) 怎么样快速找到问题?
Solaris—dbx bash-3.00# dbx crash.o For information about new features see `help changes' To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc Reading crash.o Reading ld.so.1 Reading libc.so.1 (dbx) run Running: crash.o (process id 1230) signal SEGV (no mapping at the fault address) in memchr at 0xd27044d5 0xd27044d5: memchr+0x0055: cmpb (%eax),%cl Current function is main 5 sprintf(ss,"helloworld!\n"); (dbx) quit
Solaris—dbx dbx程序调试工具—内存泄露 例子:leak.c 1:#include <stdio.h> 2:int main(void) 3:{ 4: int i=0; 5: printf(“pid=%d\n”,getpid()); 6: while(i<100) 7: { 8: char *ss=malloc(sizeof(char)*64); 9: sprintf(ss,"helloworld!\n"); 10: usleep(100000); 11: i++; 12: } 13: return 0; 14:}
Solaris—dbx 使用-g编译程序,以使编译出来的程序带调试环境 gcc -g leak.c -o leak.o 运行leak.o bash-3.00# ./ leak.o pid=1325 使用prstat -p 1325 1命令查看leak.o进程状态,怎么SIZE(占用的虚拟内存)和RSS(内存)不断增长? 怎么样快速找到内存泄露问题?
Solaris—dbx bash-3.00# dbx leak.o For information about new features see `help changes' To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc Reading leak.o Reading ld.so.1 Reading libc.so.1 (dbx) dbxenv rtc_mel_at_exit verbose 开启结束后显示详细信息 (dbx) check -leaks 开启内存泄露选项 leaks checking - ON (dbx) run
Solaris—dbx Running: leak.o (process id 1329) Reading rtcapihook.so Reading libdl.so.1 Reading rtcaudit.so Reading libmapmalloc.so.1 Reading libgen.so.1 Reading libm.so.2 Reading rtcboot.so Reading librtc.so RTC: Enabling Error Checking... RTC: Running program... pid=1329
Solaris—dbx Checking for memory leaks... Actual leaks report (actual leaks: 100 total size: 6400000 bytes) Memory Leak (mel): Found 100 leaked blocks with total size 6400000 bytes At time of each allocation, the call stack was: [1] main() at line 8 in "leak.c" Possible leaks report (possible leaks: 0 total size: 0 bytes) execution completed, exit code is 0 (dbx) quit
Solaris—dbx 小结 dbx包含在Sun Studio中。 编译时必须使用-g才能在dbx显示出与源代码相关的调试结果信息。 守护进程在调试前,请先使其成为普通进程。 调试网络程序时,在dbx调试时使用ignore SIGPIPE来避免网络信号导致调试中断。
Solaris—常用调试手段 直接在终端中输出信息 如果是守护进程写调试信息文件 使用dbx 使用sunix系统的web调试平台
Solaris—时间函数 char *ctime(const time_t *clock); struct tm *localtime(const time_t *clock); struct tm *gmtime(const time_t *clock); char *asctime(const struct tm *tm);-----------以上非MT-Safe char *ctime_r(const time_t *clock, char *buf, int buflen); struct tm *localtime_r(const time_t *restrict clock, struct tm *restrict res); struct tm *gmtime_r(const time_t *restrict clock, struct tm *restrict res); char *asctime_r(const struct tm *restrict tm, char *restrict buf, int buflen);-------------------------以上为MT-Safe 推荐
Solaris—解析传递给程序的参数 如何在程序中使用执行时传递过来的参数? 例如:./arguments.o debug hello=1。 带有参数的main()函数头格式如下: int main(int argc, char * argv[]) 其中,第一个参数argc是int型的,它用来存放命令行参数的个数,实际上argc所存放的数值比命令行参数的个数多1,即将命令字(可执行文件名)也计算在内。第二个参数argv是一个一维的一级指针数组,它是用来存放命令行中各个参数和命令字的字符串的,并且规定: argv[0]存放命令字 argv[1]存放命令行中第一个参数 argv[2]存放命令行中第二个参数 … 这里,argc的值和argv[]各元素的值都是系统自动组赋值的。
Solaris—解析传递给程序的参数 程序范例 #include "stdio.h" int main(int argc, char *argv[]) { int i=0; printf("The number of command line arguments is:%d\n",argc); printf("The program name is:%s\n",argv[0]); printf("The command line arguments:\n"); for (i=1; i<argc; i++) printf("argv[%d]:%s\n",i,argv[i]); } return 1; root@server1{/skystone} #./arguments.o debug hello=1 The number of command line arguments is:3 The program name is:./arguments.o The command line arguments: argv[1]:debug argv[2]:hello=1
Solaris—守护进程 守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Unix的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。 守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则并不一致。这需要读者注意,照搬某些书上的规则会出现错误的。
Solaris—守护进程 fork的含义 进程A调用int pid=fork()之后,实际上是克隆出来一个子进程B。 进程A是进程B的父进程;进程B是进程A的子进程。 进程B与进程A在堆栈、环境、状态方面在fork的时候是一样的。 在进程A即父进程fork()的返回值非0,返回的是子进程的pid;在进程B即子进程fork()返回的是0。根据返回值,主子进程按照自己的不同逻辑执行下去。
Solaris—守护进程 Solaris下守护进程实现函数 int sux_init_daemon(void) //返回值 !=2 退出程序 { int pid; int i; pid=fork();//派生一个进程 if (pid<0) return 0;//派生失败 if(pid!=0) exit(0);//是父进程,结束父进程 //是第一子进程,后台继续执行 setsid();//第一子进程成为新的会话组长和进程组长 并与控制终端分离 signal( SIGHUP, SIG_IGN );//忽略SIGHUP pid=fork();//再次派生一个进程 if(pid!=0) exit(0);//是第一子进程,结束第一子进程 //是第二子进程,继续 第二子进程不再是会话组长 for(i=0;i< getdtablesize();i++)//关闭打开的文件描述符 close(i); chdir("/tmp");//改变工作目录到/tmp umask(0);//重设文件创建掩模 return 2; }
Solaris—文件访问 打开文件 FILE * fp=fopen(filename,“mode"); mode:r文本方式读打开 w文本方式生成并写打开(原文件信息丢失) a文本方式打开文件追加 rb二进制方式读打开 wb二进制方式生成并写打开(原文件信息丢失) ab二进制方式打开文件追加 r+b二进制方式读/写打开文件 w+b二进制方式生成读/写打开文件(原文件信息丢失) a+b二进制方式读/写打开文件 以读写方式打开文件时,读操作与写操作(无先后顺序)之间必须有下列函数之一:fflush(),fseek(),fsetpos(),rewind()
Solaris—文件访问 关闭文件 重要函数 int fflush(FILE *fp) int fclose(FILE* fp) 重要函数 int fflush(FILE *fp) 如果文件写打开,应调用fflush()将输出缓冲区中的内容物理写入文件。
Solaris—文件访问 例一:读文本文件 char readbuf[512]; FILE *fp=fopen(filename,"rt"); if (fp==NULL) { if (errno==ENOENT) return 2;//文件不存在 return -1; } while (!feof(fp)) if (fscanf(fp,"%511s\n",readbuf)!=1) continue; … … if (fp!=NULL) fclose(fp); fp=NULL;
Solaris—文件访问 void handleftplog(char * s1)//写日志 s1中已经有\n 例二:写文本文件-日志文件 { struct tm nowtm; time_t nowtime; char log[1024]; char filename[128]; struct timeval ntv; gettimeofday(&ntv,NULL); nowtime=ntv.tv_sec; localtime_r(&nowtime,&nowtm); sprintf(log,"%04d-%02d-%02d %02d:%02d:%02d [%d]%s",nowtm.tm_year+1900,nowtm.tm_mon+1,nowtm.tm_mday, nowtm.tm_hour,nowtm.tm_min,nowtm.tm_sec,getpid(),s1); sprintf(filename,"%s/%04d%02d%02d.log",FTP_LOG_PATH,nowtm.tm_year+1900,nowtm.tm_mon+1,nowtm.tm_mday); FILE * fp fp=fopen(filename,"a+"); if (fp==NULL) return; fprintf(fp,"%s",log); fflush(fp); fclose(fp); }