900 likes | 1.1k Views
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 工作正常
E N D
Solaris 10C编程 南京天石软件技术有限公司 陈锺 (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安装 • 为Sun Studio配置环境 在/.profile文件的PATH变量赋值中加入/opt/SUNWspro/bin
Solaris—gcc • 在为Unix开发应用程序时,绝大多数情况下使用的都是C语言,因此几乎每一位Linux程序员面临的首要问题都是如何 灵活运用C编译器. 目前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 #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 • 运行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) return 0;//派生失败 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 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); }
Solaris—Posix共享内存 • Posix标准 由于Unix版本的多样性,电子电气工程协会(IEEE)开发了一个独立的Unix标准,这个新的ANSI Unix标准被称为计算机环境的可移植性操作系统界面(POSIX)。现有大部分Unix和流行版本都是遵循POSIX标准的,而Linux从一开始就遵 循POSIX标准。 • 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
Solaris—Posix共享内存 • 打开、创建共享内存 int shm_open(const char *name, int oflag, mode_t mode); name:共享内存名称,共享内存唯一标示,为兼容性考虑,一般以/开头 oflag:O_RDONLY读方式打开 O_RDWR读写方式打开 O_CREAT如果不存在则创建 O_EXCL如果O_CREAT同时被设置,那么共享内存如果已存在,就返回失败 O_TRUNC如果共享内存被正确可读写打开,那么该共享内存被重置为0长度
Solaris—Posix共享内存 mode一般设置为S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH,表示共享内存的访问权限。 返回值: 成功 返回句柄>=0 失败 返回-1 错误码 errorno
Solaris—Posix共享内存 • 关闭共享内存 int close(int fd); • 设置共享内存大小 int ftruncate(int fd,off_t size); 成功 返回0 失败 返回-1 errno • 得到共享内存大小 int fstat(int fd, struct stat * buf); 成功 返回0 失败 返回-1 errno 内存大小=buf->st_size;
Solaris—Posix共享内存 • 映射内存地址 void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off); addr:指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。 len:是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。 prot :参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。 flags:通常为MAP_SHARED 。 fd:为即将映射到进程空间的文件描述字,一般由open()返回。 off:参数一般设为0,表示从文件头开始映射。 成功 返回指针 失败 返回-1
Solaris—Posix共享内存 • 关闭映射 int munmap(void *addr, size_t len); addr:mmap映射的指针 len:mmap映射的大小 返回成功 0 失败-1 • 删除共享内存 int shm_unlink(const char *name); name:共享内存名 返回成功 0 失败-1
Solaris—Posix共享内存 • 头文件 #include<stdlib.h> #include<stdio.h> #include<unistd.h> #include<sys/mman.h> #include<sys/time.h> #include<strings.h> #include<sys/types.h> #include<errno.h> #include<string.h> #include<sys/stat.h> #include<fcntl.h> #include<string.h> #include <ctype.h> • 编译时加上-lrt gcc createshm.c -o createshm.o -lrt
int main(int argc,char * argv[]) //createshm.c { printf("usage:%s shmname string\n",argv[0]); if (argc!=3) return 0; size_t size=(strlen(argv[2])+1)*sizeof(char); shm_unlink(argv[1]); int fd; fd=shm_open(argv[1],O_RDWR|O_CREAT|O_EXCL , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd==-1) { printf("shm_open failed err:[%d]%s\n",errno,strerror(errno)); return 0; } if (ftruncate(fd,size)==-1) { printf("ftruncate failed err:[%d]%s\n",errno,strerror(errno)); } char * p=NULL; p=(char *)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if ((int)p==-1) { printf("mmap failed err:[%d]%s\n",errno,strerror(errno)); } memcpy(p,argv[2],size); munmap(p,size); close(fd); printf("ok\n"); }
int main(int argc,char * argv[]) //readshm.c { printf("usage:%s shmname\n",argv[0]); if (argc!=2) return 0; size_t size;struct stat statbuf; int fd; fd=shm_open(argv[1],O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd==-1) { printf("shm_open failed err:[%d]%s\n",errno,strerror(errno)); return 0; } if (fstat(fd, &statbuf)==-1) { printf("fstat failed err:[%d]%s\n",errno,strerror(errno)); } size=statbuf.st_size; char * p=NULL; p=(char *)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if ((int)p==-1) { printf("mmap failed err:[%d]%s\n",errno,strerror(errno)); } printf("readshm:%s\n",p); munmap(p,size); close(fd); printf("ok\n"); }
Solaris—Posix共享内存 • #gcc createshm.c -o createshm.o -lrt • #gcc readshm.c -o readshm.o -lrt • #./createshm.o /tshm helloworld! usage:./createshm.o shmname string ok • #./readshm.o /tshm usage:./readshm.o shmname readshm:helloworld! ok