免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1177 | 回复: 0
打印 上一主题 下一主题

Unix编程常见问题解答3(转) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-26 09:09 |只看该作者 |倒序浏览

                                 
                                                                            6. 工具的使用
*************
6.1 我怎样调试fork函数产生的子进程?
====================================
根据可用的工具有两种不同的方法:
你的调试器(debugger)可能有允许你选择是否跟踪调用‘fork()’以后的父或子进程
的选项,对于某些目的来说那已经足够了。
替换方法是,你的调试器可能有一个选项允许你将它依附(attach)到一个正在执行
的程序。这样你可以依附调试器到一个已经开始执行的子进程。如果你不需要从
子进程一开始就开始测试,这通常已经足够。否则,你会希望在子进程的‘fork()’
调用后插入一个‘sleep()’调用,或者插入如下的循环:
     {
         volatile int f = 1;
         while(f);
     }
这样子进程将一直在此循环不往下执行直到你用调试器设定‘f’为0。
并且记住,使用调试器并非是找到你程序中错误的唯一方法;在很多Unix系统
上有一些工具程序可用来跟踪系统调用和信号,而且丰富的日志经常也是有
用的。
6.2 怎样通过其他库文件建立新的库文件?
======================================
前提是我们所说的是归档(archive)(静态)库,最简单的方法是将所有选择的库
文件使用‘ar x’命令在一个空目录里拆分成它们原始的单个目标文件(object),
然后再合并成一个。当然,文件名有可能会重复,但是如果这个库文件很大,
你也许一开始就不想将它们合并在一起。
6.3 怎样创建动态连接库(shared library)/dlls?
=============================================
创建动态连接库(shared libraries)的方法根据不同的系统有所不同。这个过程主要
分两步;第一步要求包括在动态连接库中的目标必须首先是编译好的,通常需
要某个编译选项指示这串代码是位置无关的(position-indepenent);第二步,是将
这些目标连接在一起形成一个库文件。
这里是一个演示以上道理的小程序:
     /* shrobj.c 文件 */
     const char *myfunc()
     {
         return "Hello World";
     }
     /* shrobj.c 结束 */
     /* hello.c 文件 */
     #include
     extern const char *myfunc();
     main()
     {
         printf("%s\n", myfunc());
         return 0;
     }
     /* hello.c 结束 */
     $ gcc -fpic -c shrobj.c
     $ gcc -shared -o libshared.so shrobj.o
     $ gcc hello.c libshared.so
     $ ./a.out
     Hello World
到目前为止,如果你希望库文件和它的创建过程是都可以移植的话,那么最好
的办法是使用GNU Libtool程序包。它是个小型的工具程序套件,这些工具程序
知道建立动态连接库的平台无关性;你可以只发布你的程序必要的部分,从而
当一个安装者配置你的软件包时,他能决定生成什么库。Libtool程序包在不支持
动态连接库的系统上也能工作得很好。它而且知道与GNU Autoconf程序和GNU
Automake程序挂钩(如果你使用这些工具来管理你程序的编译创建过程)。
如果你不想使用Libtool程序包,那么对于gcc以外的编译器,你需要按照下面所
列修改编译器参数:
AIX 3.2 使用 xlc (未证实)
     取消‘-fpic’选项,以‘-bM:SRE -bE:libshared.exp’取代‘-shared’。你并且
     需要创建一个名为‘libshared.exp’的文件保存一个所有输出符号(symbols to export)
     的列表,比如以上的范例程序,就需要输出‘myfunc’符号。另外,在连接库
     时使用‘-e _nostart’参数(在较新的AIX版本上,我相信应该将其变成‘-bnoentry’)。
SCO OpenServer 5 使用 SCO 开发系统(Development System) (未证实)
     如果你使用ELF(译者注:ELF:执行与连接格式Executable and Linking Forrmat,
     一种Unix可执行目标文件的格式)格式,那么共享库只能在OS5上可用,而它
     需要‘-belf’选项。并以‘-Kpic’取代‘-fpic’,在连接时使用‘cc -belf -G’。
Solaris 使用 SparcWorks 编译器
     以‘-pic’取代‘-fpic’,并以‘ld -G’取代‘gcc -shared’。
(鼓励大家提供更多的材料丰富上述列表)
其它要当心的问题:
   * AIX和(我相信)Digital Unix不需要-fpic选项,因为所有代码都是位置无关的。
   * AIX一般需要你创建一个‘输出文件’,即一个保存所有动态连接库中输出
     符号的列表。一些连接器(linker)版本(可能只有SLHS连接器,是svld?)有一个
     选项可以输出所有符号。
   * 如果你对于连接器想使用普遍的‘-l’参数来引用你的动态连接库,你必须
     理解你的系统在实际运行时是怎样寻找动态连接库的。最普通的方法是使用
     ‘LD_LIBRARY_PATH’环境变量,但是通常在连接时有一种其它选项可以
     设定。
   * 大多数实现方法是在程序内部记录所希望的动态连接库在运行时的位置。这
     样把一个动态连接库从一个目录移到另一个目录将导致程序无法工作。许多
     系统对于连接器有一个选项用以设定希望运行时动态连接库的位置(比如在
     Solaris系统上是‘-R’连接器选项,或者是‘LD_RUN_PATH’环境变量)。
   * ELF和a.out的实现方法可能有一个连接器选项‘-Bsymbolic’,它导致在库本
     身内部的引用被解释好。否则,在这些系统上,所有符号的解释将推迟到最
     后连接时再进行,这样在main程序中的单一函数将重载库中的对应函数。
6.4 我能更改一个动态连接库里的目标吗?
======================================
一般不行。
在大多数系统上(除了AIX),当你连接目标并形成一个动态连接库时,它就象连
接一个可执行程序;目标并不保留它们单一的特征。结果是,一般不能从一个动
态连接库里析取出或更换一个单一的目标。
6.5 我能在一个运行着的程序中生成堆栈映象吗?
============================================
一些系统提供库函数可以提取(unwinding)出堆栈,从而(比如)你可以在一个错误
处理函数中生成一个堆栈映象,但是只有一小部分系统有这些函数。
一个可能的变通方法(workaround)是让你的程序执行一个调试器调试*它自己* -
详细方法仍然根据不同系统稍有不同,但一般的概念是这样:
     void dump_stack(void)
     {
         char s[160];
         sprintf(s, "/bin/echo 'where\ndetach' | dbx -a %d", getpid());
         system(s);
         return;
     }
你需要根据你不同的系统对dbx的参数和命令进行加工,或者甚至换另一个调试
器,比如‘gdb’,但这仍然是我见过的对于这种问题最普遍的解决方法。为此,
荣誉授予Ralph Corderoy。
下面列表包含在一些系统上需要用到的命令行:
大多数使用dbx的系统
    `"/bin/echo 'where\ndetach' | dbx /path/to/program %d"'
AIX
     `"/bin/echo 'where\ndetach' | dbx -a %d"'
IRIX
     `"/bin/echo 'where\ndetach' | dbx -p %d"'
  ?
范例程序
********
捕获 SIGCHLD 信号
=================
     #include   
     #include   
     #include     
     #include      
     #include        
     /* 我们的 SIGCHLD 信号处理函数的原形(prototype) */
    void sig_chld(int);     
     int main()
     {
         struct sigaction act;
         pid_t pid;
   
         /* 设定sig_chld函数作为我们SIGCHLD信号的处理函数 */
         act.sa_handler = sig_chld;
   
         /* 在这个范例程序里,我们不想阻塞其它信号 */
         sigemptyset(&act.sa_mask);
   
         act.sa_flags = SA_NOCLDSTOP;
   
         /*
          * 使这些设定的值生效. 如果我们是写一个真实的应用程序,
          * 我们也许应该保存这些原有值,而不是传递一个NULL。
          */
         if (sigaction(SIGCHLD, &act, NULL) p_pid;
             if((cur_user=kvm_getu(kd,cur_proc))!=NULL){
                 error=kvm_getcmd(kd,cur_proc,cur_user,&arg,NULL);
                 if(error==-1){
                     if(cur_user->u_comm[0]!='\0'){
                         p_name=cur_user->u_comm;
                     }
                 }
                 else{
                     p_name=arg[0];
                 }
             }
             if(p_name){
                 if(!strcmp(p_name,name)){
                     if(error!=-1){
                         free(arg);
                     }
                     if(skipit!=-1 && ourretval==skipit){
                         ourretval=-1;
                     }
                     else{
                         close(fd);
                         break;
                     }
                     break;
                 }
                 else{
                     if(step(p_name,expbuf)){
                         if(error!=-1){
                             free(arg);
                         }
                         break;
                     }
                 }
             }
             if(error!=-1){
                 free(arg);
             }
             p_name=NULL;
         }
         kvm_close(kd);
         if(p_name!=NULL){
             return(curpid);
         }
         return (-1);
     }
读取进程表 - SYSV 版
====================
     pid_t
     getpidbyname(char *name,pid_t skipit)
     {
         DIR  *dp;
         struct dirent *dirp;
         prpsinfo_t retval;
         int fd;
         pid_t ourretval=-1;
   
         if((dp=opendir("/proc"))==NULL){
             return -1;
         }
         chdir("/proc");
         while((dirp=readdir(dp))!=NULL){
             if(dirp->d_name[0]!='.'){
                 if((fd=open(dirp->d_name,O_RDONLY))!=-1){
                     if(ioctl(fd,PIOCPSINFO,&retval)!=-1){
                         if(!strcmp(retval.pr_fname,name)){
                             ourretval=(pid_t)atoi(dirp->d_name);
                             if(skipit!=-1 && ourretval==skipit){
                                 ourretval=-1;
                             }
                             else{
                                 close(fd);
                                 break;
                             }
                         }
                     }
                     close(fd);
                 }
             }
         }
         closedir(dp);
         return ourretval;
     }
读取进程表 - AIX 4.2 版
=======================
     #include
     #include
   
     int getprocs(struct procsinfo *, int, struct fdsinfo *,
                  int, pid_t *, int);
   
     pid_t getpidbyname(char *name, pid_t *nextPid)
     {
       struct procsinfo  pi;
       pid_t             retval = (pid_t) -1;
       pid_t             pid;
   
       pid = *nextPid;
   
       while(1)
       {
         if(getprocs(&pi, sizeof pi, 0, 0, &pid, 1) != 1)
           break;
   
         if(!strcmp(name, pi.pi_comm))
         {
           retval = pi.pi_pid;
           *nextPid = pid;
           break;
         }
       }
   
       return retval;
     }
   
     int main(int argc, char *argv[])
     {
       int   curArg;
       pid_t pid;
       pid_t nextPid;
   
       if(argc == 1)
       {
         printf("syntax: %s [program ...]\n",argv[0]);
         exit(1);
       }
   
       for(curArg = 1; curArg /dev/null", pid);
        fp = popen(command, "r");
        if ((FILE *)0 == fp) return (char *)0;
   
        /* 读取标题行 */
        if ((char *)0 == fgets(line, sizeof line, fp))
        {
           pclose(fp);
           return (char *)0;
        }
   
        /* 从标题栏分析出命令名所在列。
         * (BSD风格的系统将指示命令的"COMMAND"字符串放在第5列,SysV好象将
         * 指示命令的“CMD”或“COMMAND”字符串放在第4列)
         */
        for (linep = line; ; linep = (char *)0)
        {
           if ((char *)0 == (token = strtok(linep, " \t\n")))
           {
              pclose(fp);
              return (char *)0;
           }
           if (0 == strcmp("COMMAND", token) || 0 == strcmp("CMD", token))
           { /*  我们找到COMMAND所在列 */
              cmd = token;
              break;
           }
        }
   
        /* 读取 ps(1) 输出行 */
        if ((char *)0 == fgets(line, sizeof line, fp))
        {
           pclose(fp);
           return (char *)0;
        }
   
        /* 抓COMMAND标题下面的词 ... */
        if ((char *)0 == (token = strtok(cmd, " \t\n")))
        {
           pclose(fp);
           return (char *)0;
        }
   
        status = pclose(fp);
        if (!WIFEXITED(status) || 0 != WEXITSTATUS(status))
          return (char *)0;
   
        return token;
     }
   
     int main(int argc, char *argv[])
     {
        puts(procname(atoi(argv[1])));
        exit(EXIT_SUCCESS);
     }
守护程序工具函数
================
     #include
     #include
     #include
     #include
     #include
     #include
     #include
   
     /* closeall() -- 关闭所有>=给定值的文件描述符 */
   
     void closeall(int fd)
     {
         int fdlimit = sysconf(_SC_OPEN_MAX);
   
         while (fd = 0)
               switch (fork2())
               {
                 case 0:  close(sock); run_child(rc); _exit(0);
                 case -1: errreport("fork2"); close(rc); break;
                 default: close(rc);
               }
         }
     }
   
     int main()
     {
         if (daemon(0,0) = 0)
             close(fd);
         errno = err;
         return -1;
     }
   
   
     int open_port(const char *name, speed_t baud, enum flowmode flow)
     {
         int flags;
         struct termios attr;
   
         int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);
   
         if (fd tv_sec += secs;
         if ((tv->tv_usec += usecs) >= 1000000)
         {
             tv->tv_sec += tv->tv_usec / 1000000;
             tv->tv_usec %= 1000000;
         }
     }
   
     /* 设定 *RES = *A - *B, 返回结果的符号 */
   
     static int timersub(struct timeval *res,
                         const struct timeval *a, const struct timeval *b)
     {
         long sec = a->tv_sec - b->tv_sec;
         long usec = a->tv_usec - b->tv_usec;
   
         if (usec tv_sec = sec;
         res->tv_usec = usec;
   
         return (sec =给定FD的文件描述符 */
   
     void closeall(int fd)
     {
         int fdlimit = sysconf(_SC_OPEN_MAX);
   
         while (fd 2的文件描述符(一个经常过份估计的工作)
      */
   
     pid_t spawn_background_command(const char *cmd,
                                    int *infd, int *outfd, int *errfd)
     {
         int nullfd = -1;
         int pipefds[3][2];
         int error = 0;
   
         if (!cmd)
             return errno = EINVAL, -1;
   
         pipefds[0][0] = pipefds[0][1] = -1;
         pipefds[1][0] = pipefds[1][1] = -1;
         pipefds[2][0] = pipefds[2][1] = -1;
   
         if (infd && pipe(pipefds[0]) = 0)
                         close(pipefds[j]);
         }
   
         if (nullfd >= 0)
             close(nullfd);
   
         return errno = error, (pid_t) -1;
     }
   
   
     /*---------------------------------------*/
     /* 这里是使用上述函数一个小例子.         */
   
     pid_t bgjob = -1;
     volatile int signo = 0;
   
     #ifndef WCOREDUMP
      /* 如果没有 WCOREDUMP, 你也许会希望在你的平台上为它设置一个准确的定义
       * (这通常是(status & 0x80) 但也不总是这样),或者就赌没有core dumps(
       * 就象这个程序所做)
       */
     # define WCOREDUMP(status) (0)
     #endif
   
     int check_children()
     {
         pid_t pid;
         int status;
         int count = 0;
   
         while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
         {
             if (pid == bgjob && !WIFSTOPPED(status))
                 bgjob = -1;
   
             ++count;
   
             if (WIFEXITED(status))
                 fprintf(stderr,"Process %ld exited with return code %d\n",
                         (long)pid, WEXITSTATUS(status));
             else if (WIFSIGNALED(status))
                 fprintf(stderr,"Process %ld killed by signal %d%s\n",
                         (long)pid, WTERMSIG(status),
                         WCOREDUMP(status) ? " (core dumped)" : "");
             else if (WIFSTOPPED(status))
                 fprintf(stderr,"Process %ld stopped by signal %d\n",
                         (long)pid, WSTOPSIG(status));
             else
                 fprintf(stderr,"Unexpected status - pid=%ld, status=0x%x\n",
                         (long)pid, status);
         }
   
         return count;
     }
   
   
     void sighandler(int sig)
     {
         if (sig != SIGCHLD)
             signo = sig;
     }
   
   
     int main()
     {
         struct sigaction act;
         int sigcount = 0;
   
         act.sa_handler = sighandler;
         act.sa_flags = 0;
         sigemptyset(&act.sa_mask);
         sigaction(SIGINT,&act,NULL);
         sigaction(SIGQUIT,&act,NULL);
         sigaction(SIGTERM,&act,NULL);
         sigaction(SIGTSTP,&act,NULL);
         sigaction(SIGCHLD,&act,NULL);
   
         fprintf(stderr,"Starting background job 'sleep 60'\n");
         bgjob = spawn_background_command("sleep 60", NULL, NULL, NULL);
         if (bgjob = 0)
         {
             if (signo)
             {
                 fprintf(stderr,"Signal %d caught\n", signo);
                 if (sigcount++)
                     kill_job(bgjob, SIGKILL);
                 else
                 {
                     kill_job(bgjob, SIGTERM);
                     kill_job(bgjob, SIGCONT);
                 }
             }
   
             if (!check_children())
                 pause();
         }
   
         fprintf(stderr,"Done - exiting\n");
         return 0;
     }
转自http://apig.bokee.com,添加了部分头文件
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/43678/showart_346946.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP