??xml version="1.0" encoding="utf-8" standalone="yes"?>日韩在线视精品在亚洲,亚洲人成网址在线观看,亚洲欧洲久久avhttp://www.tkk7.com/huyi2006/articles/380611.htmlallicallicTue, 12 Jun 2012 07:38:00 GMThttp://www.tkk7.com/huyi2006/articles/380611.htmlhttp://www.tkk7.com/huyi2006/comments/380611.htmlhttp://www.tkk7.com/huyi2006/articles/380611.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/380611.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/380611.html

allic 2012-06-12 15:38 发表评论
]]>
linux下C语言实现文g传输的简单实?/title><link>http://www.tkk7.com/huyi2006/articles/263836.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 03 Apr 2009 15:06:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/263836.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/263836.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/263836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/263836.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/263836.html</trackback:ping><description><![CDATA[ <p>实例来自互联|,q段试代码实现了基本的文g传输原理Q没有实现错误处理?br /><br />//////////////////////////////////////////////////////////////////////////////////////<br />// file_server.c  文g传输序服务器示?br />//////////////////////////////////////////////////////////////////////////////////////<br />//本文件是服务器的代码<br />#include <netinet/in.h>    // for sockaddr_in<br />#include <sys/types.h>    // for socket<br />#include <sys/socket.h>    // for socket<br />#include <stdio.h>        // for printf<br />#include <stdlib.h>        // for exit<br />#include <string.h>        // for bzero<br />/*<br />#include <sys/types.h><br />#include <sys/stat.h><br />#include <fcntl.h><br />#include <unistd.h><br />*/<br />#define HELLO_WORLD_SERVER_PORT    6666 <br />#define LENGTH_OF_LISTEN_QUEUE  20<br />#define BUFFER_SIZE 1024<br />#define FILE_NAME_MAX_SIZE 512<br /><br />int main(int argc, char **argv)<br />{<br />    //讄一个socket地址l构server_addr,代表服务器internet地址, 端口<br />    struct sockaddr_in server_addr;<br />    bzero(&server_addr,sizeof(server_addr)); //把一D内存区的内容全部设|ؓ0<br />    server_addr.sin_family = AF_INET;<br />    server_addr.sin_addr.s_addr = htons(INADDR_ANY);<br />    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);<br /><br />    //创徏用于internet的流协议(TCP)socket,用server_socket代表服务器socket<br />    int server_socket = socket(PF_INET,SOCK_STREAM,0);<br />    if( server_socket < 0)<br />    {<br />        printf("Create Socket Failed!");<br />        exit(1);<br />    }<br />    <br />    //把socket和socket地址l构联系h<br />    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))<br />    {<br />        printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT); <br />        exit(1);<br />    }<br />    <br />    //server_socket用于监听<br />    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )<br />    {<br />        printf("Server Listen Failed!"); <br />        exit(1);<br />    }<br />    while (1) //服务器端要一直运?br />    {<br />        //定义客户端的socket地址l构client_addr<br />        struct sockaddr_in client_addr;<br />        socklen_t length = sizeof(client_addr);<br /><br />        //接受一个到server_socket代表的socket的一个连?br />        //如果没有q接h,q待到有连接请?-q是accept函数的特?br />        //accept函数q回一个新的socket,q个socket(new_server_socket)用于同连接到的客L通信<br />        //new_server_socket代表了服务器和客L之间的一个通信通道<br />        //accept函数把连接到的客L信息填写到客L的socket地址l构client_addr?br />        int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);<br />        if ( new_server_socket < 0)<br />        {<br />            printf("Server Accept Failed!\n");<br />            break;<br />        }<br />        <br />        char buffer[BUFFER_SIZE];<br />        bzero(buffer, BUFFER_SIZE);<br />        length = recv(new_server_socket,buffer,BUFFER_SIZE,0);//<font color="#ff0000">q里先接收客L发来的要获取的文件名</font><br />        if (length < 0)<br />        {<br />            printf("Server Recieve Data Failed!\n");<br />            break;<br />        }<br />        char file_name[FILE_NAME_MAX_SIZE+1];<br />        bzero(file_name, FILE_NAME_MAX_SIZE+1);<br />        strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));<br />//        int fp = open(file_name, O_RDONLY);<br />//        if( fp < 0 )<br />        FILE * fp = fopen(file_name,"r");<br />        if(NULL == fp )<br />        {<br />            printf("File:\t%s Not Found\n", file_name);<br />        }<br />        else<br />        {<br />            bzero(buffer, BUFFER_SIZE);<br />            int file_block_length = 0;<br />//            while( (file_block_length = read(fp,buffer,BUFFER_SIZE))>0)<br /><font color="#0000cc">            while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)<br />            {<br />                printf("file_block_length = %d\n",file_block_length);<br />                //发送buffer中的字符串到new_server_socket,实际是给客户?br />                if(send(new_server_socket,buffer,file_block_length,0)<0)<br />                {<br />                    printf("Send File:\t%s Failed\n", file_name);<br />                    break;<br />                }<br />                bzero(buffer, BUFFER_SIZE);<br />            }                                                                 //q段代码是@环读取文件的一D|据,在@环调用sendQ发送到客户端,q里一点的TCP每次接受最多是1024字节Q多了就会分片,因此每次发送时量不要过1024字节?/font><br />//            close(fp);<br />            fclose(fp);<br />            printf("File:\t%s Transfer Finished\n",file_name);<br />        }<br />        //关闭与客L的连?br />        close(new_server_socket);<br />    }<br />    //关闭监听用的socket<br />    close(server_socket);<br />    return 0;<br />}<br /><br /><br />//////////////////////////////////////////////////////////////////////////////////////<br />// file_client.c  文g传输客户端程序示?br />//////////////////////////////////////////////////////////////////////////////////////<br />//本文件是客户机的代码<br />#include <netinet/in.h>    // for sockaddr_in<br />#include <sys/types.h>    // for socket<br />#include <sys/socket.h>    // for socket<br />#include <stdio.h>        // for printf<br />#include <stdlib.h>        // for exit<br />#include <string.h>        // for bzero<br />/*<br />#include <sys/types.h><br />#include <sys/stat.h><br />#include <fcntl.h><br />#include <unistd.h><br />*/<br /><br />#define HELLO_WORLD_SERVER_PORT    6666 <br />#define BUFFER_SIZE 1024<br />#define FILE_NAME_MAX_SIZE 512<br /><br />int main(int argc, char **argv)<br />{<br />    if (argc != 2)<br />    {<br />        printf("Usage: ./%s ServerIPAddress\n",argv[0]);<br />        exit(1);<br />    }<br /><br />    //讄一个socket地址l构client_addr,代表客户机internet地址, 端口<br />    struct sockaddr_in client_addr;<br />    bzero(&client_addr,sizeof(client_addr)); //把一D内存区的内容全部设|ؓ0<br />    client_addr.sin_family = AF_INET;    //internet协议?br />    client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址<br />    client_addr.sin_port = htons(0);    //0表示让系l自动分配一个空闲端?br />    //创徏用于internet的流协议(TCP)socket,用client_socket代表客户机socket<br />    int client_socket = socket(AF_INET,SOCK_STREAM,0);<br />    if( client_socket < 0)<br />    {<br />        printf("Create Socket Failed!\n");<br />        exit(1);<br />    }<br />    //把客h的socket和客h的socket地址l构联系h<br />    if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr)))<br />    {<br />        printf("Client Bind Port Failed!\n"); <br />        exit(1);<br />    }<br /><br />    //讄一个socket地址l构server_addr,代表服务器的internet地址, 端口<br />    struct sockaddr_in server_addr;<br />    bzero(&server_addr,sizeof(server_addr));<br />    server_addr.sin_family = AF_INET;<br />    if(inet_aton(argv[1],&server_addr.sin_addr) == 0) //服务器的IP地址来自E序的参?br />    {<br />        printf("Server IP Address Error!\n");<br />        exit(1);<br />    }<br />    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);<br />    socklen_t server_addr_length = sizeof(server_addr);<br />    //向服务器发vq接,q接成功后client_socket代表了客h和服务器的一个socketq接<br />    if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0)<br />    {<br />        printf("Can Not Connect To %s!\n",argv[1]);<br />        exit(1);<br />    }<br /><br />    char file_name[FILE_NAME_MAX_SIZE+1];<br />    bzero(file_name, FILE_NAME_MAX_SIZE+1);<br />    printf("Please Input File Name On Server:\t");<br />    scanf("%s", file_name);<br />    <br />    char buffer[BUFFER_SIZE];<br />    bzero(buffer,BUFFER_SIZE);<br />    strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));<br />    //向服务器发送buffer中的数据<br />    send(client_socket,buffer,BUFFER_SIZE,0);<br /><br />//    int fp = open(file_name, O_WRONLY|O_CREAT);<br />//    if( fp < 0 )<br />    FILE * fp = fopen(file_name,"w");<br />    if(NULL == fp )<br />    {<br />        printf("File:\t%s Can Not Open To Write\n", file_name);<br />        exit(1);<br />    }<br />    <br />    //从服务器接收数据到buffer?br />    bzero(buffer,BUFFER_SIZE);<br />    int length = 0;<br /><font color="#0000cc">    while( length = recv(client_socket,buffer,BUFFER_SIZE,0))       //循环接收Q再写到文g<br />    {<br />        if(length < 0)<br />        {<br />            printf("Recieve Data From Server %s Failed!\n", argv[1]);<br />            break;<br />        }<br />//        int write_length = write(fp, buffer,length);<br />        int write_length = fwrite(buffer,sizeof(char),length,fp);<br />        if (write_length<length)<br />        {<br />            printf("File:\t%s Write Failed\n", file_name);<br />            break;<br />        }<br />        bzero(buffer,BUFFER_SIZE);    <br />    }</font><br />    printf("Recieve File:\t %s From Server[%s] Finished\n",file_name, argv[1]);<br />    <br />    close(fp);<br />    //关闭socket<br />    close(client_socket);<br />    return 0;<br />}</p> <img src ="http://www.tkk7.com/huyi2006/aggbug/263836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2009-04-03 23:06 <a href="http://www.tkk7.com/huyi2006/articles/263836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下的多线E编E?/title><link>http://www.tkk7.com/huyi2006/articles/256617.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Wed, 25 Feb 2009 06:53:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/256617.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/256617.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/256617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/256617.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/256617.html</trackback:ping><description><![CDATA[一介lLinux下的多线E编E的文章 <br />1 引言<br />  U程QthreadQ技术早?0q代p提出Q但真正应用多线E到操作pȝ中去Q是?0q代中期Qsolaris是这斚w的佼D。传l的<br />Unix也支持线E的概念Q但是在一个进E(processQ中只允许有一个线E,q样多线E就意味着多进E。现在,多线E技术已l被许多操作p?br />l所支持Q包括Windows/NTQ当Ӟ也包括Linux?br />  Z么有了进E的概念后,q要再引入线E呢Q用多U程到底有哪些好处?什么的pȝ应该选用多线E?我们首先必须回答q些问题?br />  使用多线E的理由之一是和q程相比Q它是一U非?节P"的多d操作方式。我们知道,在Linuxpȝ下,启动一个新的进E必d配给<br />它独立的地址I间Q徏立众多的数据表来l护它的代码Dc堆栈段和数据段Q这是一U?昂贵"的多d工作方式。而运行于一个进E中的多?br />U程Q它们彼此之间用相同的地址I间Q共享大部分数据Q启动一个线E所p的空间远q小于启动一个进E所p的空_而且Q线E间<br />彼此切换所需的时间也q远于q程间切换所需要的旉。据l计Qȝ说来Q一个进E的开销大约是一个线E开销?0倍左叻I当然Q在?br />体的pȝ上,q个数据可能会有较大的区别?br />  使用多线E的理由之二是线E间方便的通信机制。对不同q程来说Q它们具有独立的数据I间Q要q行数据的传递只能通过通信的方式进<br />行,q种方式不仅ҎQ而且很不方便。线E则不然Q由于同一q程下的U程之间׃n数据I间Q所以一个线E的数据可以直接为其它线E所<br />用,q不仅快P而且方便。当Ӟ数据的共享也带来其他一些问题,有的变量不能同时被两个线E所修改Q有的子E序中声明ؓstatic的数<br />据更有可能给多线E程序带来灾难性的打击Q这些正是编写多U程E序时最需要注意的地方?br />  除了以上所说的优点外,不和q程比较Q多U程E序作ؓ一U多d、ƈ发的工作方式Q当然有以下的优点:<br />  1)<br />提高应用E序响应。这对图形界面的E序其有意义,当一个操作耗时很长Ӟ整个pȝ都会{待q个操作Q此时程序不会响应键盘、鼠标?br />菜单的操作,而用多U程技术,耗时长的操作Qtime consumingQ置于一个新的线E,可以避免q种尬的情c?br />  2) 使多CPUpȝ更加有效。操作系l会保证当线E数不大于CPU数目Ӟ不同的线E运行于不同的CPU上?br />  3) 改善E序l构。一个既长又复杂的进E可以考虑分ؓ多个U程Q成为几个独立或半独立的q行部分Q这LE序会利于理解和修改?br />  下面我们先来试~写一个简单的多线E程序?br />2 单的多线E编E?br />  Linuxpȝ下的多线E遵循POSIXU程接口Q称为pthread。编写Linux下的多线E程序,需要用头文gpthread.hQ连接时需要用库libp<br />thread.a。顺便说一下,Linux下pthread的实现是通过pȝ调用cloneQ)来实现的。cloneQ)是Linux所Ҏ的系l调用,它的使用方式cM<br />forkQ关于cloneQ)的详l情况,有兴的读者可以去查看有关文档说明。下面我们展CZ个最单的多线E程?example1.c?br />/* example.c*/<br />#include <stdio.h><br />#include <pthread.h><br />void thread(void)<br />{<br />int i;<br />for(i=0;i<3;i++)<br />printf("This is a pthread.n");<br />}<br />int main(void)<br />{<br />pthread_t id;<br />int i,ret;<br />ret=pthread_create(&id,NULL,(void *) thread,NULL);<br />if(ret!=0){<br />printf ("Create pthread error!n");<br />exit (1);<br />}<br />for(i=0;i<3;i++)<br />printf("This is the main process.n");<br />pthread_join(id,NULL);<br />return (0);<br />}<br />我们~译此程序:<br />gcc example1.c -lpthread -o example1<br />q行example1Q我们得到如下结果:<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />This is the main process.<br />This is a pthread.<br />This is a pthread.<br />再次q行Q我们可能得到如下结果:<br />This is a pthread.<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />  前后两次l果不一Pq是两个U程争夺CPU资源的结果。上面的CZ中,我们使用C两个函数Q  pthread_create和pthread_joinQ?br />q声明了一个pthread_t型的变量?br />  pthread_t在头文g/usr/include/bits/pthreadtypes.h中定义:<br />  typedef unsigned long int pthread_t;<br />  它是一个线E的标识W。函数pthread_create用来创徏一个线E,它的原型为:<br />  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,<br />  void *(*__start_routine) (void *), void *__arg));<br />  W一个参Cؓ指向U程标识W的指针Q第二个参数用来讄U程属性,W三个参数是U程q行函数的v始地址Q最后一个参数是q行函数<br />的参数。这里,我们的函数thread不需要参敎ͼ所以最后一个参数设为空指针。第二个参数我们也设为空指针Q这样将生成默认属性的U程?br />对线E属性的讑֮和修Ҏ们将在下一节阐q。当创徏U程成功Ӟ函数q回0Q若不ؓ0则说明创建线E失败,常见的错误返回代码ؓEAGAIN<br />和EINVAL。前者表C系l限制创建新的线E,例如U程数目q多了;后者表C第二个参数代表的线E属性值非法。创建线E成功后Q新创徏?br />U程则运行参C和参数四定的函敎ͼ原来的线E则l箋q行下一行代码?br />  函数pthread_join用来{待一个线E的l束。函数原型ؓQ?br />  extern int pthread_join __P ((pthread_t __th, void **__thread_return));<br />  W一个参Cؓ被等待的U程标识W,W二个参Cؓ一个用户定义的指针Q它可以用来存储被等待线E的q回倹{这个函数是一个线E阻?br />的函敎ͼ调用它的函数一直等待到被等待的U程l束为止Q当函数q回Ӟ被等待线E的资源被收回。一个线E的l束有两U途径Q一U是<br />象我们上面的例子一P函数l束了,调用它的U程也就l束了;另一U方式是通过函数pthread_exit来实现。它的函数原型ؓQ?br />  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));<br />  唯一的参数是函数的返回代码,只要pthread_join中的W二个参数thread_return不是NULLQ这个值将被传递给<br />thread_return。最后要说明的是Q一个线E不能被多个U程{待Q否则第一个接收到信号的线E成功返回,其余调用pthread_join的线E则q?br />回错误代码ESRCH?br />  在这一节里Q我们编写了一个最单的U程Qƈ掌握了最常用的三个函数pthread_createQpthread_join和pthread_exit。下面,我们?br />了解U程的一些常用属性以及如何设|这些属性?br />3 修改U程的属?br />  在上一节的例子里,我们用pthread_create函数创徏了一个线E,在这个线E中Q我们用了默认参数Q即该函数的第二个参数设ؓNUL<br />L。的,对大多数E序来说Q用默认属性就够了Q但我们q是有必要来了解一下线E的有关属性?br />  属性结构ؓpthread_attr_tQ它同样在头文g/usr/include/pthread.h中定义,喜欢q根问底的h可以自己L看。属性g能直接设|?br />Q须使用相关函数q行操作Q初始化的函Cؓpthread_attr_initQ这个函数必dpthread_create函数之前调用。属性对象主要包括是否绑?br />、是否分R堆栈地址、堆栈大、优先。默认的属性ؓ非绑定、非分离、缺?M的堆栈、与父进E同L别的优先U?br />  关于U程的绑定,牉|到另外一个概念:轻进E(LWPQLight Weight<br />ProcessQ。轻q程可以理解为内核线E,它位于用户层和系l层之间。系l对U程资源的分配、对U程的控制是通过轻进E来实现的,一个轻<br />q程可以控制一个或多个U程。默认状况下Q启动多轻q程、哪些轻q程来控制哪些线E是ql来控制的,q种状况即称为非l定的。绑<br />定状况下Q则֐思义Q即某个U程固定?l?在一个轻q程之上。被l定的线E具有较高的响应速度Q这是因为CPU旉片的调度是面向轻q?br />E的Q绑定的U程可以保证在需要的时候它L一个轻q程可用。通过讄被绑定的轻进E的优先U和调度U可以得绑定的U程满诸如?br />时反应之cȝ要求?br />  讄U程l定状态的函数为pthread_attr_setscopeQ它有两个参敎ͼW一个是指向属性结构的指针Q第二个是绑定类型,它有两个取|<br />PTHREAD_SCOPE_SYSTEMQ绑定的Q和PTHREAD_SCOPE_PROCESSQ非l定的)。下面的代码卛_Z一个绑定的U程?br />#include <pthread.h><br />pthread_attr_t attr;<br />pthread_t tid;<br />/*初始化属性|均设为默认?/<br />pthread_attr_init(&attr);<br />pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);<br />pthread_create(&tid, &attr, (void *) my_function, NULL);<br />  U程的分ȝ态决定一个线E以什么样的方式来l止自己。在上面的例子中Q我们采用了U程的默认属性,即ؓ非分ȝ态,q种情况?br />Q原有的U程{待创徏的线E结束。只有当pthread_joinQ)函数q回Ӟ创徏的线E才终止,才能释放自己占用的系l资源。而分ȝE?br />不是q样子的Q它没有被其他的U程所{待Q自p行结束了Q线E也q止了Q马上释攄l资源。程序员应该Ҏ自己的需要,选择适当<br />的分ȝ态。设|线E分ȝ态的函数?pthread_attr_setdetachstateQpthread_attr_t *attr, int<br />detachstateQ。第二个参数可选ؓPTHREAD_CREATE_DETACHEDQ分ȝE)?PTHREAD<br />_CREATE_JOINABLEQ非分离U程Q。这里要注意的一ҎQ如果设|一个线Eؓ分离U程Q而这个线E运行又非常快,它很可能?br />pthread_create函数q回之前q止了Q它l止以后可能将U程号和pȝ资源UMl其他的U程使用Q这栯用pthread_create的线E就?br />C错误的线E号。要避免q种情况可以采取一定的同步措施Q最单的Ҏ之一是可以在被创建的U程里调?br />pthread_cond_timewait函数Q让q个U程{待一会儿Q留够的旉让函数pthread_createq回。设|一D늭待时_是在多线E编E里?br />用的Ҏ。但是注意不要用诸如waitQ)之类的函敎ͼ它们是整个q程睡眠Qƈ不能解决U程同步的问题?br />  另外一个可能常用的属性是U程的优先Q它存放在结构sched_param中。用函数pthread_attr_getschedparam和函?br />pthread_attr_setschedparamq行存放Q一般说来,我们L先取优先U,对取得的g改后再存攑֛厅R下面即是一D늮单的例子?br />#include <stdio.h><br />#include <pthread.h><br />pthread_attr_t attr;<br />pthread_t tid;<br />sched_param param;<br />int newprio=20;<br />pthread_attr_init(&attr);<br />pthread_attr_getschedparam(&attr, &para;m);<br />param.sched_priority=newprio;<br />pthread_attr_setschedparam(&attr, &para;m);<br />pthread_create(&tid, &attr, (void *)myfunction, myarg);<br />  <br />4 U程的数据处?br />  和进E相比,U程的最大优点之一是数据的׃n性,各个q程׃n父进E处沿袭的数据段Q可以方便的获得、修Ҏ据。但q也l多U程<br />~程带来了许多问题。我们必d心有多个不同的进E访问相同的变量。许多函数是不可重入的,卛_时不能运行一个函数的多个拯Q除?br />使用不同的数据段Q。在函数中声明的静态变量常常带来问题,函数的返回g会有问题。因为如果返回的是函数内部静态声明的I间的地址<br />Q则在一个线E调用该函数得到地址后用该地址指向的数据时Q别的线E可能调用此函数q修改了q一D|据。在q程中共享的变量必须?br />关键字volatile来定义,q是Z防止~译器在优化Ӟ如gcc中?OX参数Q改变它们的使用方式。ؓ了保护变量,我们必须使用信号量?br />互斥{方法来保证我们对变量的正确使用。下面,我们逐步介绍处理U程数据时的有关知识?br />4.1 U程数据<br />  在单U程的程序里Q有两种基本的数据:全局变量和局部变量。但在多U程E序里,q有W三U数据类型:U程数据QTSD:<br />Thread-Specific<br />DataQ。它和全局变量很象Q在U程内部Q各个函数可以象使用全局变量一栯用它Q但它对U程外部的其它线E是不可见的。这U数据的?br />要性是显而易见的。例如我们常见的变量errnoQ它q回标准的出错信息。它昄不能是一个局部变量,几乎每个函数都应该可以调用它Q但?br />又不能是一个全局变量Q否则在<br />AU程里输出的很可能是BU程的出错信息。要实现诸如此类的变量,我们必M用线E数据。我们ؓ每个U程数据创徏一个键Q它和这个键<br />相关联,在各个线E里Q都使用q个键来指代U程数据Q但在不同的U程里,q个键代表的数据是不同的Q在同一个线E里Q它代表同样的数<br />据内宏V?br />  和线E数据相关的函数主要?个:创徏一个键Qؓ一个键指定U程数据Q从一个键dU程数据Q删除键?br />  创徏键的函数原型为:<br />  extern int pthread_key_create __P ((pthread_key_t *__key,<br />  void (*__destr_function) (void *)));<br />  W一个参Cؓ指向一个键值的指针Q第二个参数指明了一个destructor函数Q如果这个参C为空Q那么当每个U程l束Ӟpȝ调?br />q个函数来释攄定在q个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine)<br />(void)))一起用,Z让这个键只被创徏一ơ。函数pthread_once声明一个初始化函数Q第一ơ调用pthread_once时它执行q个函数Q以?br />的调用将被它忽略?br />  在下面的例子中,我们创徏一个键Qƈ它和某个数据相兌。我们要定义一个函数createWindowQ这个函数定义一个图形窗口(数据c?br />型ؓFl_Window *Q这是图形界面开发工具FLTK中的数据cdQ。由于各个线E都会调用这个函敎ͼ所以我们用线E数据?br />/* 声明一个键*/<br />pthread_key_t myWinKey;<br />/* 函数 createWindow */<br />void createWindow ( void ) {<br />Fl_Window * win;<br />static pthread_once_t once= PTHREAD_ONCE_INIT;<br />/* 调用函数createMyKeyQ创建键*/<br />pthread_once ( & once, createMyKey) ;<br />/*win指向一个新建立的窗?/<br />win=new Fl_Window( 0, 0, 100, 100, "MyWindow");<br />/* ҎH口作一些可能的讄工作Q如大小、位|、名U等*/<br />setWindow(win);<br />/* 窗口指针值绑定在键myWinKey?/<br />pthread_setpecific ( myWinKey, win);<br />}<br />/* 函数 createMyKeyQ创Z个键Qƈ指定了destructor */<br />void createMyKey ( void ) {<br />pthread_keycreate(&myWinKey, freeWinKey);<br />}<br />/* 函数 freeWinKeyQ释攄?/<br />void freeWinKey ( Fl_Window * win){<br />delete win;<br />}<br />  q样Q在不同的线E中调用函数createMyWinQ都可以得到在线E内部均可见的窗口变量,q个变量通过函数<br />pthread_getspecific得到。在上面的例子中Q我们已l用了函数pthread_setspecific来将U程数据和一个键l定在一赗这两个函数的原<br />型如下:<br />  extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));<br />  extern void *pthread_getspecific __P ((pthread_key_t __key));<br />  q两个函数的参数意义和用方法是显而易见的。要注意的是Q用pthread_setspecificZ个键指定新的U程数据Ӟ必须自己释放原有<br />的线E数据以回收I间。这个过E函数pthread_key_delete用来删除一个键Q这个键占用的内存将被释放,但同栯注意的是Q它只释N?br />用的内存Qƈ不释放该键关联的U程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线E数?br />的释攑ֿd释放键之前完成?br />4.2 互斥?br />  互斥锁用来保证一D|间内只有一个线E在执行一D代码。必要性显而易见:假设各个U程向同一个文仉序写入数据,最后得到的l果<br />一定是N性的?br />  我们先看下面一D代码。这是一个读/写程序,它们公用一个缓冲区Qƈ且我们假定一个缓冲区只能保存一条信息。即~冲区只有两个状?br />Q有信息或没有信息?br />void reader_function ( void );<br />void writer_function ( void );<br />char buffer;<br />int buffer_has_item=0;<br />pthread_mutex_t mutex;<br />struct timespec delay;<br />void main ( void ){<br />pthread_t reader;<br />/* 定义延迟旉*/<br />delay.tv_sec = 2;<br />delay.tv_nec = 0;<br />/* 用默认属性初始化一个互斥锁对象*/<br />pthread_mutex_init (&mutex,NULL);<br />pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);<br />writer_function( );<br />}<br />void writer_function (void){<br />while(1){<br />/* 锁定互斥?/<br />pthread_mutex_lock (&mutex);<br />if (buffer_has_item==0){<br />buffer=make_new_item( );<br />buffer_has_item=1;<br />}<br />/* 打开互斥?/<br />pthread_mutex_unlock(&mutex);<br />pthread_delay_np(&delay);<br />}<br />}<br />void reader_function(void){<br />while(1){<br />pthread_mutex_lock(&mutex);<br />if(buffer_has_item==1){<br />consume_item(buffer);<br />buffer_has_item=0;<br />}<br />pthread_mutex_unlock(&mutex);<br />pthread_delay_np(&delay);<br />}<br />}<br />  q里声明了互斥锁变量mutexQ结构pthread_mutex_tZ公开的数据类型,其中包含一个系l分配的属性对象。函?br />pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,调用函?br />pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函?br />pthread_mutexattr_settype用来讄互斥锁属性。前一个函数设|属性psharedQ它有两个取|<br />PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进E中的线E同步,后者用于同步本q程的不同线E。在上面的例子中Q?br />我们使用的是默认属性PTHREAD_PROCESS_<br />PRIVATE。后者用来设|互斥锁cdQ可选的cd有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK?br />PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机Ӟ一般情况下Q选用最后一个默认属性?br />  pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直臌用pthread_mutex_unlock为止Q均被上锁,卛_一旉只能被一个线E调<br />用执行。当一个线E执行到pthread_mutex_lock处时Q如果该锁此时被另一个线E用,那此U程被阻塞,即程序将{待到另一个线E释放此<br />互斥锁。在上面的例子中Q我们用了pthread_delay_np函数Q让U程睡眠一D|_是Z防止一个线E始l占据此函数?br />  上面的例子非常简单,׃再介l了Q需要提出的是在使用互斥锁的q程中很有可能会出现死锁Q两个线E试囑֐时占用两个资源,q按<br />不同的次序锁定相应的互斥锁,例如两个U程都需要锁定互斥锁1和互斥锁2QaU程先锁定互斥锁1QbU程先锁定互斥锁2Q这时就出现了死?br />。此时我们可以用函?br />pthread_mutex_trylockQ它是函数pthread_mutex_lock的非d版本Q当它发现死锁不可避免时Q它会返回相应的信息Q程序员可以针对死锁<br />做出相应的处理。另外不同的互斥锁类型对死锁的处理不一P但最主要的还是要E序员自己在E序设计注意q一炏V?br />4.3 条g变量<br />  前一节中我们讲述了如何用互斥锁来实现线E间数据的共享和通信Q互斥锁一个明昄~点是它只有两种状态:锁定和非锁定。而条?br />变量通过允许U程d和等待另一个线E发送信LҎ弥补了互斥锁的不I它常和互斥锁一起用。用时Q条件变量被用来d一个线<br />E,当条件不满ӞU程往往解开相应的互斥锁q等待条件发生变化。一旦其它的某个U程改变了条件变量,它将通知相应的条件变量唤?br />一个或多个正被此条件变量阻塞的U程。这些线E将重新锁定互斥锁ƈ重新试条g是否满。一般说来,条g变量被用来进行线扉K的同?br />?br />  条g变量的结构ؓpthread_cond_tQ函数pthread_cond_initQ)被用来初始化一个条件变量。它的原型ؓQ?br />  extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));<br />  其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条?br />变量的属性结构,和互斥锁一h们可以用它来讄条g变量是进E内可用q是q程间可用,默认值是 PTHREAD_<br />PROCESS_PRIVATEQ即此条件变量被同一q程内的各个U程使用。注意初始化条g变量只有未被使用时才能重新初始化或被释放。释放一个条?br />变量的函Cؓpthread_cond_ destroyQpthread_cond_t condQ。 <br />  函数pthread_cond_waitQ)使线E阻塞在一个条件变量上。它的函数原型ؓQ?br />  extern int pthread_cond_wait __P ((pthread_cond_t *__cond,<br />  pthread_mutex_t *__mutex));<br />  U程解开mutex指向的锁q被条g变量condd。线E可以被函数pthread_cond_signal和函?br />pthread_cond_broadcast唤醒Q但是要注意的是Q条件变量只是vd和唤醒线E的作用Q具体的判断条gq需用户l出Q例如一个变量是?br />?{等Q这一Ҏ们从后面的例子中可以看到。线E被唤醒后,它将重新查判断条件是否满I如果q不满Q一般说来线E应该仍d?br />q里Q被{待被下一ơ唤醒。这个过E一般用while语句实现?br />  另一个用来阻塞线E的函数是pthread_cond_timedwaitQ)Q它的原型ؓQ?br />  extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,<br />  pthread_mutex_t *__mutex, __const struct timespec *__abstime));<br />  它比函数pthread_cond_waitQ)多了一个时间参敎ͼl历abstimeD|间后Q即使条件变量不满Q阻塞也被解除?br />  函数pthread_cond_signalQ)的原型ؓQ?br />  extern int pthread_cond_signal __P ((pthread_cond_t *__cond));<br />  它用来释放被d在条件变量cond上的一个线E。多个线E阻塞在此条件变量上Ӟ哪一个线E被唤醒是由U程的调度策略所军_的。要<br />注意的是Q必ȝ保护条g变量的互斥锁来保护这个函敎ͼ否则条g满信号又可能在试条g和调用pthread_cond_wait函数之间被发出,?br />而造成无限制的{待。下面是使用函数pthread_cond_waitQ)和函数pthread_cond_signalQ)的一个简单的例子?br />pthread_mutex_t count_lock;<br />pthread_cond_t count_nonzero;<br />unsigned count;<br />decrement_count () {<br />pthread_mutex_lock (&count_lock);<br />while(count==0)<br />pthread_cond_wait( &count_nonzero, &count_lock);<br />count=count -1;<br />pthread_mutex_unlock (&count_lock);<br />}<br />increment_count(){<br />pthread_mutex_lock(&count_lock);<br />if(count==0)<br />pthread_cond_signal(&count_nonzero);<br />count=count+1;<br />pthread_mutex_unlock(&count_lock);<br />}<br />  countgؓ0Ӟ decrement函数在pthread_cond_wait处被dQƈ打开互斥锁count_lock。此Ӟ当调用到函数<br />increment_countӞpthread_cond_signalQ)函数改变条g变量Q告知decrement_countQ)停止d。读者可以试着让两个线E分别运行这<br />两个函数Q看看会出现什么样的结果?br />  函数pthread_cond_broadcastQpthread_cond_t<br />*condQ用来唤醒所有被d在条件变量cond上的U程。这些线E被唤醒后将再次竞争相应的互斥锁Q所以必d心用这个函数?br />4.4 信号?br />  信号量本质上是一个非负的整数计数器,它被用来控制对公p源的讉K。当公共资源增加Ӟ调用函数sem_postQ)增加信号量。只?br />当信号量值大于0Ӟ才能使用公共资源Q用后Q函数sem_waitQ)减少信号量。函数sem_trywaitQ)和函数pthread_<br />mutex_trylockQ)起同L作用Q它是函数sem_waitQ)的非d版本。下面我们逐个介绍和信号量有关的一些函敎ͼ它们都在头文?br />/usr/include/semaphore.h中定义?br />  信号量的数据cd为结构sem_tQ它本质上是一个长整型的数。函数sem_initQ)用来初始化一个信号量。它的原型ؓQ?br />  extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));<br />  sem为指向信号量l构的一个指针;pshared不ؓQ时此信号量在进E间׃nQ否则只能ؓ当前q程的所有线E共享;valuel出了信号量?br />初始倹{?br />  函数sem_post( sem_t *sem<br />)用来增加信号量的倹{当有线E阻塞在q个信号量上Ӟ调用q个函数会其中的一个线E不在阻塞,选择机制同样是由U程的调度策略决?br />的?br />  函数sem_wait( sem_t *sem<br />)被用来阻塞当前线E直C号量sem的值大?Q解除阻塞后sem的值减一Q表明公p源经使用后减。函数sem_trywait ( sem_t *sem<br />)是函数sem_waitQ)的非d版本Q它直接信号量sem的值减一?br />  函数sem_destroy(sem_t *sem)用来释放信号量sem?br />  下面我们来看一个用信号量的例子。在q个例子中,一共有4个线E,其中两个U程负责从文件读取数据到公共的缓冲区Q另两个U程?br />~冲取数据作不同的处理(加和乘运)?br />/* File sem.c */<br />#include<br />#include<br />#include<br />#define MAXSTACK 100<br />int stack[MAXSTACK][2];<br />int size=0;<br />sem_t sem;<br />/* 从文?.datd数据Q每Mơ,信号量加一*/<br />void ReadData1(void){<br />FILE *fp=fopen("1.dat","r");<br />while(!feof(fp)){<br />fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);<br />sem_post(&sem);<br />++size;<br />}<br />fclose(fp);<br />}<br />/*从文?.datd数据*/<br />void ReadData2(void){<br />FILE *fp=fopen("2.dat","r");<br />while(!feof(fp)){<br />fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);<br />sem_post(&sem);<br />++size;<br />}<br />fclose(fp);<br />}<br />/*d{待~冲区有数据Q读取数据后Q释攄_l箋{待*/<br />void HandleData1(void){<br />while(1){<br />sem_wait(&sem);<br />printf("Plus:%d+%d=%dn",stack[size][0],stack[size][1],<br />stack[size][0]+stack[size][1]);<br />--size;<br />}<br />}<br />void HandleData2(void){<br />while(1){<br />sem_wait(&sem);<br />printf("Multiply:%d*%d=%dn",stack[size][0],stack[size][1],<br />stack[size][0]*stack[size][1]);<br />--size;<br />}<br />}<br />int main(void){<br />pthread_t t1,t2,t3,t4;<br />sem_init(&sem,0,0);<br />pthread_create(&t1,NULL,(void *)HandleData1,NULL);<br />pthread_create(&t2,NULL,(void *)HandleData2,NULL);<br />pthread_create(&t3,NULL,(void *)ReadData1,NULL);<br />pthread_create(&t4,NULL,(void *)ReadData2,NULL);<br />/* 防止E序q早退出,让它在此无限期等?/<br />pthread_join(t1,NULL);<br />}<br />  在Linux下,我们用命令gcc -lpthread sem.c -o sem生成可执行文件sem?br />我们事先~辑好数据文?.dat?.datQ假讑֮们的内容分别? 2 3 4 5 6 7 8 9 10?-1 -2 -3 -4 -5 -6 -7 -8 -9 -10<br />Q我们运行semQ得到如下的l果Q?br />Multiply:-1*-2=2<br />Plus:-1+-2=-3<br />Multiply:9*10=90<br />Plus:-9+-10=-19<br />Multiply:-7*-8=56<br />Plus:-5+-6=-11<br />Multiply:-3*-4=12<br />Plus:9+10=19<br />Plus:7+8=15<br />Plus:5+6=11<br />  从中我们可以看出各个U程间的竞争关系。而数值ƈ未按我们原先的顺序显C出来这是由于sizeq个数D各个U程L修改的缘故。这<br />也往往是多U程~程要注意的问题?br />5 结<br />  多线E编E是一个很有意思也很有用的技术,使用多线E技术的|络蚂蚁是目前最常用的下载工具之一Q用多U程技术的grep比单U程<br />的grep要快上几倍,cM的例子还有很多。希望大家能用多U程技术写出高效实用的好程序来?br />本文:http://www.china-pub.com 作? 姚?(2001-08-11 09:05:00)<img src ="http://www.tkk7.com/huyi2006/aggbug/256617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2009-02-25 14:53 <a href="http://www.tkk7.com/huyi2006/articles/256617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BTU子文g格式http://www.tkk7.com/huyi2006/articles/241632.htmlallicallicThu, 20 Nov 2008 06:08:00 GMThttp://www.tkk7.com/huyi2006/articles/241632.htmlhttp://www.tkk7.com/huyi2006/comments/241632.htmlhttp://www.tkk7.com/huyi2006/articles/241632.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/241632.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/241632.htmlBTU子文g使用了一U叫bencoding的编码方法来保存数据?br />bencoding现有四种cd的数据:srings(字符?Qintegers(整数)Qlists(列表)Qdictionaries(字典)
~码规则如下Q?br />strings(字符?~码为:<字符串长?gt;Q?lt;字符?gt;
例如Q?:test 表示为字W串"test"
 4:例子 表示为字W串“例子?br />字符串长度单位ؓ字节
没开始或l束标记

integers(整数)~码为:i<整数>e
开始标记iQ结束标Cؓe
例如Q i1234e 表示为整?234
 i-1234e 表示为整?1234
整数没有大小限制
 i0e 表示为整?
 i-0e 为非?br />?开头的为非法如Q?i01234e 为非?/p>

lists(列表)~码为:l<bencoding~码cd>e
开始标Cؓl,l束标记为e
列表里可以包含Q何bencoding~码cdQ包括整敎ͼ字符Ԍ列表Q字典?br />例如Q l4:test5abcdee 表示Z个字W串["test","abcde"]

dictionaries(字典)~码为d<bencoding字符?gt;<bencoding~码cd>e
开始标Cؓd,l束标记为e
关键字必Mؓbencoding字符?br />值可以ؓMbencoding~码cd
例如Q d3:agei20ee 表示为{"age"=20}
 d4:path3:C:\8:filename8:test.txte 表示为{"path"="C:\","filename"="test.txt"}

具体文gl构如下Q?br />全部内容必须都ؓbencoding~码cd?br />整个文gZ个字典结?包含如下关键?br />announce:tracker服务器的URL(字符?
announce-list(可?:备用tracker服务器列?列表)
creation date(可?:U子创徏的时_Unix标准旉格式Q从1970 1??00:00:00到创建时间的U数(整数)
comment(可?:备注(字符?
created by(可?:创徏人或创徏E序的信?字符?
info:一个字典结构,包含文g的主要信息,为分二种情况Q单文gl构或多文gl构
单文件结构如下:
          length:文g长度Q单位字?整数)
          md5sum(可?Q长32个字W的文g的MD5校验和,BT不用这个|只是Z兼容一些程序所保留!(字符?
          name:文g?字符?
          piece length:每个块的大小Q单位字?整数)
          pieces:每个块的20个字节的SHA1 Hash的?二进制格?
多文件结构如下:
          files:一个字典结?br />                 length:文g长度Q单位字?整数)
                 md5sum(可?:同单文gl构中相?br />                 path:文g的\径和名字Q是一个列表结构,如\test\test.txt 列表为l4:test8test.txte
          name:最上层的目录名?字符?
          piece length:同单文gl构中相?br />          pieces:同单文gl构中相同?br />实例Q?br />用记事本打开一?torrent可以看来cM如下内容
d8:announce35:http://www.manfen.net:7802/announce13:creation datei1076675108e4:infod6:lengthi17799e4:name62:MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent12:piece lengthi32768e6:pieces20:?W ?w?R排T酆ee

很容易看?br />announceQ?a >http://www.manfen.net:7802/announce
creation dateQ?076675108U?02/13/04 20:25:08)
文g?MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent
文g大小Q?7799字节
文g块大=32768字节



allic 2008-11-20 14:08 发表评论
]]>
函数指针作ؓl构体成员,实现函数注册http://www.tkk7.com/huyi2006/articles/233710.htmlallicallicSat, 11 Oct 2008 02:24:00 GMThttp://www.tkk7.com/huyi2006/articles/233710.htmlhttp://www.tkk7.com/huyi2006/comments/233710.htmlhttp://www.tkk7.com/huyi2006/articles/233710.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/233710.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/233710.html有时我们需要将函数作ؓl构体的成员Q模拟C++cȝ情ŞQ可应用于方法注册?br />#include <stdio.h>

struct a
{
    void (*func)(char *);
};

void hello(char *name)
{
    printf ("hello %s\n",name);
}

int main()
{
    struct a a1;
    a1.func = hello;
    a1.func("illusion");
    system("PAUSE");
    return 0;
}



allic 2008-10-11 10:24 发表评论
]]>
Linux用Vim+Taglist+Ctags阅读~辑代码http://www.tkk7.com/huyi2006/articles/190989.htmlallicallicSat, 05 Apr 2008 15:22:00 GMThttp://www.tkk7.com/huyi2006/articles/190989.htmlhttp://www.tkk7.com/huyi2006/comments/190989.htmlhttp://www.tkk7.com/huyi2006/articles/190989.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/190989.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/190989.html 

其实q套l合很实用了 基本上到了不用鼠标的source insight境界了,最重要的是可以在text模式下运?

使用的^台是Fedora 8

Vim和Ctags在F8安装完后pȝ已经具备

Taglist需要自׃?

1.下蝲一个Taglist的zip文gQ然后解压羃Q将taglist.vim复制到~/.vim/plugin目录下?

2.修改~/.vim/plugin/taglist.vim

?if !exists('loaded_taglist')上面加入

let Tlist_Ctags_Cmd="/usr/bin/ctags"

l果为:

" Line continuation used here
let s:cpo_save = &cpo
set cpo&vim

let Tlist_Ctags_Cmd="/usr/bin/ctags"

if !exists('loaded_taglist')
" First time loading the taglist plugin
"
" To speed up the loading of Vim, the taglist plugin uses autoload
" mechanism to load the taglist functions.
" Only define the configuration variables, user commands and some
" auto-commands and finish sourcing the file

" The taglist plugin requires the built-in Vim system() function. If this
" function is not available, then don't load the plugin.
if !exists('*system')
echomsg 'Taglist: Vim system() built-in function is not available. ' .
\ 'Plugin is not loaded.'
let loaded_taglist = 'no'
let &cpo = s:cpo_save
finish
endif

此时Ctags和Taglist已经l合h?

3.在相应的源码目录q行ctags -R产生相应的tags文g

4.tags文g在vimq行时导入。可以修改~/.vimrcQ以后每ơ启动vim自动导入此tags文g

:set tags=/root/develop/honeyids/tags

q设|语法高?

syntax enable

syntax on

5.q行vimQ?ȀzTaglist时用:TaglistToggle命o。在左边的tags区域和正常编辑区域切换时用ctrl+2个w?

6.使用ctagsӞ ctrl+]可查看函数的定义?ctrl+oq回源文件?



allic 2008-04-05 23:22 发表评论
]]>
linux ׃n内存~程http://www.tkk7.com/huyi2006/articles/190846.htmlallicallicFri, 04 Apr 2008 15:55:00 GMThttp://www.tkk7.com/huyi2006/articles/190846.html׃n内存原型
shmid_dsl构
struct shmid_ds{
    struct ipc_perm    shm_perm;
    size_t                   shm_segsz;
    pid_t                    shm_lpid;
    pid_t                    shm_cpid;
    shmatt_t               shm_nattch;
    time_t                   shm_atime;
    time_t                   shm_dtime;
    time_t                   shm_ctime;
    .
};

#include <sys/shm.h>
int shmget (key_t  key, size_t size, int flag);
     成功q回׃n存储IDQ错误返?1
int shmctl (int shmid, int cmd, struct shmid_ds,*buf);
     cmd?IPC_STAT, IPC_SET, IPC_RMID, SHM_LOCK, SHM_UNLOCK

q接到地址I间
void *shmat (int shmid ,const void *addr, int flag);

对共享内存操作结束时Q脱该D?br />int shmdt (void *addr);

要运行程序,需要在当前目录下徏立一个share文gQshare是一个空文gQ没有Q何意义,只是函数ftok需要一个文件名作参敎ͼftok另一个参数可以ؓM数字?/p>

E序q行后,分ؓ父子q程Q子q程甌׃n内存Q然后等待父q程l箋执行Q父q程首先{待子进E申请到׃n内存标识Q然后输出共享内存中的内容,Z演示׃n内存可以随时更新Q程序中在子q程里生随机数写入׃n内存供父q程d?br />代码如下Q?/p>

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

#include<time.h>
#include<signal.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>

int shmID;
char * buf;
 
void finalize(int signo)
{
 shmctl(shmID,IPC_RMID,NULL);
 
 exit(0);
}

int main()
{
 int i = 1;  
 key_t shmKey;
 
 signal(SIGINT,finalize);
 signal(SIGTERM,finalize);
 
 if(fork() == 0) //子进E?br /> {  
  shmKey = ftok("share",16);  //可以使用M大于0的数字,如果名字和数相同Q则产生的key相同?br />  if(shmKey == -1)
  {
   printf("创徏key出错\n");
   exit(-1);
  }
  
  shmID = shmget(shmKey,20,IPC_CREAT | IPC_EXCL | 0666);
  if(shmID == -1)
  {
   printf("创徏׃n标识出错\n");
   exit(-1);
  }
  
  sleep(2); //{待父进E执行,好显C第一行ؓI?br />  while(1)
  {
   buf = (char *)shmat(shmID,NULL,0);
   srandom(time(NULL));
   sprintf(buf,"%d",random()); 
   shmdt(buf);  
  }
 }
 else  //父进E?br /> {
  sleep(1); //让子q程先执行,以徏立内存映?br />  
  shmKey = ftok("share",16);  //可以使用M大于0的数字,如果名字和数相同Q则产生的key相同?br />  if(shmKey == -1)
  {
   printf("创徏key出错\n");
   exit(-1);
  }
  
  shmID = shmget(shmKey,20,0); //0表示如果shmKey映射的不存在则报错?br />  if(shmID == -1)
  {
   printf("引用׃n标识出错\n");
   exit(-1);
  }
  
  while(1)
  { 
   buf = (char *)shmat(shmID,NULL,0);
   printf("%d. 现在׃n内存中的内容为:%s\n",i++,buf);
   shmdt(buf);
   sleep(1);
  }
 } 

 return 0;
}


 



allic 2008-04-04 23:55 发表评论
]]>
linux 消息队列~程http://www.tkk7.com/huyi2006/articles/190843.htmlallicallicFri, 04 Apr 2008 15:39:00 GMThttp://www.tkk7.com/huyi2006/articles/190843.html函数原型

#include <sys/msg/h>
int msgget (key_t key, int flag)
int msgctl (int msgid, int cmd , sruct msgid_ds *buf);
int msgsnd (int msgid, const void *ptr, size_t nbyes, long type, int flag);
int msgrcv (int msgid, void *ptr, size_t nbytes, long type, int flag);

文gmsg为空文gQ可以ؓM内容Q这里只是ؓ了ftok函数使用。程序通过建立消息队列Q完成进E间通信Q注意msgrcv的第四个参数为消息类型,他定义了从队列中取消息的cd?/p>


下面是msgLucy.c,是徏立消息队列的

#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#include<sys/types.h>

#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int mqid;

void terminate_handler(int signo)
{
 msgctl(mqid,IPC_RMID,NULL);
 exit(0);
}

int main()
{
 char filenm[] = "msg";
 key_t mqkey;
 struct msgbuf
 {
      long mtype;      /* message type, must be > 0 */
    char mtext[256];  /* message data */
   }msg;
 int ret;

 mqkey = ftok(filenm,PROJID);
 if(mqkey == -1)
 {
  perror("ftok error: ");
  exit(-1);
 }

 mqid = msgget(mqkey,IPC_CREAT | IPC_EXCL | 0666);
 if(mqid == -1)
 {
  perror("msgget error: ");
  exit(-1);
 }

 signal(SIGINT, terminate_handler);
 signal(SIGTERM, terminate_handler);

 while(1)
 {
  printf("Lucy: ");
  fgets(msg.mtext, 256, stdin);
  if (strncmp("quit", msg.mtext, 4) == 0)
  {
   msgctl(mqid,IPC_RMID,NULL);
   exit(0);
  }
  msg.mtext[strlen(msg.mtext)-1] = '\0';
  msg.mtype = LUCY;
  msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
  msgrcv(mqid,&msg,256,PETER,0);
  printf("Peter: %s\n", msg.mtext);  
 }  
}

下面的是msgPeter,是和Lucy通信?E序先运行Lucy,再运行Peter

#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#include<sys/types.h>

#include<stdio.h>
#include<fcntl.h>
#include<signal.h>
#include<string.h>
#include<stdlib.h>

#define PROJID 0xFF
#define LUCY 1
#define PETER 2

int main()
{
 char filenm[] = "msg";
 int mqid;
 key_t mqkey;
 struct msgbuf
 {
         long mtype;      /* message type, must be > 0 */
         char mtext[256];  /* message data */
   }msg;
 int ret;

 mqkey = ftok(filenm, PROJID);
 if(mqkey == -1)
 {
  perror("ftok error: ");
  exit(-1);
 }

 mqid = msgget(mqkey, 0);
 if(mqid == -1)
 {
  perror("msgget error: ");
  exit(-1);
 }

 while(1)
 {
  msgrcv(mqid,&msg,256,LUCY,0);
  printf("Lucy: %s\n",msg.mtext);
  printf("Peter: ");
  fgets(msg.mtext,256,stdin);
  if(strncmp("quit", msg.mtext, 4) == 0)
  {
   exit(0);
  }
  msg.mtext[strlen(msg.mtext)-1] = '\0';
  msg.mtype = PETER;
  msgsnd(mqid,&msg,strlen(msg.mtext) + 1,0);
 } 
}



allic 2008-04-04 23:39 发表评论
]]>
Linux信号一?http://www.tkk7.com/huyi2006/articles/188298.htmlallicallicMon, 24 Mar 2008 09:17:00 GMThttp://www.tkk7.com/huyi2006/articles/188298.htmlhttp://www.tkk7.com/huyi2006/comments/188298.htmlhttp://www.tkk7.com/huyi2006/articles/188298.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/188298.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/188298.html
SIGHUP           l止q程                        l端U\挂断
SIGINT             l止q程                        中断q程
SIGQUIT          建立CORE文g             l止q程Qƈ且生成core文g
SIGILL              建立CORE文g            非法指o
SIGTRAP         建立CORE文g             跟踪自陷
SIGBUS           建立CORE文g             ȝ错误
SIGSEGV         建立CORE文g             D非法错?
SIGFPE            建立CORE文g             点异常
SIGIOT             建立CORE文g             执行I/O自陷
SIGKILL            l止q程                        杀死进E?
SIGPIPE           l止q程                        向一个没有读q程的管道写数据
SIGALARM       l止q程                        计时器到?
SIGTERM         l止q程                        软gl止信号
SIGSTOP         停止q程                         l端来的停止信号
SIGCONT         忽略信号                        l箋执行一个停止的q程
SIGURG           忽略信号                        I/O紧急信?
SIGIO                忽略信号                        描述W上可以q行I/O
SIGCHLD         忽略信号                         当子q程停止或退出时通知父进E?
SIGTTOU          停止q程                        后台q程写终?
SIGTTIN            停止q程                        后台q程ȝ?
SIGXGPU          l止q程                        CPU旉时
SIGXFSZ           l止q程                         文g长度q长
SIGWINCH       忽略信号                         H口大小发生变化
SIGPROF          l止q程                         l计分布囄计时器到?
SIGUSR1          l止q程                         用户定义信号1
SIGUSR2          l止q程                         用户定义信号2
SIGVTALRM     l止q程                          虚拟计时器到?img src ="http://www.tkk7.com/huyi2006/aggbug/188298.html" width = "1" height = "1" />

allic 2008-03-24 17:17 发表评论
]]>
linux 信号处理机制http://www.tkk7.com/huyi2006/articles/188278.htmlallicallicMon, 24 Mar 2008 08:31:00 GMThttp://www.tkk7.com/huyi2006/articles/188278.htmlhttp://www.tkk7.com/huyi2006/comments/188278.htmlhttp://www.tkk7.com/huyi2006/articles/188278.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/188278.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/188278.htmlalarmQ设|信号传送闹钟)  
相关函数   signalQsleep

表头文g   #include<unistd.h>

定义函数   unsigned int alarm(unsigned int seconds);

函数说明   alarm()用来讄信号SIGALRM在经q参数seconds指定的秒数后传送给目前的进E。如果参数seconds ?Q则之前讄的闹钟会被取消,q将剩下的时间返回?br />
q回值 ?q回之前闚w的剩余秒敎ͼ如果之前未设闚w则返??br />
范例   #include<unistd.h>
#include<signal.h>
void handler() {
printf(“hello\n?;
}
main()
{
int i;
signal(SIGALRM,handler);
alarm(5);
for(i=1;i<7;i++){
printf(“sleep %d ...\n?i);
sleep(1);
}
}

执行   sleep 1 ...
sleep 2 ...
sleep 3 ...
sleep 4 ...
sleep 5 ...
hello
sleep 6 ...

 


killQ传送信L指定的进E)  
相关函数   raiseQsignal

表头文g   #include<sys/types.h>
#include<signal.h>

定义函数   int kill(pid_t pid,int sig);

函数说明   kill()可以用来送参数sig指定的信L参数pid指定的进E。参数pid有几U情?
pid>0 信号传l进E识别码为pid 的进E?br />pid=0 信号传l和目前q程相同q程l的所有进E?br />pid=-1 信号广播传送给pȝ内所有的q程
pid<0 信号传l进E组识别码ؓpidl对值的所有进E?br />参数sig代表的信L号可参考附录D

q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EINVAL 参数sig 不合?br />ESRCH 参数pid 所指定的进E或q程l不存在
EPERM 权限不够无法传送信L指定q程

范例   #include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
main()
{
pid_t pid;
int status;
if(!(pid= fork())){
printf(“Hi I am child process!\n?;
sleep(10);
return;
}
else{
printf(“send signal to child process (%d) \n?pid);
sleep(1);
kill(pid ,SIGABRT);
wait(&status);
if(WIFSIGNALED(status))
printf(“chile process receive signal %d\n?WTERMSIG(status));
}
}

执行   sen signal to child process(3170)
Hi I am child process!
child process receive signal 6

 


pauseQ让q程暂停直到信号出现Q ?br />相关函数   killQsignalQsleep

表头文g   #include<unistd.h>

定义函数   int pause(void);

函数说明   pause()会o目前的进E暂停(q入睡眠状态)Q直到被信号(signal)所中断?br />
q回值 ?只返?1?br />
错误代码   EINTR 有信号到达中断了此函数?br />
 


sigactionQ查询或讄信号处理方式Q ?br />相关函数   signalQsigprocmaskQsigpendingQsigsuspend

表头文g   #include<signal.h>

定义函数   int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);

函数说明   sigaction()会依参数signum指定的信Lh讄该信L处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信受?br />如参数结构sigaction定义如下
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
sa_handler此参数和signal()的参数handler相同Q代表新的信号处理函敎ͼ其他意义请参考signal()?br />sa_mask 用来讄在处理该信号时暂时将sa_mask 指定的信h|?br />sa_restorer 此参数没有用?br />sa_flags 用来讄信号处理的其他相x作,下列的数值可用?br />OR q算Q|Q组?br />A_NOCLDSTOP : 如果参数signum为SIGCHLDQ则当子q程暂停时ƈ不会通知父进E?br />SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前Q将此信号处理方式改为系l预讄方式?br />SA_RESTART:被信号中断的pȝ调用会自行重?br />SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再ơ到来?br />如果参数oldact不是NULL指针Q则原来的信号处理方式会由此l构sigaction q回?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EINVAL 参数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号
EFAULT 参数actQoldact指针地址无法存取?br />EINTR 此调用被中断

范例   #include<unistd.h>
#include<signal.h>
void show_handler(struct sigaction * act)
{
switch (act->sa_flags)
{
case SIG_DFL:printf(“Default action\n?;break;
case SIG_IGN:printf(“Ignore the signal\n?;break;
default: printf(?x%x\n?act->sa_handler);
}
}
main()
{
int i;
struct sigaction act,oldact;
act.sa_handler = show_handler;
act.sa_flags = SA_ONESHOT|SA_NOMASK;
sigaction(SIGUSR1,&act,&oldact);
for(i=5;i<15;i++)
{
printf(“sa_handler of signal %2d =?i);
sigaction(i,NULL,&oldact);
}
}

执行   sa_handler of signal 5 = Default action
sa_handler of signal 6= Default action
sa_handler of signal 7 = Default action
sa_handler of signal 8 = Default action
sa_handler of signal 9 = Default action
sa_handler of signal 10 = 0x8048400
sa_handler of signal 11 = Default action
sa_handler of signal 12 = Default action
sa_handler of signal 13 = Default action
sa_handler of signal 14 = Default action

 


sigaddsetQ增加一个信可信号集)  
相关函数   sigemptysetQsigfillsetQsigdelsetQsigismember

表头文g   #include<signal.h>

定义函数   int sigaddset(sigset_t *set,int signum);

函数说明   sigaddset()用来参数signum 代表的信号加入至参数set 信号集里?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EFAULT 参数set指针地址无法存取
EINVAL 参数signum非合法的信号~号

 


sigdelsetQ从信号集里删除一个信P  
相关函数   sigemptysetQsigfillsetQsigaddsetQsigismember

表头文g   #include<signal.h>

定义函数   int sigdelset(sigset_t * set,int signum);

函数说明   sigdelset()用来参数signum代表的信号从参数set信号集里删除?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EFAULT 参数set指针地址无法存取
EINVAL 参数signum非合法的信号~号

 


sigemptysetQ初始化信号集)  
相关函数   sigaddsetQsigfillsetQsigdelsetQsigismember

表头文g   #include<signal.h>

定义函数   int sigemptyset(sigset_t *set);

函数说明   sigemptyset()用来参数set信号集初始化q清I?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EFAULT 参数set指针地址无法存取

 


sigfillsetQ将所有信号加入至信号集)  
相关函数   sigemptyQsigaddsetQsigdelsetQsigismember

表头文g   #include<signal.h>

定义函数   int sigfillset(sigset_t * set);

函数说明   sigfillset()用来参数set信号集初始化Q然后把所有的信号加入到此信号集里?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
附加说明   EFAULT 参数set指针地址无法存取

 


sigismemberQ测试某个信h否已加入至信号集里)  
相关函数   sigemptysetQsigfillsetQsigaddsetQsigdelset

表头文g   #include<signal.h>

定义函数   int sigismember(const sigset_t *set,int signum);

函数说明   sigismember()用来试参数signum 代表的信h否已加入臛_数set信号集里。如果信号集里已有该信号则返?Q否则返??br />
q回值 ?信号集已有该信号则返?Q没有则q回0。如果有错误则返?1?br />
错误代码   EFAULT 参数set指针地址无法存取
EINVAL 参数signum 非合法的信号~号

 


signalQ设|信号处理方式)  
相关函数   sigactionQkillQraise

表头文g   #include<signal.h>

定义函数   void (*signal(int signum,void(* handler)(int)))(int);

函数说明   signal()会依参数signum 指定的信Lh讄该信L处理函数。当指定的信号到达时׃跌{到参数handler指定的函数执行。如果参数handler不是函数指针Q则必须是下列两个常C一:
SIG_IGN 忽略参数signum指定的信受?br />SIG_DFL 参数signum 指定的信号重设ؓ核心预设的信号处理方式?br />关于信号的编号和说明Q请参考附录D

q回值 ?q回先前的信号处理函数指针,如果有错误则q回SIG_ERR(-1)?br />
附加说明   在信号发生蟩转到自定的handler处理函数执行后,pȝ会自动将此处理函数换回原来系l预讄处理方式Q如果要改变此操作请改用sigaction()?br />
范例   参考alarm()或raise()?br />
 


sigpendingQ查询被搁置的信P  
相关函数   signalQsigactionQsigprocmaskQsigsuspend

表头文g   #include<signal.h>

定义函数   int sigpending(sigset_t *set);

函数说明   sigpending()会将被搁|的信号集合由参数set指针q回?br />
q回值执   行成功则q回0Q如果有错误则返?1?br />
错误代码   EFAULT 参数set指针地址无法存取
EINTR 此调用被中断?br />
 


sigprocmaskQ查询或讄信号遮罩Q ?br />相关函数   signalQsigactionQsigpendingQsigsuspend

表头文g   #include<signal.h>

定义函数   int sigprocmask(int how,const sigset_t *set,sigset_t * oldset);

函数说明   sigprocmask()可以用来改变目前的信号遮|,其操作依参数how来决?br />SIG_BLOCK 新的信号遮罩q前的信号遮罩和参数set 指定的信号遮|作联集
SIG_UNBLOCK 目前的信号遮罩删除掉参数set指定的信号遮|?br />SIG_SETMASK 目前的信号遮罩设成参数set指定的信号遮|?br />如果参数oldset不是NULL指针Q那么目前的信号遮罩会由此指针返回?br />
q回值 ?执行成功则返?Q如果有错误则返?1?br />
错误代码   EFAULT 参数setQoldset指针地址无法存取?br />EINTR 此调用被中断

 


sleepQ让q程暂停执行一D|_  
相关函数   signalQalarm

表头文g   #include<unistd.h>

定义函数   unsigned int sleep(unsigned int seconds);

函数说明   sleep()会o目前的进E暂停,直到辑ֈ参数seconds 所指定的时_或是被信h中断?br />
q回值 ?若进E暂停到参数seconds 所指定的时间则q回0Q若有信号中断则q回剩余U数?br />
 


ferrorQ检查文件流是否有错误发生)  
相关函数   clearerrQperror

表头文g   #include<stdio.h>

定义函数   int ferror(FILE *stream);

函数说明   ferror()用来查参数stream所指定的文件流是否发生了错误情况,如有错误发生则返回非0倹{?br />
q回值 ?如果文g有错误发生则返回非0倹{?br />
 


perrorQ打印出错误原因信息字符Ԍ  
相关函数   strerror

表头文g   #include<stdio.h>

定义函数   void perror(const char *s);

函数说明   perror()用来上一个函数发生错误的原因输出到标准错?stderr)。参数s所指的字符串会先打印出Q后面再加上错误原因字符丌Ӏ此错误原因依照全局变量errno的值来军_要输出的字符丌Ӏ?br />
q回值 ?br />
范例   #include<stdio.h>
main()
{
FILE *fp;
fp = fopen(?tmp/noexist?”r+?;
if(fp = =NULL) perror(“fopen?;
}

执行   $ ./perror
fopen : No such file or diretory

 


strerrorQ返回错误原因的描述字符Ԍ  
相关函数   perror

表头文g   #include<string.h>

定义函数   char * strerror(int errnum);

函数说明   strerror()用来依参数errnum的错误代码来查询光误原因的描述字符Ԍ然后该字符串指针返回?br />
q回值 ?q回描述错误原因的字W串指针?br />
范例   /* 昄错误代码0 ? 的错误原因描q?/
#include<string.h>
main()
{
int i;
for(i=0;i<10;i++)
printf(?d : %s\n?i,strerror(i));
}

执行   0 : Success
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input/output error
6 : Device not configured
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor

 


mkfifoQ徏立具名管道)  
相关函数   pipeQpopenQopenQumask

表头文g   #include<sys/types.h>
#include<sys/stat.h>

定义函数   int mkfifo(const char * pathname,mode_t mode);

函数说明   mkfifo()会依参数pathname建立Ҏ的FIFO文gQ该文g必须不存在,而参数mode文g的权限(mode%~umaskQ,因此umaskg会媄响到FIFO文g的权限。Mkfifo()建立的FIFO文g其他q程都可以用d一般文件的方式存取。当使用open()来打开FIFO文gӞO_NONBLOCK旗标会有影响
1、当使用O_NONBLOCK 旗标Ӟ打开FIFO 文g来读取的操作会立刻返回,但是若还没有其他q程打开FIFO 文g来读取,则写入的操作会返回ENXIO 错误代码?br />2、没有用O_NONBLOCK 旗标Ӟ打开FIFO 来读取的操作会等到其他进E打开FIFO文g来写入才正常q回。同样地Q打开FIFO文g来写入的操作会等到其他进E打开FIFO 文g来读取后才正常返回?br />
q回值 ?若成功则q回0Q否则返?1Q错误原因存于errno中?br />
错误代码   EACCESS 参数pathname所指定的目录\径无可执行的权限
EEXIST 参数pathname所指定的文件已存在?br />ENAMETOOLONG 参数pathname的\径名U太ѝ?br />ENOENT 参数pathname包含的目录不存在
ENOSPC 文gpȝ的剩余空间不?br />ENOTDIR 参数pathname路径中的目录存在但却非真正的目录?br />EROFS 参数pathname指定的文件存在于只读文gpȝ内?br />
范例   #include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
main()
{
char buffer[80];
int fd;
unlink(FIFO);
mkfifo(FIFO,0666);
if(fork()>0){
char s[ ] = “hello!\n?
fd = open (FIFO,O_WRONLY);
write(fd,s,sizeof(s));
close(fd);
}
else{
fd= open(FIFO,O_RDONLY);
read(fd,buffer,80);
printf(?s?buffer);
close(fd);
}
}

执行   hello!

 


pcloseQ关闭管道I/OQ ?br />相关函数   popen

表头文g   #include<stdio.h>

定义函数   int pclose(FILE * stream);

函数说明   pclose()用来关闭由popen所建立的管道及文g指针。参数stream为先前由popen()所q回的文件指针?br />
q回值 ?q回子进E的l束状态。如果有错误则返?1Q错误原因存于errno中?br />
错误代码   ECHILD pclose()无法取得子进E的l束状态?br />
范例   参考popen()?br />
 


pipeQ徏立管道)  
相关函数   mkfifoQpopenQreadQwriteQfork

表头文g   #include<unistd.h>

定义函数   int pipe(int filedes[2]);

函数说明   pipe()会徏立管道,q将文g描述词由参数filedes数组q回。filedes[0]为管道里的读取端Qfiledes[1]则ؓ道的写入端?br />
q回值 ?若成功则q回Ӟ否则q回-1Q错误原因存于errno中?br />
错误代码   EMFILE q程已用完文件描q词最大量?br />ENFILE pȝ已无文g描述词可用?br />EFAULT 参数filedes数组地址不合法?br />
范例   /* 父进E借管道将字符东yhello!\n”传l子q程q显C?/
#include <unistd.h>
main()
{
int filedes[2];
char buffer[80];
pipe(filedes);
if(fork()>0){
/* 父进E?/
char s[ ] = “hello!\n?
write(filedes[1],s,sizeof(s));
}
else{
/*子进E?/
read(filedes[0],buffer,80);
printf(?s?buffer);
}
}

执行   hello!

 


popenQ徏立管道I/OQ ?br />相关函数   pipeQmkfifoQpcloseQforkQsystemQfopen

表头文g   #include<stdio.h>

定义函数   FILE * popen( const char * command,const char * type);

函数说明   popen()会调用fork()产生子进E,然后从子q程中调?bin/sh -c来执行参数command的指令。参数type可用“r”代表读取,“w”代表写入。依照此type|popen()会徏立管道连到子q程的标准输备或标准输入讑֤Q然后返回一个文件指针。随后进E便可利用此文g指针来读取子q程的输备或是写入到子进E的标准输入讑֤中。此外,所有用文件指?FILE*)操作的函C都可以用,除了fclose()以外?br />
q回值 ?若成功则q回文g指针Q否则返回NULLQ错误原因存于errno中?br />
错误代码   EINVAL参数type不合法?br />
注意事项   在编写具SUID/SGID权限的程序时请尽量避免用popen()Qpopen()会承环境变量,通过环境变量可能会造成pȝ安全的问题?br />
范例   #include<stdio.h>
main()
{
FILE * fp;
char buffer[80];
fp=popen(“cat /etc/passwd?”r?;
fgets(buffer,sizeof(buffer),fp);
printf(?s?buffer);
pclose(fp);
}



allic 2008-03-24 16:31 发表评论
]]>
Linux下automake软g~译与发布之多目录l构的处?/title><link>http://www.tkk7.com/huyi2006/articles/187908.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 22 Mar 2008 09:20:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/187908.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/187908.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/187908.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/187908.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/187908.html</trackback:ping><description><![CDATA[ <div id="6116111" class="cnt" id="blog_text">在入门篇我们单介l了使用automake自动产生makefile的几个关键步骤,所有文仉在同一个目录下。但在比较大的项目中Q很将所有文件放在一个目录下的。本文针对这U情况做个简单介l?br />    <br />    多目录l构的YӞ一般是单个E序、库文g或模块放在各自的目录中。automake要求每个目录都有自己的Makefile.am文g来编译各自目?下的代码。在的目录中Q有一个Makefile.am文gQ该文g通过SUBDIRS指明了这个目录下有多个直接下目录的代码需要编译。下U目?的Makefile.am也指明自己需要编译的下目录。通过q样的层层递归iQ从而完成多U目录结构的~译?br />    <br />    下面看一个具体的CZQ?br />    <br />    本例假设目录l构如下Q?br />    helloworld    <br />    |src<br />    ||--test.cpp<br />    ||dohello   <br />    |||--dohello.h<br />    |||--dohello.cpp <br />    |doc<br />    ||--userguide<br />    目录helloworldQ该目录下有src和doc两个目录。src目录下有一个test.cpp文gQ一个dohello目录Qdohello目录下又有dohello.h和dohello.cpp两个文g。doc下有一个readme文g?br />    整个E序中test.cpp是主文gQ里面有main函数。test.cpp调用了dohello.h和dohello.cpp中的cR?br />    <br />    下面是编译步骤:<br />    <br /><strong><font color="#0000ff">    1.q行autoscan产生configure.scan</font></strong><br />    <br />    在顶U目录helloworld下运行autoscan命o。生configure.scan文g内容如下Q?br /><font color="#800000">#                -*- Autoconf -*-<br /># Process this file with autoconf to produce a configure script.</font><p><font color="#800000">AC_PREREQ(2.61)<br />AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)<br />AC_CONFIG_SRCDIR([src/test.cpp])<br />AC_CONFIG_HEADER([config.h])</font></p><p><font color="#800000"># Checks for programs.<br />AC_PROG_CXX<br />AC_PROG_CC</font></p><p><font color="#800000"># Checks for libraries.</font></p><p><font color="#800000"># Checks for header files.</font></p><p><font color="#800000"># Checks for typedefs, structures, and compiler characteristics.</font></p><p><font color="#800000"># Checks for library functions.</font></p><p><font color="#800000">AC_CONFIG_FILES([Makefile<br />                 src/Makefile<br />                 src/dohello/Makefile])                       <br />AC_OUTPUT<br /></font>    <br />    <br /><strong><font color="#0000ff">    2.~辑configure.in</font></strong><br />    <br />    configure.scan改名成configure.inQ然后修改如下:<br /><font color="#800000"># Process this file with autoconf to produce a configure script.</font></p><p><font color="#800000">AC_PREREQ(2.61)<br />AC_INIT(hello,1.0)   #软g包名U和版本?另一U写法是写在AM_INIT_AUTOMAKE?<br />AC_CONFIG_SRCDIR([src/test.cpp])<br />#AC_CONFIG_HEADER([config.h])<br />AM_INIT_AUTOMAKE</font></p><p><font color="#800000"># Checks for programs.<br />AC_PROG_CXX<br />AC_PROG_CC<br />AC_PROG_RANLIB       #使用了静态库~译,需要此宏定?/font></p><p><font color="#800000"># Checks for libraries.</font></p><p><font color="#800000"># Checks for header files.</font></p><p><font color="#800000"># Checks for typedefs, structures, and compiler characteristics.</font></p><p><font color="#800000"># Checks for library functions.</font></p><p><font color="#800000">#AC_CONFIG_FILES([src/Makefile src/dohello/Makefile])<br />AC_OUTPUT(Makefile src/Makefile src/dohello/Makefile) #需要生成的MakefileQ本例需要生成三个?br /></font>    <br /><strong><font color="#0000ff">    3.q行aclocal和autoconf</font></strong><br />    <br />    configure.in修改完后则先后执行aclocal和autoconf命o?br />    <br /><strong><font color="#0000ff">    4.建立各个目录的Makefile.am文g</font></strong><br />    <br />    在顶U目录、src目录和dohello目录下分别徏立三个Makefile.am文g。内容分别如下:<br />    <br />    目录Makefile.amQ?br /><font color="#800000">#----------------开?-----------------------------------------     <br />AUTOMAKE_OPTIONS=foreign<br />SUBDIRS=src   #本目录的直接下目录src需要编?br />EXTRA_DIST=doc/userguide #doc/userguide不需要编译,但要发布该文件。如果有多个文gQ则用空格分开?br />#----------------l束------------------------------------------</font><br />    <br />    src目录下的Makefile.amQ?br /><font color="#800000">#----------------开?----------------------------------------- <br />AUTOMAKE_OPTIONS=foreign<br />SUBDIRS=dohello #本目录的直接下目录dohello需要编?br />bin_PROGRAMS=hello #本目录的文g~译成可执行文ghello。如有多个,用空格分开。然后在下面分别写它们的SOURCE和LDADD?br />hello_SOURCES=test.cpp #~译hello需要的源文件列表,如有多个Q用I格分开?br />hello_LDADD=dohello/libhello.a #~译hello需要的库文件列表。如有多个,用空格分开?br />#----------------l束------------------------------------------</font><br />    <br />    dohello目录下的Makefile.am<br /><font color="#800000">#----------------开?-----------------------------------------    <br />AUTOMAKE_OPTIONS=foreign<br />noinst_LIBRARIES=libhello.a   #本目录下的代码编译成libhello.a库。不需要发布。如果需要发布,则写成bin_LIBRARIES。注意,库的名称格式必需?libxxx.a。因为编译静态库Qconfigure.in需要定义AC_PROG_RANLIB宏?br />libhello_a_SOURCES=dohello.h dohello.cpp #~译libhello.a需要的源文件。注意将库名UC?.'h?_'受?<br />#----------------l束------------------------------------------</font><br />    <br /><strong><font color="#0000ff">    5.q行automake</font></strong><br />    <br />    以上几个Makefile.am都书写完毕后Q运行automake --add-missing?br />    <br /><strong><font color="#0000ff">    6.q行./configure和make</font></strong><br />    <br />    上面步骤完成后,先后q行./configure和make完成~译。如果编译成功,q行make dist可以所有文件打包成hello-1.0.tar.gz?/p></div> <br /> <img src ="http://www.tkk7.com/huyi2006/aggbug/187908.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2008-03-22 17:20 <a href="http://www.tkk7.com/huyi2006/articles/187908.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下Makefile的automake生成全攻?/title><link>http://www.tkk7.com/huyi2006/articles/187907.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 22 Mar 2008 09:19:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/187907.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/187907.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/187907.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/187907.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/187907.html</trackback:ping><description><![CDATA[作ؓLinux下的E序开发h员,大家一定都遇到qMakefileQ用make命o来编译自己写的程序确实是很方ѝ一般情况下Q大安是手工写一个简单MakefileQ如果要惛_Z个符合自pY件惯例的Makefile׃那么Ҏ了? <p>  在本文中Q将l大家介l如何用autoconf和automake两个工具来帮助我们自动地生成W合自由软g惯例的MakefileQ这样就 可以象常见的GNUE序一P只要使用?/configure”,“make”,“make instal”就可以把程序安装到Linuxpȝ中去了。这特别适合惛_开放源代码软g的程序开发h员,又或如果你只是自己写些小的ToyE序Q那么这 个文章对你也会有很大的帮助?/p><p>  一、Makefile介绍</p><p>  Makefile是用于自动编译和链接的,一个工E有很多文gl成Q每一个文件的改变都会D工程的重新链接,但是不是所有的文g都需要重新编译,Makefile中纪录有文g的信息,在make时会军_在链接的时候需要重新编译哪些文件?/p><p>  Makefile的宗旨就是:让编译器知道要编译一个文仉要依赖其他的哪些文g。当那些依赖文g有了改变Q编译器会自动的发现最l的生成文g已经q时Q而重新编译相应的模块?/p><p>  Makefile的基本结构不是很复杂Q但当一个程序开发h员开始写MakefileӞl常会怀疑自己写的是否符合惯例,而且自己写的 Makefilel常和自q开发环境相兌Q当pȝ环境变量或\径发生了变化后,Makefile可能q要跟着修改。这样就造成了手工书?Makefile的诸多问题,automake恰好能很好地帮助我们解决q些问题?/p><p>  使用automakeQ程序开发h员只需要写一些简单的含有预定义宏的文Ӟ由autoconfҎ一个宏文g生成configureQ由 automakeҎ另一个宏文g生成Makefile.inQ再使用configure依据Makefile.in来生成一个符合惯例的 Makefile。下面我们将详细介绍Makefile的automake生成Ҏ?/p><p>  二、用的环境</p><p>  本文所提到的程序是ZLinux发行版本QFedora Core release 1Q它包含了我们要用到的autoconfQautomake?/p><p>  三、从helloworld入手</p><p>  我们从大家最怋用的例子E序helloworld开始?/p><p>  下面的过E如果简单地说来是Q?/p><p>  新徏三个文gQ?/p><p>   helloworld.c<br />configure.in<br />Makefile.am</p><p>  然后执行Q?br />   aclocal<br />   autoconf<br />   automake --add-missing<br />   ./configure<br />   make<br />   ./helloworld</p><p>  可以看到Makefile被生出来,而且可以helloworld.c~译通过?/p><p>  很简单吧Q几条命令就可以做出一个符合惯例的MakefileQ感觉如何呀?/p><p>  现在开始介l详l的q程Q?/p><p>  1、徏目录</p><p>  在你的工作目录下Z个helloworld目录Q我们用它来存放helloworldE序及相xӞ如在/home/my/build下:</p><p>$ mkdir helloword<br />$ cd helloworld</p><p>  2?helloworld.c</p><p>  然后用你自己最喜欢的编辑器写一个hellowrold.c文gQ如命oQvi helloworld.c。用下面的代码作ؓhelloworld.c的内宏V?/p><p>int main(int argc, char** argv)<br />{<br />printf("Hello, Linux World!\n");<br />return 0;<br />}</p><p>  完成后保存退出?/p><p>  现在在helloworld目录下就应该有一个你自己写的helloworld.c了?/p><p>  3、生成configure</p><p>  我们使用autoscan命o来帮助我们根据目录下的源代码生成一个configure.in的模板文件?/p><p>  命oQ?/p><p>$ autoscan<br />$ ls<br />configure.scan helloworld.c</p><p>  执行后在hellowrold目录下会生成一个文Ӟconfigure.scanQ我们可以拿它作为configure.in的蓝本?/p><p>  现在configure.scan改名为configure.inQƈ且编辑它Q按下面的内容修改,L无关的语句:</p><p>============================configure.in内容开?========================================<br /># -*- Autoconf -*-<br /># Process this file with autoconf to produce a configure script.</p><p>AC_INIT(helloworld.c)<br />AM_INIT_AUTOMAKE(helloworld, 1.0)</p><p># Checks for programs.<br />AC_PROG_CC</p><p># Checks for libraries.</p><p># Checks for header files.</p><p># Checks for typedefs, structures, and compiler characteristics.</p><p># Checks for library functions.<br />AC_OUTPUT(Makefile)<br />============================configure.in内容l束=========================================</p><p>  然后执行命oaclocal和autoconfQ分别会产生aclocal.m4及configure两个文gQ?/p><p>$ aclocal <br />$ls <br />aclocal.m4 configure.in helloworld.c <br />$ autoconf <br />$ ls <br />aclocal.m4 autom4te.cache configure configure.in helloworld.c</p><p><br />大家可以看到configure.in内容是一些宏定义Q这些宏lautoconf处理后会变成查系l特性、环境变量、Y件必ȝ参数的shell脚本?/p><p>  autoconf 是用来生成自动配|Y件源代码脚本QconfigureQ的工具。configure脚本能独立于autoconfq行Q且在运行的q程中,不需要用Lq预?/p><p>  要生成configure文gQ你必须告诉autoconf如何扑ֈ你所用的宏。方式是使用aclocalE序来生成你的aclocal.m4?/p><p>  aclocalҎconfigure.in文g的内容,自动生成aclocal.m4文g。aclocal是一个perl 脚本E序Q它的定义是Q“aclocal - create aclocal.m4 by scanning configure.ac”?/p><p>  autoconf从configure.inq个列D~译软g时所需要各U参数的模板文g中创建configure?/p><p>  autoconf需要GNU m4宏处理器来处理aclocal.m4Q生成configure脚本?/p><p>  m4是一个宏处理器。将输入拯到输出,同时宏展开。宏可以是内嵌的Q也可以是用户定义的。除了可以展开宏,m4q有一些内建的函数Q用来引用文Ӟ执行命oQ整数运,文本操作Q@环等。m4既可以作为编译器的前端,也可以单独作Z个宏处理器?br />  <br />   4、新建Makefile.am</p><p>  新徏Makefile.am文gQ命令:<br />$ vi Makefile.am <br />内容如下:<br />AUTOMAKE_OPTIONS=foreign<br />bin_PROGRAMS=helloworld<br />helloworld_SOURCES=helloworld.c <br />automake会根据你写的Makefile.am来自动生成Makefile.in?/p><p>  Makefile.am中定义的宏和目标,会指导automake生成指定的代码。例如,宏bin_PROGRAMS导致编译和q接的目标被生成?/p><p>  5、运行automake<br />命oQ?br />$ automake --add-missing<br />configure.in: installing `./install-sh'<br />configure.in: installing `./mkinstalldirs'<br />configure.in: installing `./missing'<br />Makefile.am: installing `./depcomp'</p><p>  automake会根据Makefile.am文g产生一些文Ӟ包含最重要的Makefile.in?/p><p>  6、执行configure生成Makefile</p><p>$ ./configure <br />checking for a BSD-compatible install... /usr/bin/install -c<br />checking whether build environment is sane... yes<br />checking for gawk... gawk<br />checking whether make sets $(MAKE)... yes<br />checking for gcc... gcc<br />checking for C compiler default output... a.out<br />checking whether the C compiler works... yes<br />checking whether we are cross compiling... no<br />checking for suffix of executables... <br />checking for suffix of object files... o<br />checking whether we are using the GNU C compiler... yes<br />checking whether gcc accepts -g... yes<br />checking for gcc option to accept ANSI C... none needed<br />checking for style of include used by make... GNU<br />checking dependency style of gcc... gcc3<br />configure: creating ./config.status<br />config.status: creating Makefile<br />config.status: executing depfiles commands<br />$ ls -l Makefile<br />-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile</p><p>  你可以看刎ͼ此时Makefile已经产生出来了?/p><p>  7、用Makefile~译代码<br />$ make<br />if gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -</p><p>DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="helloworld" -DVERSION="1.0"</p><p>-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" \<br />-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; \<br />then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; \<br />else rm -f ".deps/helloworld.Tpo"; exit 1; \<br />fi<br />gcc -g -O2 -o helloworld helloworld.o</p><p>  q行helloworld<br />$ ./helloworld <br />Hello, Linux World! <br />q样helloworldq译出来了Q你如果按上面的步骤来做的话Q应该也会很Ҏ地编译出正确的helloworld文g。你q可以试着使用一?其他的make命oQ如make cleanQmake installQmake distQ看看它们会l你什么样的效果。感觉如何?自己也能写出q么专业的MakefileQ老板一定会对你刮目相看?/p><p><br />四、深入浅?/p><p>  针对上面提到的各个命令,我们再做些详l的介绍?/p><p>  1?autoscan</p><p>  autoscan是用来扫描源代码目录生成configure.scan文g的。autoscan可以用目录名做ؓ参数Q但如果你不使用参数?话,那么autoscan认Z用的是当前目录。autoscan扫描你所指定目录中的源文Ӟq创建configure.scan文g?/p><p>  2?configure.scan</p><p>  configure.scan包含了系l配|的基本选项Q里面都是一些宏定义。我们需要将它改名ؓconfigure.in</p><p>  3?aclocal</p><p>  aclocal是一个perl 脚本E序。aclocalҎconfigure.in文g的内容,自动生成aclocal.m4文g。aclocal的定义是Q“aclocal - create aclocal.m4 by scanning configure.ac”?/p><p>  4?autoconf</p><p>  autoconf是用来生configure文g的。configure是一个脚本,它能讄源程序来适应各种不同的操作系l^収ͼq且Ҏ不同的系l来产生合适的MakefileQ从而可以你的源代码能在不同的操作pȝq_上被~译出来?/p><p>  configure.in文g的内Ҏ一些宏Q这些宏l过autoconf 处理后会变成查系l特性、环境变量、Y件必ȝ参数的shell脚本。configure.in文g中的宏的序q没有规定,但是你必d所有宏的最?面和最后面分别加上AC_INIT宏和AC_OUTPUT宏?/p><p>  在configure.in中:</p><p>  #可C注释,q个宏后面的内容被忽略?/p><p>  AC_INIT(FILE)</p><p>  q个宏用来检查源代码所在的路径?/p><p>AM_INIT_AUTOMAKE(PACKAGE, VERSION)</p><p>   q个宏是必须的,它描qC我们要生成的Y件包的名字及其版本号QPACKAGE是Y件包的名字,VERSION是版本号。当你用make dist命oӞ它会l你生成一个类似helloworld-1.0.tar.gz的Y件发行包Q其中就有对应的软g包的名字和版本号?/p><p>AC_PROG_CC</p><p>  q个宏将查系l所用的C~译器?/p><p>AC_OUTPUT(FILE)</p><p>  q个宏是我们要输出的Makefile的名字?/p><p>  我们在用automakeӞ实际上还需要用到其他的一些宏Q但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文g?/p><p>  产生了configure.in和aclocal.m4 两个宏文件后Q我们就可以使用autoconf来生configure文g了?/p><p>  5?Makefile.am</p><p>  Makefile.am是用来生成Makefile.in的,需要你手工书写。Makefile.am中定义了一些内容:</p><p>AUTOMAKE_OPTIONS</p><p>  q个是automake的选项。在执行automakeӞ它会查目录下是否存在标准GNU软g包中应具备的各种文gQ例如AUTHORS、ChangeLog、NEWS{文件。我们将其设|成foreignӞautomake会改用一般Y件包的标准来查?/p><p>bin_PROGRAMS</p><p>  q个是指定我们所要生的可执行文件的文g名。如果你要生多个可执行文gQ那么在各个名字间用I格隔开?/p><p>helloworld_SOURCES</p><p>  q个是指定生“helloworld”时所需要的源代码。如果它用到了多个源文gQ那么请使用I格W号它们隔开。比如需?helloworld.hQhelloworld.c那么请写成helloworld_SOURCES= helloworld.h helloworld.c?/p><p>  如果你在bin_PROGRAMS定义了多个可执行文gQ则对应每个可执行文仉要定义相对的filename_SOURCES?/p><p>LIBS<br />   q个用来指定链接的程序库。如LIBS += -lpthreadQ指定链接pthread库?/p><p>  6?automake</p><p>  我们使用automake --add-missing来生Makefile.in?/p><p>  选项--add-missing的定义是“add missing standard files to package”,它会让automake加入一个标准的软g包所必须的一些文件?/p><p>  我们用automake产生出来的Makefile.in文g是符合GNU Makefile惯例的,接下来我们只要执行configureq个shell 脚本可以生合适的 Makefile 文g了?/p><p>  7?Makefile</p><p>  在符合GNU Makefiel惯例的Makefile中,包含了一些基本的预先定义的操作:<br />make<br />ҎMakefile~译源代码,q接Q生成目标文Ӟ可执行文件?br />make clean<br />清除上次的make命o所产生的object文gQ后~为?o”的文gQ及可执行文件?br />make install<br />编译成功的可执行文件安装到pȝ目录中,一般ؓ/usr/local/bin目录?br />make dist<br />产生发布软g包文Ӟ即distribution packageQ。这个命令将会将可执行文件及相关文g打包成一个tar.gz压羃的文件用来作为发布Y件的软g包?br />它会在当前目录下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文g。PACKAGE和VERSIONQ是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)?/p><p>make distcheck</p><p>  生成发布软g包ƈ对其q行试查,以确定发布包的正性。这个操作将自动把压~包文g解开Q然后执行configure命oQƈ且执行makeQ来认~译不出现错误,最后提CZ软g包已l准备好Q可以发布了?/p><p>===============================================<br />helloworld-1.0.tar.gz is ready for distribution<br />===============================================<br />make distclean</p><p>  cMmake cleanQ但同时也将configure生成的文件全部删除掉Q包括Makefile?/p><p>  五、结束语</p><p>  通过上面的介l,你应该可以很Ҏ地生成一个你自己的符合GNU惯例的Makefile文g及对应的目文g?/p><p>  如果你想写出更复杂的且符合惯例的MakefileQ你可以参考一些开放代码的目中的configure.in和Makefile.am文gQ比如:嵌入式数据库sqliteQ单元测试cppunit?/p><img src ="http://www.tkk7.com/huyi2006/aggbug/187907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2008-03-22 17:19 <a href="http://www.tkk7.com/huyi2006/articles/187907.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>STDIN和STDIN_FILENO的区?/title><link>http://www.tkk7.com/huyi2006/articles/179096.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 02 Feb 2008 14:45:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/179096.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/179096.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/179096.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/179096.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/179096.html</trackback:ping><description><![CDATA[ <p>我个人理解STDIN应该是指针吧。像通过fopen 打开的是文g指针Q通过fgets,fwrite操作Q是文g方式。属于高UIOQ带~冲的?br />而STDIN_FILENO则是文g描述W,是个整型Qopen,read,write用到的是文g描述W。属于低UIOQ要自己处理~冲?/p> <img src ="http://www.tkk7.com/huyi2006/aggbug/179096.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2008-02-02 22:45 <a href="http://www.tkk7.com/huyi2006/articles/179096.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ps 命o中stat字段的含?/title><link>http://www.tkk7.com/huyi2006/articles/175405.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Tue, 15 Jan 2008 03:11:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/175405.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/175405.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/175405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/175405.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/175405.html</trackback:ping><description><![CDATA[ <div id="6111166" class="tit">ps 命o中stat字段的含?/div> <div id="6161161" class="date"> </div> <table style="TABLE-LAYOUT: fixed"> <tbody> <tr> <td> <div id="1611666" class="cnt"> <p style="TEXT-INDENT: 2em">D 无法中断的休眠状态(通常 IO 的进E)Q?/p> <p style="TEXT-INDENT: 2em">R 正在q行可中在队列中可过行的Q?/p> <p style="TEXT-INDENT: 2em">S 处于休眠状态;</p> <p style="TEXT-INDENT: 2em">T 停止或被q踪Q?/p> <p style="TEXT-INDENT: 2em">W q入内存交换Q从内核2.6开始无效)Q?/p> <p style="TEXT-INDENT: 2em">X L的进E(从来没见q)Q?/p> <p style="TEXT-INDENT: 2em">Z 僵尸q程Q?/p> <p style="TEXT-INDENT: 2em"> </p> <p style="TEXT-INDENT: 2em">< 优先U高的进E?/p> <p style="TEXT-INDENT: 2em">N 优先U较低的q程</p> <p style="TEXT-INDENT: 2em">L 有些被锁进内存Q?/p> <p style="TEXT-INDENT: 2em">s q程的领D(在它之下有子q程Q;</p> <p style="TEXT-INDENT: 2em">l 多进E的Q?CLONE_THREAD, cM NPTL pthreadsQ;</p> <p style="TEXT-INDENT: 2em">+ 位于后台的进E组Q?/p> </div> </td> </tr> </tbody> </table> <img src ="http://www.tkk7.com/huyi2006/aggbug/175405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2008-01-15 11:11 <a href="http://www.tkk7.com/huyi2006/articles/175405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解Proc文gpȝhttp://www.tkk7.com/huyi2006/articles/121838.htmlallicallicMon, 04 Jun 2007 06:53:00 GMThttp://www.tkk7.com/huyi2006/articles/121838.htmlhttp://www.tkk7.com/huyi2006/comments/121838.htmlhttp://www.tkk7.com/huyi2006/articles/121838.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/121838.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/121838.html

理解 Proc 文gpȝ

目录:
 

摘要:

Linux 内核提供了一U通过 /proc 文gpȝQ在q行时访问内核内部数据结构、改变内核设|的机制。尽在各种gq_上的 Linux pȝ?/proc 文gpȝ的基本概念都是相同的Q但本文只讨论基?intel x86 架构?Linux /proc 文gpȝ?


_________________ _________________ _________________

 

/proc --- 一个虚拟文件系l?/h2>

/proc 文gpȝ是一U内核和内核模块用来向进E?(process) 发送信息的机制 (所以叫?/proc)。这个伪文gpȝ让你可以和内核内部数据结构进行交互,获取 有关q程的有用信息,在运行中 (on the fly) 改变讄 (通过改变内核参数)?与其他文件系l不同,/proc 存在于内存之中而不是硬盘上。如果你察看文g /proc/mounts (?mount 命o一样列出所有已l加载的文gpȝ)Q你会看到其?一行是q样的:


grep proc /proc/mounts
/proc /proc proc rw 0 0

/proc 由内核控Ӟ没有承蝲 /proc 的设备。因?/proc 主要存放由内核控制的状态信息,所以大部分q些信息的逻辑位置位于内核控制的内存。对 /proc q行一?'ls -l' 可以看到大部分文仉?0 字节大的Q不q察看这些文件的时候,实可以看到一些信息。这怎么可能Q这是因?/proc 文gpȝ和其他常规的文gpȝ一h自己注册到虚拟文件系l层 (VFS) 了。然而,直到?VFS 调用它,h文g、目录的 i-node 的时候,/proc 文gpȝ才根据内怸的信息徏立相应的文g和目录?

 

加蝲 proc 文gpȝ

如果pȝ中还没有加蝲 proc 文gpȝQ可以通过如下命o加蝲 proc 文gpȝQ?

mount -t proc proc /proc

上述命o成功加载你?proc 文gpȝ。更多细节请阅读 mount 命o?man page?

 

察看 /proc 的文?/h2>

/proc 的文件可以用于访问有兛_核的状态、计机的属性、正在运行的q程的状态等信息。大部分 /proc 中的文g和目录提供系l物理环境最新的信息。尽?/proc 中的文g是虚拟的Q但它们仍可以用Q何文件编辑器或像'more', 'less'?'cat'q样的程序来查看。当~辑E序试图打开一个虚拟文件时Q这个文件就通过内核中的信息被凭I地 (on the fly) 创徏了。这是一些我从我的系l中得到的一些有结果:

$ ls -l /proc/cpuinfo
-r--r--r-- 1 root root 0 Dec 25 11:01 /proc/cpuinfo

$ file /proc/cpuinfo
/proc/cpuinfo: empty

$ cat /proc/cpuinfo

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 8
model name      : Pentium III (Coppermine)
stepping        : 6
cpu MHz         : 1000.119
cache size      : 256 KB
fdiv_bug        : no
hlt_bug         : no
sep_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 mmx fxsr xmm
bogomips        : 1998.85

processor       : 3
vendor_id       : GenuineIntel
cpu family      : 6
model           : 8
model name      : Pentium III (Coppermine)
stepping        : 6
cpu MHz         : 1000.119
cache size      : 256 KB
fdiv_bug        : no
hlt_bug         : no
sep_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 mmx fxsr xmm
bogomips        : 1992.29

q是一个从?CPU 的系l中得到的结果,上述大部分的信息十分清楚地给Zq个pȝ的有用的g信息。有?/proc 的文件是l过~码的,不同的工具可以被用来解释q些~码q的信息q输出成可读的Ş式。这L工具包括Q?top', 'ps', 'apm' {?

 

得到有用的系l?内核信息


proc 文gpȝ可以被用于收集有用的关于pȝ和运行中的内核的信息。下面是一些重要的文gQ?

  • /proc/cpuinfo - CPU 的信?(型号, 家族, ~存大小{?
  • /proc/meminfo - 物理内存、交换空间等的信?
  • /proc/mounts - 已加载的文gpȝ的列?
  • /proc/devices - 可用讑֤的列?
  • /proc/filesystems - 被支持的文gpȝ
  • /proc/modules - 已加载的模块
  • /proc/version - 内核版本
  • /proc/cmdline - pȝ启动时输入的内核命o行参?
proc 中的文gq不止上面列出的q么多。想要进一步了解的读者可以对 /proc 的每一个文仉'more'一下或d考文献[1]获取更多的有?/proc 目录中的文g的信息。我使用'more'而不?cat'Q除非你知道q个文g很小Q因为有些文?(比如 kcore) 可能会非帔R?

 

有关q行中的q程的信?/h2>

/proc 文gpȝ可以用于获取q行中的q程的信息。在 /proc 中有一些编L子目录。每个编L目录对应一个进E?id (PID)。这P每一个运行中的进E?/proc 中都有一个用它的 PID 命名的目录。这些子目录中包含可以提供有兌E的状态和环境的重要细节信息的文g。让我们试着查找一个运行中的进E?

$ ps -aef | grep mozilla
root 32558 32425 8  22:53 pts/1  00:01:23  /usr/bin/mozilla
上述命o昄有一个正在运行的 mozilla q程?PID ?32558。相对应的,/proc 中应该有一个名?32558 的目?br />
$ ls -l /proc/32558
total 0
-r--r--r--    1 root  root            0 Dec 25 22:59 cmdline
-r--r--r--    1 root  root            0 Dec 25 22:59 cpu
lrwxrwxrwx    1 root  root            0 Dec 25 22:59 cwd -> /proc/
-r--------    1 root  root            0 Dec 25 22:59 environ
lrwxrwxrwx    1 root  root            0 Dec 25 22:59 exe -> /usr/bin/mozilla*
dr-x------    2 root  root            0 Dec 25 22:59 fd/
-r--r--r--    1 root  root            0 Dec 25 22:59 maps
-rw-------    1 root  root            0 Dec 25 22:59 mem
-r--r--r--    1 root  root            0 Dec 25 22:59 mounts
lrwxrwxrwx    1 root  root            0 Dec 25 22:59 root -> //
-r--r--r--    1 root  root            0 Dec 25 22:59 stat
-r--r--r--    1 root  root            0 Dec 25 22:59 statm
-r--r--r--    1 root  root            0 Dec 25 22:59 status
文g "cmdline" 包含启动q程时调用的命o行?envir" q程的环境变两?"status" 是进E的状态信息,包括启动q程的用L用户ID (UID) 和组ID(GID) Q父q程ID (PPID)Q还有进E当前的状态,比如"Sleelping"?Running"。每个进E的目录都有几个W号链接Q?cwd"是指向进E当前工作目录的W号链接Q?exe"指向q行的进E的可执行程序,"root"指向被这个进E看作是根目录的目录 (通常?/")。目?fd"包含指向q程使用的文件描q符的链接?"cpu"仅在q行 SMP 内核时出玎ͼ里面是按 CPU 划分的进E时间?

/proc/self 是一个有的子目录,它得程序可以方便地使用 /proc 查找本进E地信息?proc/self 是一个链接到 /proc 中访?/proc 的进E所对应?PID 的目录的W号链接?br />

 

通过 /proc 与内怺?/h2>


上面讨论的大部分 /proc 的文件是只读的。而实际上 /proc 文gpȝ通过 /proc 中可d的文件提供了对内核的交互机制。写q些文g可以改变内核的状态,因而要慎重改动q些文g?proc/sys 目录存放所有可d的文件的目录Q可以被用于改变内核行ؓ?/p>

/proc/sys/kernel - q个目录包含反通用内核行ؓ的信息?/proc/sys/kernel/{domainname, hostname} 存放着机器/|络的域名和L名。这些文件可以用于修改这些名字?br />

$ hostname
machinename.domainname.com

$ cat /proc/sys/kernel/domainname
domainname.com

$ cat /proc/sys/kernel/hostname
machinename

$ echo "new-machinename"  > /proc/sys/kernel/hostname

$ hostname
new-machinename.domainname.com


q样Q通过修改 /proc 文gpȝ中的文gQ我们可以修改主机名。很多其他可配置的文件存在于 /proc/sys/kernel/。这里不可能列出所有这些文Ӟ读者可以自己去q个目录查看以得到更多细节信息?br />另一个可配置的目录是 /proc/sys/net。这个目录中的文件可以用于修Ҏ?|络的网l属性。比如,单修改一个文Ӟ你可以在|络上瘾藏匿的计机?br />
$ echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
q将在网l上瘾藏你的机器Q因为它不响?icmp_echo。主机将不会响应其他L发出?ping 查询?br />
$ ping machinename.domainname.com
no answer from machinename.domainname.com
要改回缺省设|,只要
$ echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
/proc/sys 下还有许多其它可以用于改变内核属性。读者可以通过参考文?[1], [2] 获取更多信息?

 

l论

/proc 文gpȝ提供了一个基于文件的 Linux 内部接口。它可以用于定pȝ的各U不同设备和q程的状态。对他们q行配置。因而,理解和应用有兌个文件系l的知识是理解你?Linux pȝ的关键?br />

 

参考文?/h2>




allic 2007-06-04 14:53 发表评论
]]>Linux Deamon~程Ҏhttp://www.tkk7.com/huyi2006/articles/117860.htmlallicallicWed, 16 May 2007 07:41:00 GMThttp://www.tkk7.com/huyi2006/articles/117860.htmlhttp://www.tkk7.com/huyi2006/comments/117860.htmlhttp://www.tkk7.com/huyi2006/articles/117860.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/117860.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/117860.html
Linux Deamon~程Ҏ
守护q程QDaemonQ是q行在后台的一U特D进E。它独立于控制终端ƈ且周期性地执行某种d或等待处理某些发生的事g。守护进E是一U很 有用的进E? Linux的大多数服务器就是用守护q程实现的。比如,Internet服务器inetdQWeb服务器httpd{。同Ӟ守护q程完成许多pȝd? 比如Q作业规划进EcrondQ打印进Elpd{?

守护q程的编E本wƈ不复杂,复杂的是各种版本的Unix的实现机制不相同,造成不同 Unix环境下守护进E的~程规则q不一致。需要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System VQ到Linux会出现错误的。下面将l出Linux下守护进E的~程要点和详l实例?

一Q?守护q程及其Ҏ?

守护q程最重要的特性是后台q行。在q一点上DOS下的帔R内存E序TSR与之怼。其ơ,守护q程必须与其q行前的环境隔离开来。这些环境包括未 关闭的文件描q符Q控制终端,会话和进E组Q工作目录以及文件创建掩模等。这些环境通常是守护进E从执行它的父进E(特别是shellQ中l承下来的。最 后,守护q程的启动方式有其特D之处。它可以在Linuxpȝ启动时从启动脚本/etc/rc.d中启动,可以׃业规划进Ecrond启动Q还可以q L端(通常?shellQ执行?

MQ除开q些Ҏ性以外,守护q程与普通进E基本上没有什么区别。因此,~写守护q程实际上是把一个普通进E按照上q的守护q程的特性改造成为守护进E。如果对q程有比较深入的认识更Ҏ理解和编E了?

二. 守护q程的编E要?

前面讲过Q不同Unix环境下守护进E的~程规则q不一致。所q的是守护进E的~程原则其实都一P区别在于具体的实现细节不同。这个原则就是要? _护进E的Ҏ。同ӞLinux是基于Syetem V的SVR4q循Posix标准Q实现v来与BSD4相比更方ѝ编E要点如下;

1. 在后台运行?

为避免挂h制终端将Daemon攑օ后台执行。方法是在进E中调用fork使父q程l止Q让Daemon在子q程中后台执行?

if(pid=fork())
exit(0); //是父q程Q结束父q程Q子q程l箋

2. q控制l端Q登录会话和q程l?

有必要先介绍一下Linux中的q程与控制终端,d会话和进E组之间的关p:q程属于一个进E组Q进E组PGIDQ就是进E组长的q程? QPIDQ。登录会话可以包含多个进E组。这些进E组׃n一个控制终端。这个控制终端通常是创E的dl端。控制终端,d会话和进E组通常是从父进 E承下来的。我们的目的是要摆脱它们,使之不受它们的媄响。方法是在第1点的基础上,调用setsid()使进E成Z话组长:

setsid();

说明Q当q程是会话组长时setsid()调用p|。但W一点已l保证进E不是会话组ѝsetsid()调用成功后,q程成ؓ新的会话l长和新的进E组长,q与原来的登录会话和q程l脱R由于会话过E对控制l端的独占性,q程同时与控制终端脱R?

3. 止q程重新打开控制l端

现在Q进E已l成为无l端的会话组ѝ但它可以重新申h开一个控制终端。可以通过使进E不再成Z话组长来止q程重新打开控制l端Q?

if(pid=fork()) exit(0); //l束W一子进E,W二子进El(W二子进E不再是会话l长Q?

4. 关闭打开的文件描q符

q程从创建它的父q程那里l承了打开的文件描q符。如不关闭,会费pȝ资源Q造成q程所在的文gpȝ无法怸以及引v无法预料的错误。按如下Ҏ关闭它们Q?

for(i=0;i 关闭打开的文件描q符close(i);>

5. 改变当前工作目录

q程zdӞ其工作目录所在的文gpȝ不能怸。一般需要将工作目录改变到根目录。对于需要{储核心,写运行日志的q程工作目录改变到特定目录?/tmpchdir("/")

6. 重设文g创徏掩模

q程从创建它的父q程那里l承了文件创建掩模。它可能修改守护q程所创徏的文件的存取位。ؓ防止q一点,文件创建掩模清除:umask(0);

7. 处理SIGCHLD信号

处理SIGCHLD信号q不是必ȝ。但对于某些q程Q特别是服务器进E往往在请求到来时生成子进E处理请求。如果父q程不等待子q程l束Q子q程 成为僵进E(zombieQ从而占用系l资源。如果父q程{待子进E结束,增加父q程的负担,影响服务器进E的q发性能。在Linux下可以简单地 ?SIGCHLD信号的操作设为SIG_IGN?

signal(SIGCHLD,SIG_IGN);

q样Q内核在子进E结束时不会产生僵尸q程。这一点与BSD4不同QBSD4下必L式等待子q程l束才能释放僵尸q程?

三. 守护q程实例

守护q程实例包括两部分:ȝ序test.c和初始化E序init.c。主E序每隔一分钟?tmp目录中的日志test.log报告q行状态。初始化E序中的init_daemon函数负责生成守护q程。读者可以利用init_daemon函数生成自己的守护进E?

1Q?init.c清单

Qi nclude < unistd.h >
Qi nclude < signal.h >
Qi nclude < sys/param.h >
Qi nclude < sys/types.h >
Qi nclude < sys/stat.h >

void init_daemon(void)
{
int pid;
int i;
if(pid=fork())
exit(0);//是父q程Q结束父q程
else if(pid< 0)
exit(1);//forkp|Q退?br />//是第一子进E,后台l箋执行
setsid();//W一子进E成为新的会话组长和q程l长
//q与控制l端分离
if(pid=fork())
exit(0);//是第一子进E,l束W一子进E?br />else if(pid< 0)
exit(1);//forkp|Q退?br />//是第二子q程Ql?br />//W二子进E不再是会话l长

for(i=0;i< NOFILE;++i)//关闭打开的文件描q符
close(i);
chdir("/tmp");//改变工作目录?tmp
umask(0);//重设文g创徏掩模
return;
}

2Q?test.c清单

Qi nclude < stdio.h >
Qi nclude < time.h >

void init_daemon(void);//守护q程初始化函?br />
main()
{
FILE *fp;
time_t t;
init_daemon();//初始化ؓDaemon

while(1)//每隔一分钟向test.log报告q行状?br />{
sleep(60);//睡眠一分钟
if((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"Im here at %sn",asctime(localtime(&t)) );
fclose(fp);
}
}
}

以上E序在RedHat Linux6.0下编译通过。步骤如下:

~译Qgcc -g -o test init.c test.c

执行Q?/test

查看q程Qps -ef

从输出可以发现test守护q程的各U特性满上面的要求?



allic 2007-05-16 15:41 发表评论
]]>
Linux启动q程lDhttp://www.tkk7.com/huyi2006/articles/116947.htmlallicallicSat, 12 May 2007 03:22:00 GMThttp://www.tkk7.com/huyi2006/articles/116947.htmlhttp://www.tkk7.com/huyi2006/comments/116947.htmlhttp://www.tkk7.com/huyi2006/articles/116947.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/116947.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/116947.html本文以Redhat 6.0 Linux 2.2.19 for Alpha/AXP为^収ͼ描述了从开机到d?Linux 启动全过E。该文对i386q_同样适用?/blockquote>

Bootloader

在Alpha/AXPq_上引导Linux通常有两U方法,一U是由MILO及其他类似的引导E序引导Q另一U是由Firmware直接引导。MILO功能与i386q_的LILO相近Q但内置有基本的盘驱动E序Q如IDE、SCSI{)Q以及常见的文gpȝ驱动E序Q如ext2Qiso9660{)Q?firmware有ARC、SRM两种形式QARChcBIOS界面Q甚臌有多重引导的讄Q而SRM则具有功能强大的命o行界面,用户可以在控制台上用boot{命令引导系l。ARC有分区(PartitionQ的概念Q因此可以访问到分区的首扇区Q而SRM只能控制{l磁盘的首扇区。两Ufirmware都可以通过引导MILO来引导LinuxQ也可以直接引导Linux的引g码?/p>

“arch/alpha/boot”下是制作Linux Bootloader的文件。“head.S”文件提供了?OSF PAL/1的调用入口,它将被编译后|于引导扇区QARC的分区首扇区或SRM的磁?扇区Q,得到控制后初始化一些数据结构,再将控制转给“main.c”中的start_kernel()Q?start_kernel()向控制台输出一些提C,调用pal_init()初始化PAL代码Q调用openboot() 打开引导讑֤Q通过dFirmware环境Q,调用load()核心代码加载到START_ADDRQ见 “include/asm-alpha/system.h”)Q再Firmware中的核心引导参数加蝲到ZERO_PAGE(0) 中,最后调用runkernel()控制{l?x100000的kernelQbootloader部分l束?/p>

Bootloader中用的所有“srm_”函数在“arch/alpha/lib/”中定义?/p>

以上q种Boot方式是一U最单的方式Q即不需其他工具p引导KernelQ前提是按照 Makefile的指|生成bootimage文gQ内含以上提到的bootloader以及vmlinuxQ然后将 bootimage写入自磁盘引导扇区始的位|中?/p>

当采用MILOq样的引导程序来引导LinuxӞ不需要上面所说的BootloaderQ而只需?vmlinux或vmlinux.gzQ引导程序会d解压加蝲内核?x1000Q小内核Q或0x100000Q大内核Q,q直接进入内核引导部分,x文的W二节?/p>

对于I386q_
i386pȝ中一般都有BIOS做最初的引导工作Q那是四个主分区表中的第一个可引导分区的第一个扇区加载到实模式地址0x7c00上,然后控制{交给它?
在“arch/i386/boot”目录下Qbootsect.S是生成引导扇区的汇编源码Q它首先自己拷贝到0x90000上,然后紧接其后的setup部分Q第二扇区)拯?x90200Q将真正的内总码拷贝到0x100000。以上这些拷贝动作都是以bootsect.S、setup.S以及vmlinux在磁盘上q箋存放为前提的Q也是_我们的bzImage文g或者zImage文g是按照bootsectQsetupQ?vmlinuxq样的顺序组l,q存放于始于引导分区的首扇区的连l磁盘扇Z中?/blockquote>
bootsect.S完成加蝲动作后,q接蟩转到0x90200Q这里正是setup.S的程序入口?setup.S的主要功能就是将pȝ参数Q包括内存、磁盘等Q由BIOSq回Q拷贝到 0x90000-0x901FF内存中,q个地方正是bootsect.S存放的地方,q时它将被系l参数覆盖。以后这些参数将׃护模式下的代码来d?/blockquote>
除此之外Qsetup.Sq将video.S中的代码包含q来Q检和讄昄器和昄模式。最后,setup.S系l{换到保护模式Qƈ跌{?x100000Q对于bzImage格式的大内核?0x100000Q对于zImage格式的是0x1000Q的内核引导代码QBootloaderq程l束?/blockquote>
对于2.4.x版内?/b>
没有什么变化?




回页?/font>


Kernel引导入口

对于I386q_
在i386体系l构中,因ؓi386本n的问题,?arch/alpha/kernel/head.S"中需要更多的讄Q但最l也是通过call SYMBOL_NAME(start_kernel)转到start_kernel()q个体系l构无关的函CL行了?
所不同的是Q在i386pȝ中,当内总bzImage的Ş式压~,卛_内核方式Q__BIG_KERNEL__Q压~时需要预先处理bootsect.S和setup.SQ按照大核模式?(CPP) 处理生成bbootsect.S和bsetup.SQ然后再~译生成相应?o文gQƈ使用 "arch/i386/boot/compressed/build.c"生成的build工具Q将实际的内核(未压~的Q含 kernel中的head.S代码Q与"arch/i386/boot/compressed"下的head.S和misc.c合成CP其中的head.S代替?arch/i386/kernel/head.S"的位|,由Bootloader引导执行Qstartup_32入口Q,然后它调用misc.c中定义的decompress_kernel()函数Q?"lib/inflate.c"中定义的gunzip()内核解压到0x100000Q再转到其上执行 "arch/i386/kernel/head.S"中的startup_32代码?/blockquote>
对于2.4.x版内?/b>
没有变化?




回页?/font>


核心数据l构初始?-内核引导W一部分

start_kernel()中调用了一pd初始化函敎ͼ以完成kernel本n的设|。这些动作有的是公共的,有的则是需要配|的才会执行的?/p>

在start_kernel()函数中,

  • 输出Linux版本信息Qprintk(linux_banner)Q?
  • 讄与体pȝ构相关的环境Qsetup_arch()Q?
  • 表l构初始化(paging_init()Q?
  • 使用"arch/alpha/kernel/entry.S"中的入口点设|系l自陷入口(trap_init()Q?
  • 使用alpha_mvl构和entry.S入口初始化系lIRQQinit_IRQ()Q?
  • 核心q程调度器初始化Q包括初始化几个~省的Bottom-halfQsched_init()Q?
  • 旉、定时器初始化(包括dCMOS旉、估主频、初始化定时器中断等Qtime_init()Q?
  • 提取q分析核心启动参敎ͼ从环境变量中d参数Q设|相应标志位{待处理Q(parse_options()Q?
  • 控制台初始化Qؓ输出信息而先于PCI初始化,console_init()Q?
  • 剖析器数据结构初始化Qprof_buffer和prof_len变量Q?
  • 核心Cache初始化(描述Cache信息的CacheQkmem_cache_init()Q?
  • 延迟校准Q获得时钟jiffies与CPU主频ticks的gq,calibrate_delay()Q?
  • 内存初始化(讄内存上下界和表初始|mem_init()Q?
  • 创徏和设|内部及通用cacheQ?slab_cache"Qkmem_cache_sizes_init()Q?
  • 创徏uid taskcount SLAB cacheQ?uid_cache"Quidcache_init()Q?
  • 创徏文gcacheQ?files_cache"Qfilescache_init()Q?
  • 创徏目录cacheQ?dentry_cache"Qdcache_init()Q?
  • 创徏与虚存相关的cacheQ?vm_area_struct"Q?mm_struct"Qvma_init()Q?
  • 块设备读写缓冲区初始化(同时创徏"buffer_head"cache用户加速访问,buffer_init()Q?
  • 创徏cacheQ内存页hash表初始化Qpage_cache_init()Q?
  • 创徏信号队列cacheQ?signal_queue"Qsignals_init()Q?
  • 初始化内存inode表(inode_init()Q?
  • 创徏内存文g描述W表Q?filp_cache"Qfile_table_init()Q?
  • 查体pȝ构漏z(对于alphaQ此函数为空Qcheck_bugs()Q?
  • SMP机器其余CPUQ除当前引导CPUQ初始化Q对于没有配|SMP的内核,此函CؓI,smp_init()Q?
  • 启动initq程Q创建第一个核心线E,调用init()函数Q原执行序列调用cpu_idle() {待调度Qinit()Q?
xstart_kernel()l束Q基本的核心环境已经建立h了?

对于I386q_
i386q_上的内核启动q程与此基本相同Q所不同的主要是实现方式?
对于2.4.x版内?/b>
2.4.x中变化比较大Q但基本q程没变Q变动的是各个数据结构的具体实现Q比如Cache?




回页?/font>


外设初始?-内核引导W二部分

init()函数作ؓ核心U程Q首先锁定内核(仅对SMP机器有效Q,然后调用 do_basic_setup()完成外设及其驱动E序的加载和初始化。过E如下:

  • ȝ初始化(比如pci_init()Q?
  • |络初始化(初始化网l数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,调用protocolsl构中包含的所有协议的初始化过E,sock_init()Q?
  • 创徏bdflush核心U程Qbdflush()q程帔R核心I间Q由核心唤醒来清理被写过的内存缓冲区Q当bdflush()由kernel_thread()启动后,它将自己命名为kflushdQ?
  • 创徏kupdate核心U程Qkupdate()q程帔R核心I间Q由核心按时调度执行Q将内存~冲Z的信息更新到盘中,更新的内容包括超U块和inode表)
  • 讄q启动核心调늺EkswapdQؓ了防止kswapd启动时将版本信息输出到其他信息中_核心U调用kswapd_setup()讄kswapdq行所要求的环境,然后再创?kswapd核心U程Q?
  • 创徏事g理核心U程Qstart_context_thread()函数启动context_thread()q程Qƈ重命名ؓkeventdQ?
  • 讑֤初始化(包括q口parport_init()、字W设备chr_dev_init()、块讑֤ blk_dev_init()、SCSI讑֤scsi_dev_init()、网l设备net_dev_init()、磁盘初始化及分区检查等{,device_setup()Q?
  • 执行文g格式讄Qbinfmt_setup()Q?
  • 启动M使用__initcall标识的函敎ͼ方便核心开发者添加启动函敎ͼdo_initcalls()Q?
  • 文gpȝ初始化(filesystem_setup()Q?
  • 安装root文gpȝQmount_root()Q?
xdo_basic_setup()函数q回init()Q在释放启动内存D(free_initmem()Qƈl内核解锁以后,init()打开/dev/console讑֤Q重定向stdin、stdout和stderr到控制台Q最后,搜烦文gpȝ中的initE序Q或者由init=命o行参数指定的E序Q,q?execve()pȝ调用加蝲执行initE序?

init()函数到此l束Q内核的引导部分也到此结束了Q这个由start_kernel()创徏的第一个线E已l成Z个用h式下的进E了。此时系l中存在着六个q行实体Q?/p>

  • start_kernel()本n所在的执行体,q其实是一?手工"创徏的线E,它在创徏了init()U程以后p入cpu_idle()循环了,它不会在q程Q线E)列表中出?
  • initU程Q由start_kernel()创徏Q当前处于用h,加蝲了initE序
  • kflushd核心U程Q由initU程创徏Q在核心态运行bdflush()函数
  • kupdate核心U程Q由initU程创徏Q在核心态运行kupdate()函数
  • kswapd核心U程Q由initU程创徏Q在核心态运行kswapd()函数
  • keventd核心U程Q由initU程创徏Q在核心态运行context_thread()函数
对于I386q_
基本相同?
对于2.4.x版内?/b>
q一部分的启动过E在2.4.x内核中简化了不少Q缺省的独立初始化过E只剩下|络Qsock_init()Q和创徏事g理核心U程Q而其他所需要的初始化都使用__initcall()宏包含在do_initcalls()函数中启动执行?




回页?/font>


initq程和inittab引导指o

initq程是系l所有进E的LQ内核在完成核内引导以后Q即在本U程Q进E)I间内加载initE序Q它的进E号??/p>

initE序需要读?etc/inittab文g作ؓ其行为指针,inittab是以行ؓ单位的描q性(非执行性)文本Q每一个指令行都具有以下格式:

id:runlevel:action:process其中id为入口标识符Qrunlevel行别,action为动作代Pprocess为具体的执行E序?/p>

id一般要?个字W以内,对于getty或其他loginE序,要求id与tty的编L同,否则gettyE序不能正常工作?/p>

runlevel是init所处于的运行别的标识Q一般?Q?以及S或s???q行U别被系l保留,0作ؓshutdown动作Q?作ؓ重启臛_用户模式Q?为重启;S和s意义相同Q表C单用户模式Q且无需inittab文gQ因此也不在inittab中出玎ͼ实际上,q入单用h式时Qinit直接在控制台Q?dev/consoleQ上q行/sbin/sulogin?/p>

在一般的pȝ实现中,都用了2???几个U别Q在Redhatpȝ中,2表示无NFS支持的多用户模式Q?表示完全多用h式(也是最常用的别)Q?保留l用戯定义Q?表示XDM囑Şd方式?Q?U别也是可以使用的,传统的Unixpȝ没有定义q几个别。runlevel可以是ƈ列的多个|以匹配多个运行别,对大多数action来说Q仅当runlevel与当前运行别匹配成功才会执行?/p>

initdefault是一个特D的action|用于标识~省的启动别;当init由核心激zM后,它将dinittab中的initdefault,取得其中的runlevelQƈ作ؓ当前的运行别。如果没有inittab文gQ或者其中没有initdefault,init在控制Ch输入 runlevel?/p>

sysinit、boot、bootwait{action在pȝ启动时无条gq行Q而忽略其中的runlevelQ其余的actionQ不含initdefaultQ都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描q?/p>

在Redhatpȝ中,一般情况下inittab都会有如下几:

id:3:initdefault:
#表示当前~省q行U别?--完全多Q务模式;
si::sysinit:/etc/rc.d/rc.sysinit
#启动时自动执?etc/rc.d/rc.sysinit脚本
l3:3:wait:/etc/rc.d/rc 3    
#当运行别ؓ3Ӟ?为参数运?etc/rc.d/rc脚本Qinit等待其q回
0:12345:respawn:/sbin/mingetty tty0 
#?Q?各个U别上以tty0为参数执?sbin/mingettyE序Q打开tty0l端用于
#用户dQ如果进E退出则再次q行mingettyE序
x:5:respawn:/usr/bin/X11/xdm -nodaemon
#?U别上运行xdmE序Q提供xdm囑Ş方式d界面Qƈ在退出时重新执行





回页?/font>


rc启动脚本

上一节已l提到initq程启动运行rc脚本Q这一节将介绍rc脚本具体的工作?/p>

一般情况下Qrc启动脚本都位?etc/rc.d目录下,rc.sysinit中最常见的动作就是激zM换分区,查磁盘,加蝲g模块Q这些动作无论哪个运行别都是需要优先执行的。仅当rc.sysinit执行完以后init才会执行其他的boot或bootwait动作?/p>

如果没有其他boot、bootwait动作Q在q行U别3下,/etc/rc.d/rc会得到执行Q命令行参数?Q即执行/etc/rc.d/rc3.d/目录下的所有文件。rc3.d下的文g都是指向/etc/rc.d/init.d/目录下各个Shell脚本的符可接,而这些脚本一般能接受start、stop、restart、status{参数。rc脚本以start参数启动所有以S开头的脚本Q在此之前,如果相应的脚本也存在K打头的链接,而且已经处于q行态了Q以/var/lock/subsys/下的文g作ؓ标志Q,则将首先启动K开头的脚本Q以stop作ؓ参数停止q些已经启动了的服务Q然后再重新q行。显Ӟq样做的直接目的是当init改变q行U别Ӟ所有相关的服务都将重启Q即使是同一个别?/p>

rcE序执行完毕后,pȝ环境已经讄好了Q下面就该用L录系l了?/p>



回页?/font>


getty和login

在rcq回后,init得到控Ӟq启动mingettyQ见W五节)。mingetty是getty的简化,不能处理串口操作。getty的功能一般包括:

  • 打开l端U,q设|模?
  • 输出d界面及提C,接受用户名的输入
  • 以该用户名作为login的参敎ͼ加蝲loginE序

注:用于q程d的提CZ息位?etc/issue.net中?/p>

loginE序在getty的同一个进E空间中q行Q接受getty传来的用户名参数作ؓd的用户名?/p>

如果用户名不是rootQ且存在/etc/nologin文gQlogin输出nologin文g的内容,然后退出。这通常用来pȝl护旉止非root用户d?/p>

只有/etc/securetty中登C的终端才允许root用户dQ如果不存在q个文gQ则root可以在Q何终端上d?etc/usertty文g用于对用户作出附加访问限Ӟ如果不存在这个文Ӟ则没有其他限制?/p>

当用L录通过了这些检查后Qlogin搜?etc/passwd文gQ必要时搜烦 /etc/shadow文gQ用于匹配密码、设|主目录和加载shell。如果没有指定主目录Q将默认为根目录Q如果没有指定shellQ将默认?bin/sh。在控制{交给shell以前Q?getty输?var/log/lastlog中记录的上次dpȝ的信息,然后查用h否有新邮Ӟ/usr/spool/mail/{username}Q。在讄好shell的uid、gidQ以及TERMQPATH {环境变量以后,q程加蝲shellQlogin的Q务也完成了?/p>



回页?/font>


bash

q行U别3下的用户login以后Q将启动一个用h定的shellQ以下以/bin/bashZl箋我们的启动过E?/p>

bash是Bourne Shell的GNU扩展Q除了承了sh的所有特点以外,q增加了很多Ҏ和功能。由login启动的bash是作Z个登录shell启动的,它承了getty讄的TERM、PATH{环境变量,其中PATH对于普通用户ؓ"/bin:/usr/bin:/usr/local/bin"Q对于root ?/sbin:/bin:/usr/sbin:/usr/bin"。作为登录shellQ它首先寻?etc/profile 脚本文gQƈ执行它;然后如果存在~/.bash_profileQ则执行它,否则执行 ~/.bash_loginQ如果该文g也不存在Q则执行~/.profile文g。然后bash作Z个交互式shell执行~/.bashrc文gQ如果存在的话)Q很多系l中Q~/.bashrc都将启动 /etc/bashrc作ؓpȝ范围内的配置文g?/p>

当显C出命o行提C符的时候,整个启动q程q束了。此时的pȝQ运行着内核Q运行着几个核心U程Q运行着initq程Q运行着一批由rc启动脚本Ȁzȝ守护q程Q如 inetd{)Q运行着一个bash作ؓ用户的命令解释器?/p>



回页?/font>


附:XDM方式d

如果~省q行U别设ؓ5Q则pȝ中不光有1-6个getty监听着文本l端Q还有启动了一个XDM的图形登录窗口。登录过E和文本方式差不多,也需要提供用户名和口令,XDM 的配|文件缺省ؓ/usr/X11R6/lib/X11/xdm/xdm-config文gQ其中指定了 /usr/X11R6/lib/X11/xdm/xsession作ؓXDM的会话描q脚本。登录成功后QXDM执行这个脚本以q行一个会话管理器Q比如gnome-session{?/p>

除了XDM以外Q不同的H口理pȝQ如KDE和GNOMEQ都提供了一个XDM的替代品Q如gdm和kdmQ这些程序的功能和XDM都差不多?br />

关于作?/span>

杨沙zԌP现攻d防科大计机学院计算Y件方向博士学位。您可以通过电子邮g pubb@163.net跟他联系?



allic 2007-05-12 11:22 发表评论
]]>IPC之共享存储区http://www.tkk7.com/huyi2006/articles/115339.htmlallicallicFri, 04 May 2007 09:27:00 GMThttp://www.tkk7.com/huyi2006/articles/115339.htmlhttp://www.tkk7.com/huyi2006/comments/115339.htmlhttp://www.tkk7.com/huyi2006/articles/115339.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/115339.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/115339.htmlIPC之共享存储区
用共享存储区q行q程间通信主要有以下步骤组?br />1. Create shared memory 
int shmget(key_t key, int size, int shmflg); 
if ((shm_id = shmget (mykey, sizeof (struct sharedbuf), 0600 | IFLAGS)) < 0)
    perror ("shmget");

2. Attach shared memory 
char *buf = shmat (shm_id, 0, 0);
 
3. Read / Write shared memory 
sharedbuf->size = size_; 
memcpy(sharedbuf->buf, mybuf, size_); 
memcpy(mybuf, sharedbuf->buf, sharedbuf->size); 
 
3. Detach shared memory (optional) 
shmdt (buf);
 
4. Remove shared memory 
if (shmctl (shm_id, IPC_RMID, (struct shmid_ds *)0) < 0) 
    perror ("shmctl");

一个测试过的实?br />#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define SHM_MODE (SHM_R | SHM_W)
#define SHM_SIZE 2048

int main()
{
 int segment_id, segment_size;
 char *shared_memory;
 pid_t pid;
 
 if((segment_id = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)/*获得׃n内存标识W?/
  perror("shmget error!\n");
 if((shared_memory = shmat(segment_id, 0, 0)) == (void *)-1)/*q程和共享内存段相连?/
  perror("shmat error!\n");
 printf("test1 send a message to share memory.\n");
 sprintf(shared_memory, "Hello test2\n");
 shmdt(shared_memory);/*q链接*/
 pid = fork();
 if(pid < 0)
  perror("Creating process error!\n");
 else if(pid > 0)
 {
  wait(NULL); /*父进E等待子q程l束*/
  shmctl(segment_id, IPC_RMID, 0);/*子进E结束,父进E将׃n内存删除*/
  exit(0);
 }
 else
 {                                                                                                                                                                       
  if((shared_memory = shmat(segment_id, 0, 0)) == (void*)-1)/*子进E和׃n内存q接*/
   perror("shmat error!\n");
  printf("test2 get a message form share memory:%s",shared_memory);
  shmdt(shared_memory);
 }
}     

相关参考:http://blog.csdn.net/Apollo_zhc/archive/2006/06/01/768694.aspx



allic 2007-05-04 17:27 发表评论
]]>
linux的c~程技?/title><link>http://www.tkk7.com/huyi2006/articles/110693.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 14 Apr 2007 13:03:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/110693.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/110693.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/110693.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/110693.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/110693.html</trackback:ping><description><![CDATA[ <p>linux的c~程技?/p> <p>1. 获取文g的信息:<br />stat(char* filename, struct stat* buf);<br />struct stat { <br />dev_t st_dev; /* 讑֤ */ <br />ino_t st_ino; /* 节点 */ <br />mode_t st_mode; /* 模式 */ <br />nlink_t st_nlink; /* 连?*/ <br />uid_t st_uid; /* 用户ID */ <br />gid_t st_gid; /* lID */ <br />dev_t st_rdev; /* 讑֤cd */ <br />off_t st_off; /* 文g字节?*/ <br />unsigned long st_blksize; /* 块大?*/ <br />unsigned long st_blocks; /* 块数 */ <br />time_t st_atime; /* 最后一ơ访问时?*/ <br />time_t st_mtime; /* 最后一ơ修Ҏ?*/ <br />time_t st_ctime; /* 最后一ơ改变时?指属? */ <br />};</p> <p>struct statfs <br />{<br />long f_type; /* 文gpȝcd */<br />long f_bsize; /* 块大?/<br />long f_blocks; /* 块多?/<br />long f_bfree; /* I闲的块Q)*/<br />long f_bavail; /* 可用?*/<br />long f_files; /* L件节?*/<br />long f_ffree; /* I闲文g节点 */<br />fsid_t f_fsid; /* 文gpȝid */<br />long f_namelen; /* 文g名的最大长?*/<br />long f_spare[6]; /* spare for later */<br />};</p> <p>2. 获取文g讉K权限或者判断文件是否存在:<br />int access(char* filename, int mode);</p> <p>3. 获取当前旉Q?br />time_t t;char* asctime(localtime(&t));<br />或?br />time(&t);char* ctime(&t);<br />得到的字W串形式为:Wed Mar 12 10:07:53 2003</p> <p>4. 计算两个时刻之间的时间差<br />double difftime(time_t time2, time_t time1);</p> <p>5. 删除某文Ӟ<br />int unlink(char* pathname);<br />int remove(char* pathname);</p> <p>6. 删除某目录:<br />int rmdir(const char* pathname);</p> <p>7. 获得当前所在目录名Q?br />char * getcwd(char *buf,size_t size); buf会q回目前路径名称?</p> <p>8. 获取目录信息Q?br />DIR * opendir(const char * pathname);<br />int closedir(DIR *dir);<br />struct dirent * readdir(DIR *dir);<br />struct dirent <br />{ <br />long d_ino; /* inode number */ <br />off_t d_off; /* offset to this dirent */ <br />unsigned short d_reclen; /* length of this d_name */ <br />char d_name [NAME_MAX+1]; /* file name (null-terminated) */ <br />};</p> <p>9. strerror(errno);函数会返回一个指定的错误L错误信息的字W串. </p> <p>10.得到当前路径下面所有的文g(包含目录)的个?<br />struct dirent **namelist; <br />int num = scandir(".",&namelist,0,alphasort)</p> <p>11./etc/ld.so.confQ包含共享库的搜索位|?<br />查看执行文g调用了哪些共享库<br />shell>ldd a.out<br />׃n库管理工P一般在更新了共享库之后要运行该命o <br />shell>ldconfig</p> <p>12.查看文g执行的速度<br />shell>time ./a.out</p> <p>13.改变文g讉K权限<br />int chmod(const char* path, mode_t mode);</p> <p>14.改变文g大小 <br />int chsize(int handle, long size);</p> <p>15.把一个QҎ转换为字W串 <br />char ecvt(double value, int ndigit, int *decpt, int *sign);</p> <p>16.文件结?br />int eof(int *handle);</p> <p>17.流上的文gl束W?<br />int feof(FILE *stream);</p> <p>18.流上的错误<br />int ferror(FILE *stream);</p> <p>19.装入q运行其它程序的函数<br />int execl(char *pathname, char *arg0, arg1, ..., argn, NULL);<br />int execle(char *pathname, char *arg0, arg1, ..., argn, NULL,<br />char *envp[]);<br />int execlp(char *pathname, char *arg0, arg1, .., NULL); <br />int execple(char *pathname, char *arg0, arg1, ..., NULL, <br />char *envp[]); <br />int execv(char *pathname, char *argv[]); <br />int execve(char *pathname, char *argv[], char *envp[]); <br />int execvp(char *pathname, char *argv[]); <br />int execvpe(char *pathname, char *argv[], char *envp[]);</p> <p>20.指数函数 <br />double exp(double x);</p> <p>21. struct sockaddr <br />{<br />unsigned short sa_family; /* address family, AF_xxx */<br />char sa_data[14]; /* 14 bytes of protocol address */<br />};<br />struct sockaddr_in<br />{<br />short int sin_family; /* Address family */<br />unsigned short int sin_port; /* Port number */<br />struct in_addr sin_addr; /* Internet address */<br />unsigned char sin_zero[8]; /* Same size as struct sockaddr */<br />};<br />struct in_addr <br />{<br />unsigned long s_addr;<br />};<br />s_addr按照|络字节序存储IP地址<br />sin_zero是ؓ了让sockaddr与sockaddr_in两个数据l构保持大小相同而保留的I字节?/p> <p>使用的例子:<br />struct sockaddr_in sa;<br />sa.sin_family = AF_INET;<br />sa.sin_port = htons(3490);<br />sa.sin_addr.s_addr = inet_addr("132.241.5.10");<br />bzero(&(sa.sin_zero), 8);<br />注意Q如果sa.sin_addr.s_addr Q?INADDR_ANYQ则不指定IP地址Q用于ServerE序Q?/p> <p>22. #define UNIX_PATH_MAX 108<br />struct sockaddr_un <br />{<br />sa_family_t sun_family; /* AF_UNIX */<br />char sun_path[UNIX_PATH_MAX]; /* 路径?*/<br />};</p> <p>23. IP地址转换函数:<br />unsigned long inet_addr (const char *cp);<br />inet_addr一个点分十q制IP地址字符串{换成32位数字表C的IP地址Q网l字节顺序)</p> <p>char* inet_ntoa (struct in_addr in);<br />inet_ntoa一?2位数字表C的IP地址转换成点分十q制IP地址字符丌Ӏ?/p> <p>q两个函C为反函数</p> <p>字节序转换:<br />htons()--"Host to Network Short"<br />htonl()--"Host to Network Long"<br />ntohs()--"Network to Host Short"<br />ntohl()--"Network to Host Long" </p> <p>24. 获取当前机器的CPU、内存用情?br />getrusage </p> <p>25. open的用中常用的flag和mode参数<br />int FILE_FLAG = O_WRONLY|O_APPEND|O_CREAT;<br />int FILE_MODE = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;</p> <p>26. makefile中常用的W号Q?br />预定义变?含义<br />$* 不包含扩展名的目标文件名U?br />$@ 目标的完整名U?br />$% 如果目标是归档成员,则该变量表示目标的归档成员名U。例如,如果目标名称<br />?mytarget.so(image.o)Q则 $@ ?mytarget.soQ?$% ?image.o?/p> <p>$+ 所有的依赖文gQ以I格分开Qƈ以出现的先后为序Q可能包含重复的依赖文g?br />$< W一个依赖文件的名称?br />$? 所有的依赖文gQ以I格分开Q这些依赖文件的修改日期比目标的创徏日期?br />$^ 所有的依赖文gQ以I格分开Q不包含重复的依赖文件?/p> <p>AR 归档l护E序的名Uͼ默认gؓ ar?br />ARFLAGS 归档l护E序的选项?br />AS 汇编E序的名Uͼ默认gؓ as?br />ASFLAGS 汇编E序的选项?br />CC C ~译器的名称Q默认gؓ cc?br />CCFLAGS C ~译器的选项?br />CPP C 预编译器的名Uͼ默认gؓ $(CC) -E?br />CPPFLAGS C 预编译的选项?br />CXX C++ ~译器的名称Q默认gؓ g++?br />CXXFLAGS C++ ~译器的选项?br />FC FORTRAN ~译器的名称Q默认gؓ f77?br />FFLAGS FORTRAN ~译器的选项?/p> <p>用变量object表示所有的.o文gQ?br />objects := $(wildcard *.o)</p> <p>make -n或?-just-print表示只是昄命oQ但不会执行命o<br />make -s或?-slient表示全面止命o的显C?br />make -i或?-ignore-errors表示Makefile中所有命令都会忽略错?br />make -k或?-keep-going表示如果某规则中的命令出错了Q那么就l止该规则的执行Q但l箋执行其它规则</p> <p>在makefile中直接利用shell获取变量PLAT<br />使用make中的一U用变量来定义变量的Ҏ。这U方法用的是?=”操作符<br />PLAT := $(shell uname -a)</p> <p>我们要定义一个变量,其值是一个空|那么我们可以q样来:<br />nullstring :=<br />space := $(nullstring) # end of the line</p> <p>FOO ?= bar含义?<br />如果FOO没有被定义过Q那么变量FOO的值就是“bar”,如果FOO先前被定义过Q那么这条语什么也不做Q其{h于:<br />ifeq ($(origin FOO), undefined)<br />FOO = bar<br />endif</p> <p>foo := a.o b.o c.o<br />bar := $(foo:.o=.c)<br />我们先定义了一个?(foo)”变量,而第二行的意思是把?(foo)”中所有以?o”字东y结䏀全部替换成?c”,所以我们的?(bar)”的值就是“a.c b.c c.c”?/p> <p> <br />你想在Makefile中设|参数|你可以用“override”指C符。其语法是:<br />override <variable> = <value><br />override <variable> := <value><br />当然Q你q可以追加:<br />override <variable> += <more text></p> <p>makeq行时的pȝ环境变量可以在make开始运行时被蝲入到Makefile文g中,<br />但是如果Makefile中已定义了这个变量,或是q个变量由make命o行带入,<br />那么pȝ的环境变量的值将被覆盖?br />如果make指定了?e”参敎ͼ那么Q系l环境变量将覆盖Makefile中定义的变量<br /></p> <img src ="http://www.tkk7.com/huyi2006/aggbug/110693.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2007-04-14 21:03 <a href="http://www.tkk7.com/huyi2006/articles/110693.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>~写Linux/Unix守护q程 [zz]http://www.tkk7.com/huyi2006/articles/110431.htmlallicallicFri, 13 Apr 2007 06:04:00 GMThttp://www.tkk7.com/huyi2006/articles/110431.htmlhttp://www.tkk7.com/huyi2006/comments/110431.htmlhttp://www.tkk7.com/huyi2006/articles/110431.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/110431.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/110431.html

~写Linux/Unix守护q程 [zz]

守护q程在Linux/Unixpȝ中有着q泛的应用。有Ӟ开发h员也x自己的程序变成守护进E。在创徏一个守护进E的时候,要接触到子进E、进E组、会晤期、信h制、文件、目录和控制l端{多个概c因此守护进E还是比较复杂的Q在q里详细地讨论Linux/Unix的守护进E的~写Qȝ出八条经验,q给出应用范例?

 ~程要点

    1.屏蔽一些有x制终端操作的信号。防止在守护q程没有正常q{hӞ控制l端受到q扰退出或挂v。示例如下:

signal(SIGTTOU,SIG_IGN); 
signal(SIGTTIN,SIG_IGN); 
signal(SIGTSTP,SIG_IGN); 
signal(SIGHUP ,SIG_IGN);/PRE>
BR>
    所有的信号都有自己的名字。这些名字都以“SIG”开_只是后面有所不同。开发h员可以通过q些名字了解到系l中发生了什么事。当信号出现Ӟ开发h员可以要求系l进行以下三U操作:
    ?忽略信号。大多数信号都是采取q种方式q行处理的,q里采用了q种用法。但值得注意的是对SIGKILL和SIGSTOP信号不能做忽略处理?br />    ?捕捉信号。最常见的情况就是,如果捕捉到SIGCHID信号Q则表示子进E已l终止。然后可在此信号的捕捉函C调用waitpid()函数取得该子q程的进EID和它的终止状态。另外,如果q程创徏了时文Ӟ那么pE终止信号SIGTERM~写一个信h捉函数来清除q些临时文g?br />    ?执行pȝ的默认动作。对l大多数信号而言Q系l的默认动作都是l止该进E?

    对这些有关终端的信号Q一般采用忽略处理,从而保障了l端免受q扰?

    q类信号分别是,SIGTTOUQ表C后台进E写控制l端Q、SIGTTINQ表C后台进E读控制l端Q、SIGTSTPQ表C终端挂P和SIGHUPQ进E组镉K出时向所有会议成员发出的Q?

    2.程序进入后台执行。由于守护进E最l脱L制终端,到后台去q行。方法是在进E中调用fork使父q程l止Q让Daemon在子q程中后台执行。这是常说的“脱壳”。子q程l箋函数fork()的定义如下:

#include <sys/types.h>
#include <unistd.h>
 pid_t fork(void);/PRE>
BR>
    该函数是Linux/Unix~程中非帔R要的函数。它被调用一ơ,但返回两ơ。这两次q回的区别是子进E的q回gؓ?”,而父q程的返回gؓ子进E的ID。如果出错则q回?1”?

    3.q控制l端、登录会话和q程l。开发h员如果要摆脱它们Q不受它们的影响Q一般?setsid() 讄C话的领头q程Qƈ与原来的d会话和进E组q。这只是其中的一U方法,也有如下处理的办法:

if  ((fd = open("/dev/tty",O_RDWR)) >= 0) { 
ioctl(fd,TIOCNOTTY,NULL); 
close(fd); 
}/PRE>
BR>
    其中/dev/tty是一个流讑֤Q也是终端映,调用close()函数终端关闭?

    4.止q程重新打开控制l端。进E已l成为无l端的会话组长,但它可以重新甌打开一个控制终端。开发h员可以通过不再让进E成Z话组长的方式来禁止进E重新打开控制l端Q需要再ơ调用fork函数?br />    上面的程序代码表C结束第一子进E,W二子进El(W二子进E不再是会话l长Q?

    5. 关闭打开的文件描q符Qƈ重定向标准输入、标准输出和标准错误输出的文件描q符。进E从创徏它的父进E那里承了打开的文件描q符。如果不关闭Q将会浪费系l资源,引v无法预料的错误。关闭三者的代码如下Q?

for (fd = 0, fdtablesize = getdtablesize(); 
 fd < fdtablesize; fd++) 
  close(fd);/PRE>
BR>
    但标准输入、标准输出和标准错误输出的重定向是可选的。也许有的程序想保留标准输入Q?Q、标准输出(1Q和标准错误输出Q?Q,那么循环应绕q这三者。代码如下:

for (fd =3, fdtablesize = getdtablesize();
fd < fdtablesize; fd++) 
  close(fd);/PRE>
BR>
    有的E序有些Ҏ的需求,q需要将q三者重新定向。示例如下:

error=open("/tmp/error",O_WRONLY|O_CREAT,
0600);
  dup2(error,2);
 close(error);
 in=open("/tmp/in",O_RDONLY|O_CREAT,0600);
 if(dup2(in,0)==-1)  perror("in");
 close(in);
out=open("/tmp/out",O_WRONLY|O_CREAT,0600);
 if(dup2(out,1)==-1) perror("out");
 close(out);/PRE>
BR>
    6.改变工作目录到根目录或特定目录进E活动时Q其工作目录所在的文gpȝ不能怸?

    一般需要将工作目录改变到根目录或特定目录,注意用户Ҏ目录需要有d权。防止超U用户卸载设备时pȝ报告讑֤忙?

    7.处理SIGCHLD信号。SIGCHLD信号是子q程l束Ӟ向内核发送的信号?

如果父进E不{待子进E结束,子进E将成ؓ僵尸q程QzombieQ从而占用系l资源。因此需要对SIGCHLD信号做出处理Q回收僵进E的资源Q避免造成不必要的资源费。可以用如下语句Q?br />    signal(SIGCHLD,(void *)reap_status);

    捕捉信号SIGCHLDQ用下面的函数进行处理:

void reap_status() 
 { int pid; 
   union wait status; 
   while ((pid = wait3(&status,WNOHANG,NULL)) > 0) 
  …?}/PRE>
BR>
    8.在Linux/Unix下有个syslogd的守护进E,向用h供了syslog()pȝ调用。Q何程序都可以通过syslog记录事g?

    ׃syslog非常好用和易配置Q所以很多程序都使用syslog来发送它们的记录信息。一般守护进E也使用syslog向系l输Z息。syslog有三个函敎ͼ一般只需要用syslog(...)函数Qopenlog()/closelog()可有可无。syslog()在shslog.h定义如下Q?

#include <syslog.h>
void syslog(int priority,char *format,...);/PRE>
BR>
    其中参数priority指明了进E要写入信息的等U和用途。第二个参数是一个格式串Q指定了记录输出的格式。在q个串的最后需要指定一?mQ对应errno错误码?

    应用范例

    下面l出Linux下编E的守护q程的应用范例,在UNIX中,不同版本实现的细节可能不一_但其实现的原则是与Linux一致的?

#include <stdio.h> 
#include <signal.h> 
#include <sys/file.h> 
main(int argc,char **argv)
{
  time_t now;
  int childpid,fd,fdtablesize;
  int error,in,out;
  /* 忽略l端 I/O信号,STOP信号 */
 signal(SIGTTOU,SIG_IGN);
 signal(SIGTTIN,SIG_IGN);
  signal(SIGTSTP,SIG_IGN); 
  signal(SIGHUP ,SIG_IGN);
  /* 父进E退?E序q入后台q行 */
  if(fork()!=0) exit(1);
   if(setsid()<0)exit(1);/* 创徏一个新的会议组 */ 
  /* 子进E退?孙进E没有控制终端了 */  
  if(fork()!=0) exit(1);
  if(chdir("/tmp")==-1)exit(1);
/* 关闭打开的文件描q符,包括标准输入、标准输出和标准错误输出 */ 
 for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) 
   close(fd);
   umask(0);/*重设文g创徏掩模 */ 
   signal(SIGCHLD,SIG_IGN);/* 忽略SIGCHLD信号 */ 
/*打开logpȝ*/
  syslog(LOG_USER|LOG_INFO,"守护q程试!n");  
   while(1)  
   {  
    time(&now);
   syslog(LOG_USER|LOG_INFO,"当前旉:t%sttn",ctime(&now));
    sleep(6);
     }  
 }/PRE>
BR>
    此程序在Turbo Linux 4.0下编译通过。这个程序比较简单,但基本体C守护q程的编E要炏V读者针对实际应用中不同的需要,q可以做相应的调整?

allic 2007-04-13 14:04 发表评论
]]>
SIGCHLD的一个处理函数[c/c++] http://www.tkk7.com/huyi2006/articles/110430.htmlallicallicFri, 13 Apr 2007 05:56:00 GMThttp://www.tkk7.com/huyi2006/articles/110430.htmlhttp://www.tkk7.com/huyi2006/comments/110430.htmlhttp://www.tkk7.com/huyi2006/articles/110430.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/110430.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/110430.html

void sig_chld(int sig)
{
   pid_t pid;
   int stat;

   while(1)
   {
     pid = waitpid(-1,&stat, WNOHANG);//
WNOHANG 非阻?/span>方式
     if(pid == 0 ||(pid ==-1 &&errno!= EINTR))
     {
         break;
     }
   }
   
   return;
}



allic 2007-04-13 13:56 发表评论
]]>
Linux下定时器使用 http://www.tkk7.com/huyi2006/articles/110356.htmlallicallicFri, 13 Apr 2007 01:39:00 GMThttp://www.tkk7.com/huyi2006/articles/110356.htmlhttp://www.tkk7.com/huyi2006/comments/110356.htmlhttp://www.tkk7.com/huyi2006/articles/110356.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/110356.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/110356.htmlLinux下的定时器有两种Q以下分别介l:

       1、alarm
       如果不要求很_的话Q用 alarm() ?signal() 够?
           unsigned int alarm(unsigned int seconds)
       专门为SIGALRM信号而设Q在指定的时间secondsU后Q将向进E本w发送SIGALRM信号Q又UCؓ闚w旉。进E调用alarm后,M以前的alarm()调用都将无效。如果参数seconds为零Q那么进E内不再包含Q何闹钟时间。如果调用alarmQ)前,q程中已l设|了闚w旉Q则q回上一个闹钟时间的剩余旉Q否则返??

       CZQ?
       #include <stdio.h>
       #include <unistd.h>
       #include <signal.h>

       void sigalrm_fn(int sig)
       {
               /* Do something */
               printf("alarm!\n");

               alarm(2);
               return;
       }

       int main(void)
       {
               signal(SIGALRM, sigalrm_fn);
               alarm(2);

               /* Do someting */
               while(1) pause();
       }


       2、setitimer
       int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
       setitimer()比alarm功能强大Q支?U类型的定时器:

       ITIMER_REAL :  以系l真实的旉来计,它送出SIGALRM信号? 
       ITIMER_VIRTUAL :  以该行程真正有执行的旉来计,它送出SIGVTALRM信号? 
       ITIMER_PROF :  以行E真正有执行及在核心中所费的旉来计,它送出SIGPROF信号? 
       Setitimer()W一个参数which指定定时器类型(上面三种之一Q;W二个参数是l构itimerval的一个实例;W三个参数可不做处理?
       Setitimer()调用成功q回0Q否则返?1?

       下面是关于setitimer调用的一个简单示范,在该例子中,每隔一U发Z个SIGALRMQ每?.5U发Z个SIGVTALRM信号Q:
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <signal.h>
       #include <time.h>
       #include <sys/time.h>

       int sec;
       void sigroutine(int signo){

           switch (signo){
           case SIGALRM:
               printf("Catch a signal -- SIGALRM \n");
               signal(SIGALRM, sigroutine);
               break;
           case SIGVTALRM:
               printf("Catch a signal -- SIGVTALRM \n");
               signal(SIGVTALRM, sigroutine);
               break;
           }
           return;
       }

       int main()
       {
           struct itimerval value, ovalue, value2;
   
           sec = 5;
           printf("process id is %d ", getpid());
           signal(SIGALRM, sigroutine);
           signal(SIGVTALRM, sigroutine);
           value.it_value.tv_sec = 1;
           value.it_value.tv_usec = 0;
           value.it_interval.tv_sec = 1;
           value.it_interval.tv_usec = 0;
           setitimer(ITIMER_REAL, &value, &ovalue);

           value2.it_value.tv_sec = 0;
           value2.it_value.tv_usec = 500000;
           value2.it_interval.tv_sec = 0;
           value2.it_interval.tv_usec = 500000;
           setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
           for(;;)
               ;
       }


        该例子的屏幕拯如下Q?

       localhost:~$ ./timer_test
       process id is 579
       Catch a signal ?SIGVTALRM
       Catch a signal ?SIGALRM
       Catch a signal ?SIGVTALRM
       Catch a signal ?SIGVTALRM
       Catch a signal ?SIGALRM
       Catch a signal –GVTALRM


       注意QLinux信号机制基本上是从Unixpȝ中承过来的。早期Unixpȝ中的信号机制比较单和原始Q后来在实践中暴露出一些问题,因此Q把那些建立在早期机制上的信号叫?不可靠信?Q信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32QSIGRTMAX=63)的信号都是不可靠信号。这是"不可靠信?的来源。它的主要问题是Q进E每ơ处理信号后Q就对信号的响应设|ؓ默认动作。在某些情况下,导致对信号的错误处理;因此Q用户如果不希望q样的操作,那么p在信号处理函数结ֆ一ơ调用signal()Q重新安装该信号?/font> 


allic 2007-04-13 09:39 发表评论
]]>
Linux环境下的Socket~程[学习W记摘]http://www.tkk7.com/huyi2006/articles/110316.htmlallicallicThu, 12 Apr 2007 23:13:00 GMThttp://www.tkk7.com/huyi2006/articles/110316.htmlhttp://www.tkk7.com/huyi2006/comments/110316.htmlhttp://www.tkk7.com/huyi2006/articles/110316.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/110316.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/110316.html什么是Socket
  Socket接口是TCP/IP|络的APIQSocket接口定义了许多函数或例程Q程序员可以用它们来开发TCP/IP|络上的应用E序。要学Internet上的TCP/IP|络~程Q必ȝ解Socket接口?
  Socket接口设计者最先是接口放在Unix操作pȝ里面的。如果了解Unixpȝ的输入和输出的话Q就很容易了解Socket了。网l的Socket数据传输是一U特D的I/OQSocket也是一U文件描q符。Socket也具有一个类g打开文g的函数调用Socket()Q该函数q回一个整型的Socket描述W,随后的连接徏立、数据传输等操作都是通过该Socket实现的。常用的Socketcd有两U:式SocketQSOCK_STREAMQ和数据报式SocketQSOCK_DGRAMQ。流式是一U面向连接的SocketQ针对于面向q接的TCP服务应用Q数据报式Socket是一U无q接的SocketQ对应于无连接的UDP服务应用?

Socket建立
  Z建立SocketQ程序可以调用Socket函数Q该函数q回一个类g文g描述W的句柄。socket函数原型为:
  int socket(int domain, int type, int protocol);
  domain指明所使用的协议族Q通常为PF_INETQ表CZ联网协议族(TCP/IP协议族)Qtype参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAMQSocket接口q定义了原始SocketQSOCK_RAWQ,允许E序使用低层协议Qprotocol通常赋?0"。Socket()调用q回一个整型socket描述W,你可以在后面的调用用它?
  Socket描述W是一个指向内部数据结构的指针Q它指向描述W表入口。调用Socket函数Ӟsocket执行体将建立一个SocketQ实际上"建立一个Socket"意味着Z个Socket数据l构分配存储I间。Socket执行体ؓ你管理描q符表?
  两个|络E序之间的一个网l连接包括五U信息:通信协议、本地协议地址、本C机端口、远端主机地址和远端协议端口。Socket数据l构中包含这五种信息?

Socket配置
  通过socket调用q回一个socket描述W后Q在使用socketq行|络传输以前Q必配|该socket。面向连接的socket客户端通过调用Connect函数在socket数据l构中保存本地和q端信息。无q接socket的客L和服务端以及面向q接socket的服务端通过调用bind函数来配|本C息?
Bind函数socket与本Z的一个端口相兌Q随后你可以在该端口监听服务请求。Bind函数原型为:
  int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
  Sockfd是调用socket函数q回的socket描述W?my_addr是一个指向包含有本机IP地址及端口号{信息的sockaddrcd的指针;addrlen常被讄为sizeof(struct sockaddr)?
  struct sockaddrl构cd是用来保存socket信息的:
  struct sockaddr {
   unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字节的协议地址 */
};
  sa_family一般ؓAF_INETQ代表InternetQTCP/IPQ地址族;sa_data则包含该socket的IP地址和端口号?
  另外q有一U结构类型:
  struct sockaddr_in {
   short int sin_family; /* 地址?*/
   unsigned short int sin_port; /* 端口?*/
   struct in_addr sin_addr; /* IP地址 */
   unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
  };
  q个l构更方便用。sin_zero用来sockaddr_inl构填充Cstruct sockaddr同样的长度,可以用bzero()或memset()函数其|ؓ零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互{换,q意味着如果一个函数所需参数cd是sockaddrӞ你可以在函数调用的时候将一个指向sockaddr_in的指针{换ؓ指向sockaddr的指针;或者相反?
  使用bind函数Ӟ可以用下面的赋值实现自动获得本机IP地址和随取一个没有被占用的端口号Q?
  my_addr.sin_port = 0; /* pȝ随机选择一个未被用的端口?*/
  my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
通过my_addr.sin_port|ؓ0Q函C自动Z选择一个未占用的端口来使用。同P通过my_addr.sin_addr.s_addr|ؓINADDR_ANYQ系l会自动填入本机IP地址?
注意在用bind函数是需要将sin_port和sin_addr转换成ؓ|络字节优先序Q而sin_addr则不需要{换?
  计算机数据存储有两种字节优先序Q高位字节优先和低位字节优先。Internet上数据以高位字节优先序在网l上传输Q所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时需要进行{换,否则׃出现数据不一致?
  下面是几个字节顺序{换函敎ͼ
·htonl()Q把32位gL字节序{换成|络字节?
·htons()Q把16位gL字节序{换成|络字节?
·ntohl()Q把32位g|络字节序{换成L字节?
·ntohs()Q把16位g|络字节序{换成L字节?
  Bind()函数在成功被调用时返?Q出现错误时q回"-1"q将errno|ؓ相应的错误号。需要注意的是,在调用bind函数时一般不要将端口L为小?024的|因ؓ1?024是保留端口号Q你可以选择大于1024中的M一个没有被占用的端口号?

q接建立
  面向q接的客L序用Connect函数来配|socketq与q端服务器徏立一个TCPq接Q其函数原型为:
  int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
Sockfd是socket函数q回的socket描述W;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。Connect函数在出现错误时q回-1Qƈ且设|errno为相应的错误码。进行客LE序设计无须调用bind()Q因U情况下只需知道目的机器的IP地址Q而客户通过哪个端口与服务器建立q接q不需要关心,socket执行体ؓ你的E序自动选择一个未被占用的端口Qƈ通知你的E序数据什么时候到打断口?
  Connect函数启动和远端主机的直接q接。只有面向连接的客户E序使用socket时才需要将此socket与远端主机相q。无q接协议从不建立直接q接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客Lh?
  Listen函数使socket处于被动的监听模式,qؓ该socket建立一个输入数据队列,到辄服务h保存在此队列中,直到E序处理它们?
  int listen(int sockfdQ?int backlog);
Sockfd是Socketpȝ调用q回的socket 描述W;backlog指定在请求队列中允许的最大请求数Q进入的q接h在队列中等待accept()它们Q参考下文)。Backlog寚w列中{待服务的请求的数目q行了限Ӟ大多数系l缺省gؓ20。如果一个服务请求到来时Q输入队列已满,该socket拒l连接请求,客户收C个出错信息?
当出现错误时listen函数q回-1Qƈ|相应的errno错误码?
  accept()函数让服务器接收客户的连接请求。在建立好输入队列后Q服务器p用accept函数Q然后睡眠ƈ{待客户的连接请求?
  int accept(int sockfd, void *addr, int *addrlen);
  sockfd是被监听的socket描述W,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提接请求服务的L的信息(某台L从某个端口发hQ;addrten通常Z个指向gؓsizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数q回-1q置相应的errno倹{?
  首先Q当accept函数监视的socket收到q接hӞsocket执行体将建立一个新的socketQ执行体这个新socket和请求连接进E的地址联系hQ收到服务请求的初始socket仍可以l在以前?socket上监听,同时可以在新的socket描述W上q行数据传输操作?

数据传输
  Send()和recv()q两个函数用于面向连接的socket上进行数据传输?
  Send()函数原型为:
  int send(int sockfd, const void *msg, int len, int flags);
Sockfd是你想用来传输数据的socket描述W;msg是一个指向要发送数据的指针QLen是以字节为单位的数据的长度;flags一般情况下|ؓ0Q关于该参数的用法可参照man手册Q?
  Send()函数q回实际上发送出的字节数Q可能会于你希望发送的数据。在E序中应该将send()的返回gƲ发送的字节数进行比较。当send()q回glen不匹配时Q应该对q种情况q行处理?
char *msg = "Hello!";
int len, bytes_sent;
…?
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
…?
  recv()函数原型为:
  int recv(int sockfd,void *buf,int len,unsigned int flags);
  Sockfd是接受数据的socket描述W;buf 是存放接收数据的~冲区;len是缓冲的长度。Flags也被|ؓ0。Recv()q回实际上接收的字节敎ͼ当出现错误时Q返?1q置相应的errno倹{?
Sendto()和recvfrom()用于在无q接的数据报socket方式下进行数据传输。由于本地socketq没有与q端机器建立q接Q所以在发送数据时应指明目的地址?
sendto()函数原型为:
  int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
  该函数比send()函数多了两个参数Qto表示目地机的IP地址和端口号信息Q而tolen常常被赋gؓsizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时q回-1?
  Recvfrom()函数原型为:
  int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
  from是一个struct sockaddrcd的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()q回Ӟfromlen包含实际存入from中的数据字节数。Recvfrom()函数q回接收到的字节数或当出现错误时q回-1Qƈ|相应的errno?
如果你对数据报socket调用了connect()函数Ӟ你也可以利用send()和recv()q行数据传输Q但该socket仍然是数据报socketQƈ且利用传输层的UDP服务。但在发送或接收数据报时Q内怼自动Z加上目地和源地址信息?

l束传输
  当所有的数据操作l束以后Q你可以调用close()函数来释放该socketQ从而停止在该socket上的M数据操作Q?
close(sockfd);
  你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输l进行。如你可以关闭某socket的写操作而允许l在该socket上接受数据,直至d所有数据?
  int shutdown(int sockfd,int how);
  Sockfd是需要关闭的socket的描q符。参?how允许为shutdown操作选择以下几种方式Q?
  ·0-------不允许l接收数?
  ·1-------不允许l发送数?
·2-------不允许l发送和接收数据Q?
·均ؓ允许则调用close ()
  shutdown在操作成功时q回0Q在出现错误时返?1q置相应errno?

面向q接的Socket实例
  代码实例中的服务器通过socketq接向客L发送字W串"Hello, you are connected!"。只要在服务器上q行该服务器软gQ在客户端运行客戯YӞ客户端就会收到该字符丌Ӏ?
  该服务器软g代码如下Q?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 3333 /*服务器监听端口号 */
#define BACKLOG 10 /* 最大同时连接请求数 */
main()
{
int sockfd,client_fd; /*sock_fdQ监听socketQclient_fdQ数据传输socket */
 struct sockaddr_in my_addr; /* 本机地址信息 */
 struct sockaddr_in remote_addr; /* 客户端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  perror("socket创徏出错Q?); exit(1);
}
my_addr.sin_family=AF_INET;
 my_addr.sin_port=htons(SERVPORT);
 my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
   == -1) {
perror("bind出错Q?);
exit(1);
}
 if (listen(sockfd, BACKLOG) == -1) {
perror("listen出错Q?);
exit(1);
}
while(1) {
  sin_size = sizeof(struct sockaddr_in);
  if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, \
  &sin_size)) == -1) {
perror("accept出错");
continue;
}
  printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
  if (!fork()) { /* 子进E代码段 */
   if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)
   perror("send出错Q?);
close(client_fd);
exit(0);
}
  close(client_fd);
  }
 }
}
  服务器的工作程是这LQ首先调用socket函数创徏一个SocketQ然后调用bind函数其与本机地址以及一个本地端口号l定Q然后调用listen在相应的socket上监听,当accpet接收C个连接服务请求时Q将生成一个新的socket。服务器昄该客h的IP地址Qƈ通过新的socket向客L发送字W串"HelloQyou are connected!"。最后关闭该socket?
  代码实例中的fork()函数生成一个子q程来处理数据传输部分,fork()语句对于子进E返回的gؓ0。所以包含fork函数的if语句是子q程代码部分Q它与if语句后面的父q程代码部分是ƈ发执行的?

客户端程序代码如下:
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100 /*每次最大数据传输量 */
main(int argc, char *argv[]){
 int sockfd, recvbytes;
 char buf[MAXDATASIZE];
 struct hostent *host;
 struct sockaddr_in serv_addr;
 if (argc < 2) {
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
 if((host=gethostbyname(argv[1]))==NULL) {
herror("gethostbyname出错Q?);
exit(1);
}
 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket创徏出错Q?);
exit(1);
}
 serv_addr.sin_family=AF_INET;
 serv_addr.sin_port=htons(SERVPORT);
 serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
 bzero(&(serv_addr.sin_zero),8);
 if (connect(sockfd, (struct sockaddr *)&serv_addr, \
   sizeof(struct sockaddr)) == -1) {
perror("connect出错Q?);
exit(1);
}
 if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {
perror("recv出错Q?);
exit(1);
}
 buf[recvbytes] = '\0';
 printf("Received: %s",buf);
 close(sockfd);
}
  客户端程序首先通过服务器域名获得服务器的IP地址Q然后创Z个socketQ调用connect函数与服务器建立q接Q连接成功之后接收从服务器发送过来的数据Q最后关闭socket?
  函数gethostbyname()是完成域名{换的。由于IP地址难以记忆和读写,所以ؓ了方便,Z常常用域名来表示LQ这需要进行域名和IP地址的{换。函数原型ؓQ?
  struct hostent *gethostbyname(const char *name);
  函数q回为hosten的结构类型,它的定义如下Q?
  struct hostent {
  char *h_name; /* L的官方域?*/
   char **h_aliases; /* 一个以NULLl尾的主机别名数l?*/
   int h_addrtype; /* q回的地址cdQ在Internet环境下ؓAF-INET */
  int h_length; /* 地址的字节长?*/
   char **h_addr_list; /* 一个以0l尾的数l,包含该主机的所有地址*/
  };
  #define h_addr h_addr_list[0] /*在h-addr-list中的W一个地址*/
  ?gethostname()调用成功Ӟq回指向struct hosten的指针,当调用失败时q回-1。当调用gethostbynameӞ你不能用perror()函数来输出错误信息,而应该用herror()函数来输出?

  无连接的客户/服务器程序的在原理上和连接的客户/服务器是一LQ两者的区别在于无连接的客户/服务器中的客户一般不需要徏立连接,而且在发送接收数据时Q需要指定远端机的地址?

d和非d
  d函数在完成其指定的Q务以前不允许E序调用另一个函数。例如,E序执行一个读数据的函数调用时Q在此函数完成读操作以前不会执行下一E序语句。当服务器运行到accept语句Ӟ而没有客戯接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这U情늧为阻塞(blockingQ。而非d操作则可以立卛_成。比如,如果你希望服务器仅仅注意查是否有客户在等待连接,有就接受q接Q否则就l箋做其他事情,则可以通过Socket讄为非d方式来实现。非dsocket在没有客户在{待时就使accept调用立即q回?
  #include <unistd.h>
  #include <fcntl.h>
  …?
sockfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK)Q?
…?
  通过讄socket为非d方式Q可以实?轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socketd数据Ӟ函数立卌回,q回gؓ-1Qƈ|errnogؓEWOULDBLOCK。但是这U?轮询"会CPU处于忙等待方式,从而降低性能Q浪费系l资源。而调用select()会有效地解决q个问题Q它允许你把q程本n挂v来,而同时ɾpȝ内核监听所要求的一l文件描q符的Q何活动,只要认在Q何被监控的文件描q符上出现活动,select()调用返回指C文g描述W已准备好的信息Q从而实CE选出随机的变化,而不必由q程本n对输入进行测试而浪费CPU开销。Select函数原型?
int select(int numfds,fd_set *readfds,fd_set *writefdsQ?
fd_set *exceptfds,struct timeval *timeout);
  其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文g描述W集合。如果你希望定是否可以从标准输入和某个socket描述W读取数据,你只需要将标准输入的文件描q符0和相应的sockdtfd加入到readfds集合中;numfds的值是需要检查的L最高的文g描述W加1Q这个例子中numfds的值应为sockfd+1Q当selectq回Ӟreadfds被修改Q指C某个文件描q符已经准备被读取,你可以通过FD_ISSSET()来测试。ؓ了实现fd_set中对应的文g描述W的讄、复位和试Q它提供了一l宏Q?
  FD_ZERO(fd_set *set)----清除一个文件描q符集;
  FD_SET(int fd,fd_set *set)----一个文件描q符加入文g描述W集中;
  FD_CLR(int fd,fd_set *set)----一个文件描q符从文件描q符集中清除Q?
  FD_ISSET(int fd,fd_set *set)----试判断是否文件描q符被置位?
  Timeout参数是一个指向struct timevalcd的指针,它可以select()在等待timeout长时间后没有文g描述W准备好卌回。struct timeval数据l构为:
  struct timeval {
   int tv_sec; /* seconds */
   int tv_usec; /* microseconds */
};

POP3客户端实?
  下面的代码实例基于POP3的客户协议,与邮件服务器q接q取回指定用户帐L邮g。与邮g服务器交互的命o存储在字W串数组POPMessage中,E序通过一个do-while循环依次发送这些命令?
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define POP3SERVPORT 110
#define MAXDATASIZE 4096

main(int argc, char *argv[]){
int sockfd;
struct hostent *host;
struct sockaddr_in serv_addr;
char *POPMessage[]={
"USER userid\r\n",
"PASS password\r\n",
"STAT\r\n",
"LIST\r\n",
"RETR 1\r\n",
"DELE 1\r\n",
"QUIT\r\n",
NULL
};
int iLength;
int iMsg=0;
int iEnd=0;
char buf[MAXDATASIZE];

if((host=gethostbyname("your.server"))==NULL) {
perror("gethostbyname error");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(POP3SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("connect error");
exit(1);
}

do {
send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
printf("have sent: %s",POPMessage[iMsg]);

iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
iEnd+=iLength;
buf[iEnd]='\0';
printf("received: %s,%d\n",buf,iMsg);

iMsg++;
} while (POPMessage[iMsg]);

close(sockfd);
}

r_4883a52702000h0d.jpg

allic 2007-04-13 07:13 发表评论
]]>
linux软g工程师之基本?/title><link>http://www.tkk7.com/huyi2006/articles/106469.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Mon, 26 Mar 2007 09:30:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/106469.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/106469.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/106469.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/106469.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/106469.html</trackback:ping><description><![CDATA[ <p>Linux软g工程师测?/p> <p>一、基dQ?br />1QGCC <br />用哪个参数可以生obj文gQB<br />(A) -c      (B)-o      (C)-share      (D)-static</p> <p> <br />2、GDB <br />在main函数处设|断点的命o是:A<br />(A)b main      (B)set main      (C)set 0      (D)b 0</p> <p> <br />3、UNP <br />下列函数中可以将L字节序{换成|络字节序的是:D<br />(A)convert()      (B)sprintf()      (C)ntonl()      (D)htonl()</p> <p> <br />4、man <br />查阅"readpȝ调用"man手册的命令是QB<br />(A)man 1 read      (B)man 2 read      (C)man 3 read      (D)man read</p> <p> <br />5、shell <br />删除"/tmp/prj/"目录下(及所有子目录下)所有名字以".o"l尾的文ӞA<br />(A)find /tmp/prj/ -name "*.o" | xargs -i rm {}<br />(B)rm -rf /tmp/prj/*.o<br />(C)find /tmp/prj/ -name "*.o" -rm {} \;<br />(D)find /tmp/prj/*.o -name "*.o" | xargs -i rm {}</p> <p> <br />6、IPC <br />下列哪些属于IPC范畴(多?QA,C,D,E,G,H<br />(A)信号<br />(B)文g监视<br />(C)道<br />(D)消息队列<br />(E)信号?br />(F)odbc<br />(G)׃n内存<br />(H)UNIX域套接字</p> <p> </p> <p>7、Signal <br />下列那个信号不可以被捕获或阻塞:A<br />(A)SIGKILL      (B)SIGINT      (C)SIGCHILD      (D)SIGUSR1</p> <p> </p> <p>8、Thread<br />下列那一Ҏq是错误的:B<br />(A)q程拥有独立的内存空_而线E之间却׃n内存I间?br />(B)q程可以使用libc库,而线E不可以<br />(C)q程和线E在Linux内核中都使用clone()来实?br />(D)信号量也可以作ؓU程间的通讯手段</p> <p> <br />二、C/C++部分Q?br />1、以下ؓHP-UX下的64位应用程序,请写出其q行l果?<br />void func(char *ptr)<br />{<br />        printf("%d\n", sizeof(ptr));<br />}</p> <p>int main()<br />{<br />        char buf[1024];<br />        char *buf_p = buf;</p> <p>        printf("%d\n", sizeof(char));<br />        printf("%d\n", sizeof(int *));<br />        printf("%d\n", sizeof(buf));<br />        func(buf);<br />        func(buf_p);<br />}</p> <p>{案Q?br />1<br />8<br />1024<br />8<br />8</p> <p> </p> <p> <br />2、请分析以下E序Qƈ写出其运行结果?br />char *get_memory(void)<br />{<br />        char p[] = "hello world";</p> <p>        return p;<br />}</p> <p>int main()<br />{<br />        char *str = NULL;</p> <p>        str = get_memory();<br />        printf(str);<br />}</p> <p>{案Q?q是个典型有内存错误的程序,回答出有内存错误对Q最好能回答哪里有错)</p> <p> <br />3、请~写下面的C函数<br />/*<br />功能Q在堆上分配一块指定大的内存Qƈ且全部清0Q如果出错则q回一个空指针<br />lensQ请求分配内存的寸<br />*/<br />void *get_mem(int lens)<br />{<br />        ...<br />}</p> <p> <br />{案Q?考察~程风格和细心程度,不一定要和下面的E序一P注意出错处理和返回值控?<br />void *get_mem(int lens)<br />{<br />        char *ret;<br />        if (lens <= 0)<br />                return NULL;<br />        ret = malloc(lens);<br />        if (ret == NULL)<br />                return NULL;<br />        bzero(ret, lens);<br />        return ret;<br />}</p> <p> </p> <p>4、Makefile<br />假设有一个小型web服务器程序名?miniweb",它的源代码包含三个源文g: utils.c, lib.c, main.c<br />请ؓq个E序~写一个Makefile?/p> <p>{案Q?考察是否会写MakefileQ可能很多h都不会写Q如果此题没回答出来Q面试的时候需要再ơ询问是否会用make和Makefile)<br />一个最基本的例子:<br />miniweb: main.o lib.o utils.o<br />        cc -o $@ $<</p> <p>*.o: *.c<br />        cc -c $<</p> <p>clean:<br />        rm -f *.o rm miniweb<br /></p> <img src ="http://www.tkk7.com/huyi2006/aggbug/106469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2007-03-26 17:30 <a href="http://www.tkk7.com/huyi2006/articles/106469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UNIX常用命ohttp://www.tkk7.com/huyi2006/articles/106371.htmlallicallicMon, 26 Mar 2007 05:07:00 GMThttp://www.tkk7.com/huyi2006/articles/106371.htmlhttp://www.tkk7.com/huyi2006/comments/106371.htmlhttp://www.tkk7.com/huyi2006/articles/106371.html#Feedback0http://www.tkk7.com/huyi2006/comments/commentRss/106371.htmlhttp://www.tkk7.com/huyi2006/services/trackbacks/106371.html1.1 ls


[语法]Q?ls [-RadCxmlnogrtucpFbqisf1] [目录或文?.....]

[说明]Q?ls 命o列出指定目录下的文gQ缺省目录ؓ当前目录 ./Q缺省输出顺序ؓU向按字W顺序排列?br /> 
-R 递归地列出每个子目录的内?/p>

-a 列出所有文Ӟ包括W一个字Wؓ?”的隐藏文g

-d 若后面参数是目录Q则只列出目录名而不列出目录内容Q常?l选项q?br />用以昄目录状态?/p>

-C 输出时多列显C?

-x 横向按字W顺序排?

-m 输出按流式格式横向排列,文g名之间用逗号(Q?分隔

-l 长列表输出,昄文g详细信息Q每行一个文Ӟ从左臛_依次是:

文g存取模式 链接?文g?文gl?文g字节?上次修改旉

其中文g存取模式?0个字母表C,从左臛_的意义如下:

W一个字母表C文件种c,可以是以下几U情况:

d 为目录文?

l 为链?

b 为块文g

c 为字W型文g

p 为命名管道(FIFO)

- 为普通文?

后面9个字母分别表C文件主、同l用戗其他用户对文g的权力,用r表示可读Qw 表示可写Qx 表示可执行。如果是讑֤文gQ则在文件字节数处显C:主设?从设备?

-n ?l选项相同Q只是文件主用数?即UID)昄Q文件组用数?

(即GID)表示

-o ?l选项相同Q只是不昄文gl?

-g ?l选项相同Q只是不昄文g?

-r 逆序排列

-t 按时间顺序排列而非按名?

-u 昄旉时用上ơ访问时间而非上次修改旉

-c 昄旉时用上ơ修改i节点旉而非上次修改旉

-p 若所列文件是目录文gQ则在其后显C斜?/)

-F 在目录文件后?/'Q在可执行文件后?*'

-b 文g名中若有非打印字W,则用八进制显C字符

-q 文g名中的打印字W用'?'表示

-i 昄节点?

-s 昄文g长度时用块长度而非字节长度

-f 后面的参数解释为目录ƈ列出其中的每一?

-1 每行仅列一?

[例子]:

ls 列出当前目录下的文g

ls -al /bin 以长列表的Ş式列出目?/bin 下的所有文Ӟ包括隐藏文g

1.2 pwd
[语法]: pwd

[说明]Q?本命令用于显C当前的工作目录

[例子]:

pwd 昄出当前的工作目录

1.3 cd
[语法]: cd [目录]

[说明]Q本命o用于改变当前的工作目录,无参数时使用环境变量$HOME 作ؓ其参敎ͼ$HOME 一般ؓ注册时进入的路径?

[例子]Q?

cd 回到注册q入时的目录

cd /tmp q入 /tmp 目录

cd ../ q入上目录

1.4 mkdir

 

[语法]: mkdir [-m 模式] [-p] 目录?

[说明]: 本命令用于徏立目录,目录的存取模式由掩码Qumask)军_Q要求对其父目录h写权限,目录的UID和GID为实际UID和GID

-m 按指定存取模式徏立目?

-p 建立目录时徏立其所有不存在的父目录

[例子]:

mkdir tmp 在当前目录下建立子目?tmp

mkdir -m 777 /tmp/abc 用所有用户可d写可执行的存取模?

建立目录 /tmp/aaa Q存取模式参看命?chmod

mkdir -p /tmp/a/b/c 建立目录 /tmp/a/b/c Q若不存在目?/tmp/a

?tmp/a/b 则徏立之

1.5 rmdir

 

[语法]: rmdir [-p] [-s] 目录?

[说明]: 本命令用于删除目?

-p 删除所有已lؓI的父目?

-s 当?p 选项Ӟ出现错误不提C?

[例子]:

rmdir /tmp/abc 删除目录 /tmp/abc

rmdir -p /tmp/a/b/c 删除目录 /tmp/a/b/c Q若目录 /tmp/a /b

?tmp/a I,则删?


1.6 cat

 

[语法]: cat [-u] [-s] [-v[-t] [-e]] 文g...

[说明]: 昄和连接一个或多个文gx准输?

-u 无缓冲的输出(~省为有~冲输出)

-s 对不存在的文件不作提C?

-v 昄出文件中的非打印字符Q控制字W显C成^n Qn为八q制数字Q?

其他非打印字W显C成M-x Q?x 字符?位的8q制数?

-t 在?v 选项Ӟ制表符QtabQ?昄?^IQ将换页W?

QformfeedQ显C成 ^ L

-e 在?v 选项Ӟ在每一行的行尾昄 $

[例子]:

cat file 昄文g

cat -s -v -e file1 file2 file3 逐个昄文g file1 file2 file3

1.7 head

 

[语法]: head [-n] [文g ...]

[说明]: 文件的头n 行显C?~省gؓ 10 行,昄多个文gӞ在每个文件的前面加上 ==> 文g?<==

[例子]Q?

head -9999 file1 file2 昄文g file1 ?file2 的头 9999 ?

 

 

1.8 more

 

[语法]: more [-cdflrsuw] [Q?行数] [+ 行数] [+ / 模式 ] [ 文g ... ]

[说明]: 文件显C在l端上,每次一屏,在左下部昄Q-moreQ-Q若是从文gd而非从管道,则在后面昄癑ֈ比,表示已显C的部分Q按回R键则上滚一行,按空格键则上滚一屏,未显C完时可以用more 命o中的子命令?

-c 昄文g之前先清?

-d 当输错命令时昄错误信息而不是响?bell)

-f 不折叠显C长的行

-l 不将分页控制W?CTRL D)当作늻?

-r 一般情况下Qmore 不显C控制符Q本选项使more 昄控制W,

例如Q将 (CTRL C) 昄?^ C

-s 多个空行{换成一个空行显C?

-u 止产生下划U序?

-w 一般情况下 more 昄完后立即推出Q本选项在显C完后作?

C,敲Q意键后推?

-n 行数 指定每屏昄的行?

+ 行号 从指定行号开始显C?

+/模式 在文件中搜烦指定模式Q从模式出现行的上两行开始显C?文g未显C完Ӟ可以使用more 命o中的子命令,命o中除? ?/ 以外均不回显Q也不用敲回车,当命令破?more 提示行时Q可用退格键恢复提示行。在以下子命令操作中Qi 表示数字Q缺省gؓ 1?

i I格 上滚一屏多 i ?

i 回R 上滚 i ?

i CTRL+D i ~省时上?11 行,否则上滚 i ?

id i ~省时上?11 行,否则上滚 i ?

iz i ~省时上滚一屏,否则定义每屏?i ?

is 跌 i 行后昄一?

if 跌 i 屏后昄一?

i CTRL+B 跛_ i 屏后昄一?

b 跛_ 一屏后昄一?

q ?Q 推出 more

= 昄当前行号

v 从当前行开始编辑当前文件编辑器q境变?

$EDITOR定义

h 昄帮助信息

i / 模式 向前搜烦Q直x式的W?i ơ出?Q?从该行的?两行开始显CZ?

in 向前搜烦Q直至上一模式的第 i ơ出?Q?从该?的上两行开始显CZ?

单引?回到上次搜烦的出发点Q若无搜索则回到开始位|?

! 命o ȀzM个sh L行指定的命o

i Q?n 跛_后面W?i 个文Ӟ若不存在则蟩到最后一个文?

Qf 昄当前文g名和行号

Qq ?QQ 推出 more

. (? 重复上次命o

[ 例子]:

more -c +50 file 清屏后,从第50行开始显C文?file

more -s -w file1 file2 file3 昄文g file1 file2 file3

 

 

1.9 cp

 

[语法]: cp [ -p ] [ -r ] 文g 1 [ 文g 2 ...] 目标

[说明]: 文?(文g2 ...)拯到目标上Q目标不能与文g同名Q?若目标是文g名,则拷贝的文g只能有一个,若目标是目录Q则拯的文件可以有多个Q若目标文g不存在,则徏立这个文Ӟ若存在,则覆盖其以前的内容,若目标是目录Q则文件拷贝到q个目录下?

- i 在覆盖已存在文g时作提示Q若回答 y 则覆盖,其他则中?

- p 不仅拯文g内容Q还有修Ҏ_存取模式Q存取控制表Q?但不拯

UID ?GID

- r 若文件名为目录,则拷贝目录下所有文件及子目录和它们的文Ӟ此时

目标必须为目?

[例子]:

cp file1 file2 文?file1 拯到文?file2

cp file1 file2 /tmp 文?file1 和文?file2 拯到目?/tmp ?

cp -r /tmp /mytmp 目?/tmp 下所有文件及其子目录拯至目?mytmp

 

1.10 mv

 

[语法]: mv [-f] [-i] 文g1 [文g2...] 目标

[说明]: 文件移动至目标Q若目标是文件名Q则相当于文件改?

- i 在覆盖已存在文g时作提示Q若回答 y 则覆盖,其他则中?

- f 覆盖前不作Q何提C?

[例子]:

mv file1 file2 文?file1 改名?file2

mv file1 file2 /tmp 文?file1 和文?file2 Ud到目?/tmp ?

 

 

1.11 rm

 

[语法]: rm [-f] [-i] 文g...

?rm -r [-f] [-i] 目录?.. [文g]

[说明]: 用来删除文g或目?

- f 删除文g时不作提C?

- r 递归地删除目录及其所有子目录

- i 删除文g之前先作提示

[例子]:

rm file1 删除文g file1

rm -i /tmp/* 删除目录 /tmp 下的所有文?

rm -r /mytmp 递归地删除目?/mytmp

 

 

1.12 chmod

 

[语法]: chmod [-R] 模式 文g...

?chmod [ugoa] {+|-|=} [rwxst] 文g...

[说明]: 改变文g的存取模式,存取模式可表CZؓ数字或符号串Q例如:

chmod nnnn file Q?n?-7的数字,意义如下:

4000 q行时可改变UID

2000 q行时可改变GID

1000 |粘着?

0400 文gd?

0200 文gd?

0100 文gd执行

0040 同组用户可读

0020 同组用户可写

0010 同组用户可执?

0004 其他用户可读

0002 其他用户可写

0001 其他用户可执?

nnnn 是上列数字相加得到的,例如 chmod 0777 file 是指文?file 存取权限|ؓ所有用户可d写可执行?

-R 递归地改变所有子目录下所有文件的存取模式

u 文g?

g 同组用户

o 其他用户

a 所有用?

+ 增加后列权限

- 取消后列权限

= |成后列权限

r 可读

w 可写

x 可执?

s q行时可|UID

t q行时可|GID

[例子]:

chmod 0666 file1 file2 文?file1 ?file2 |ؓ所有用户可d?

chmod u+x file Ҏ?file 增加文gd执行权限

chmod o-rwx Ҏ件file 取消其他用户的所有权?

 

 

1.13 chown

 

[语法]: chown [-R] 文g?文g...

[说明]: 文g的UID表示文g的文件主Q文件主可用数字表示Q?也可用一个有效的用户名表C,此命令改变一个文件的UIDQ仅当此文g的文件主或超U用户可使用?

-R 递归地改变所有子目录下所有文件的存取模式

[例子]:

chown mary file 文?file 的文件主改ؓ mary

chown 150 file 文?file 的UID改ؓ150

 

 

1.14 chgrp

 

[语法]: chgrp [-R] 文gl?文g...

[说明]Q?文g的GID表示文g的文件组Q文件组可用数字表示Q也可用一个有效的l名表示Q此命o改变一个文件的GIDQ可参看chown?

-R 递归地改变所有子目录下所有文件的存取模式

[例子]:

chgrp group file 文?file 的文件组改ؓ group

 

 

1.15 cmp

 

[语法]: cmp [-l] [-s] 文g1 文g2

[说明]: 比较两个文gQ若文g1 ?"-" Q则使用标准输入Q?两个文g相同则无提示Q不同则昄出现W一个不同时的字W数和行受?

-l 昄每个不同处的字节?10q制)和不同的字节(8q制)

-s 不作M提示Q只q回?

[例子]:

cmp file1 file2 比较文g file1 ?file2

cmp -l file1 file2 比较文gfile1 ?file2 的每处不?

 

1.16 diff

 

[语法]: diff [-be] 文g1 文g2

[说明]: 本命令比较两个文本文Ӟ不同的行列出来

-b 一串空格或TAB转换成一个空格或TAB

-e 生成一个编辑角本,作ؓex或ed的输入可文?转换成文?

[例子]:

diff file1 file2

diff -b file1 file2

diff -e file1 file2 >edscript

 

 

1.17 wc

 

[语法]: wc [-lwc] 文g...

[说明]: l计文g的行、字、字W数Q若无指定文Ӟ则统计标准输?

-l 只统计行?

-w 只统计字?

-c 只统计字W数

[例子]:

wc -l file1 file2 l计文gfile1和file2 的行?

 

 

1.18 split

 

[语法]: split [-n] [ 文g [名字]]

[说明]: split 指定大文g分解q个文Ӟ每个文g长度为n?n ~省时ؓ1000)Q第一个小文g名ؓ指定的名字后跟aaQ直至zzQ名字缺省gؓxQ若未指定大文g名,则用标准输?

[例子]:

split -500 largefile little

文件largefile ?00行写入一个文ӞW一个文件名为littleaa

 

 

1.19 touch

 

[语法]: touch [-amc] [mmddhhmm[yy]] 文g...

[说明]: 指定文件的讉K旉和修Ҏ间改变,若指定文件不存在则创ZQ若无指定时_则用当前时_q回值是未成功改变时间的文g个数Q包括不存在而又未能创徏的文件?

-a 只改变访问时?

-m 只改变修Ҏ?

-c 若文件不存在Q不创徏它且不作提示

mmddhhmm[yy] 两位表示 月日时分[q]

[例子]:

touch file

更新文gfile的时?

touch 0701000097 HongKong

文件HongKong的时间改?7q?????

 

 

1.20 file

 

[语法]: file [-f 文g名文件] 文g...

[说明]: file Ҏ定文件进行测试,量猜测出文件类型ƈ昄出来

-f 文g名文?文g名文件是一个包含了文g名的文本文gQ?-f 选项试

文g名文件中所列出的文?

[例子]:

file * 昄当前目录下所有文件的cd

 

 

1.21 pack

 

[语法]: pack 文g...

[说明]: pack 指定文件{储ؓ压羃格式Q文件名后加 ".z "Q?文g存取模式Q访问时_修改旉{均不变

[例子]:

pack largefile largefile 压羃后{储ؓlargefile.z

 

 

1.22 pcat 昄压羃文g

 

[语法]: pcat 文g...

[说明]: pcat 昄输出压羃文g

 

[例子]:

pcat largefile.z 昄压羃前的largefile

pcat largefile.z > oldfile 昄压羃前的laregfileQƈ其重定向到

文goldfile?

 

 

1.23 unpack

 

[语法]: unpack 文g...

[说明]: 压~后的文件解压后转储为压~前的格?

[例子]:

unpack largefile.z 压~文件largefile.z解压后{储ؓlargefile

 

 

1.24 find

 

[语法]: find 路径?.. 表达?

[说明]: find 命o递归地遍历指定\径下的每个文件和子目录,看该文g是否能表达式gؓ真,以下 n 代表一个十q制整数Q?n 代表打印 n Q?-n 代表于 n Q下面是合法表达式说明:

-name 模式 文g名与模式匚w则ؓ真,(\ {意符)

-perm [-]八进制数 文g存取模式与八q制数相同则为真若有- 选项Q则文g?

取模式含有八q制数规定模式即为真

-size n[c] 文g块长度ؓ n 则真(一块ؓ512字节)Q若

有c 选项Q则文g字节长度?n 则真

-atime n 若文件的最q访问时间ؓ n 天前则ؓ真,

find 命o改变其讉K的目录的讉K旉

-mtime n 若文件的最q修Ҏ间ؓ n 天前则ؓ?

-ctime n 若文件状态ؓ n 天前改变则ؓ?

-exec 命o { }\; 若命令返回gؓ0则真Q{ }内ؓ命o参数Q?

此命令必M \; 为结?

-ok 命o { }\; ?exec 相同Q只是在命o执行前先提示Q若

回答 y 则执行命?

-print 昄输出使表辑ּ为真的文件名

-newer 文g 若文件的讉K旉比newer 指定的文件新则真

-depth 先下降到搜烦目录的子目录Q然后才臛_自n

-mount 仅查扑֌含指定目录的文gpȝ

-local 文g在当前文件系l时为真

-type c 文gcd?c 则真Qc 取值可?b(块文? c (字符文g)

d(目录) l (W号链接) p (命名道) f (普通文?

\( 表达?\) 表达式ؓ真则?

-links n 文g链接Cؓ n 时ؓ?

-user 用户 当文件属于用h为真Q用户可用数字表CUID

-nouser 当文件不属于 /etc/passwd 中的一个用h为真

-group 文gl?当文件属于文件组时ؓ真,文gl可用数字表CGID

-nogroup 当文件不属于 /etc/group 中的一个组时ؓ?

-fstype cd 当文件所属文件系l类型ؓ指定cd时真

-inum n 当文?i 节点号ؓ n 时ؓ?

-prune 当目录名与模式匹配时Q不再搜索其子目?

可以用逻辑操作W将单表辑ּq接成复杂表辑ּ

逻辑操作W有 ! 表示非操作, -o 表示或操作,两个表达式ƈ列则表示

与操?

[例子]:

find / -name find* -print

从根目录开始搜索文件名?find* 的文件ƈ昄?

find ./ -exec sleep{1}\; -print

每秒昄一个当前目录下的文?

find $HOME \(-name a.out -o -name '*.o' \) -atime +7 -exec rm {} \;

?HOME目录开始搜索,删除所有文件名为a.out ?*.o 且访问时间在7天前的文?

 

 

1.25 grep

 

[语法]: grep [选项] 模式 [文g...]

[说明]: 在指定的文g中搜索模式,q显C所有包含模式的行,模式是一个正规表辑ּQ在使用正规表达式时Q最好将其引在单引号(') 中,若指定文件ؓ~省Q则使用标准输入Q正规表辑ּ可以是:

. 匚wL一个字W?

* 匚w0个或多个*前的字符

^ 匚w行开?

$ 匚w行结?

[] 匚w[ ]中的L一个字W,[]中可?- 表示范围Q?

例如[a-z]表示字母a 至z 中的L一?

\ 转意字符

命o中的选项为:

-b 昄块号

-c 仅显C各指定文g中包含模式的总行?

-i 模式中字母不区分大小?

-h 不将包含模式的文件名昄在该行上

-l 仅显C包含模式的文g?

-n 昄模式所在行的行?

-s 指定文g若不存在或不可读Q不提示错误信息

-v 昄所有不包含模式的行

[例子]:

grep 'good' * 在所有文件中搜烦含有字符?good 的行

grep '^myline' mytext 在文件mytext中搜索行首出现myline字符串的?

 

 

1.26 vi

 

[语法]Qvi [-wn] [-R] 文g...

[说明]: vi 是一个基于行~辑?ex 上的全屏q编辑器Q可以在vi 中?exQed的全部命令,vi选项?-wn 指将~辑H口大小|ؓn行,-R 为将~辑的文件置为只L式, vi 工作模式分ؓ命o模式和输入模式,一般情况下在命令模式下Q可敲入vi命oQ进入输入模式下时可以编辑要~辑的文本,命o a A i I o O c C s S R 可进入输入模式,在输入模式下?ESC 键可推出输入模式Q回到命令模式,在命令模式中敲入Q命令,则可q入ex方式Q在屏幕底部出现提示W?Q?Q此时可使用Lex命oQ屏q底行也用来? ? ! 命o的提CQ大多数命o可以在其前面加数字,表示命o执行的重复次敎ͼ下面单介l一下vi 的命令集Q^ 表示(CTRL)?

^B 退回前一,前面加数字表C重复次敎ͼ每次换页?

保留上一늚两行

^D 在命令模式下Q表CZ滚屏q的一半,在输入模式下Q表C回退?

左边的自动羃q处

^E 昄屏幕底线之下的一?

^F 前进一,前面加数字表C重复次敎ͼ每次换页?

保留上一늚两行

^G 昄当前文g名,当前行号和文件总行敎ͼq用癑ֈ号当前行?

整个文g中的位置

^H(退? 在命令模式下Q光标左UM|在输入模式下Q删d面的字符

^I(TAB) 在输入模式下Q生一串空?

^J(LF) 光标下移一?

^L h屏幕Q即屏q重新显C?

^M(回R) 在命令模式下Q光标移动至下行开?

在输入模式下Q开辟一新行

^N 光标下移一?

^P 光标上移一?

^Q 在输入模式下Q将其后的非打印字符作ؓ正文插入

^R h屏幕

^U 屏幕上滚一半,前面加数字时表示上滚的行敎ͼ此数字对

以后的^D ^U 命o有效

^V 在输入模式下Q将其后的非打印字符作ؓ正文插入

^W 在输入模式下Q光标回退一个字

^Y 昄屏幕底线之上的一?

^Z 暂停~辑Q退回上层Shell

^[(ESC) 退入模式,回到命o模式

! 暂时退出编辑,执行Shell命o

"(双引? 用于标志有名~冲区,~号~冲?-9用于保存被删ȝ正文Q字

母名~冲区a-z供用户存放自定义的正?

$ 光标移动到当前行尾Q前加数字则表示前移行数Q如2$表示Ud

C一行行?

% 光标移动到配对的小括号()或大括号{}上去

( 退回句子开?

) 前移到句子开?

- 退C一行第一个非I格字符

. 重复上一ơ改变缓冲区内容的命?

/ 模式 向前搜烦模式Q将光标Ud到模式出现处Q模式是一个正?

表达式,(参看 grep)

Q?在屏q底部提C:Q其后可使用ex命o

? 功能?/ Q但方向是向前查?

[[ 光标回退臛_一节分界处

\ 转意W?

]] 光标前移臌分界?

^(不是CTRL) 光标U至当前行第一个非I字W上

' q箋两个''表示光标移臛_Ud前的位置Q?后跟字母表示光标?

母标记的行首(参看 m 命o)

A 在行插入正文,q入输入模式

B 光标回退一个字

C 替换光标后的内容

D 删除光标后的内容

E 光标前移到字?

F 字符 在当前行向左查找指定字符

G 光标Ud到其前面数字指定的行Q若未指定则Ud到最后一?

H 光标Ud到屏q顶行,若前面有数字Q则Ud到屏q上该数?

指定的行

I 在行开头插入正?

J q接两行Q若前面有数字则q接数字指定的行

L 光标Ud到屏q底行,若前面有数字Q则Ud到屏q底U往上数?

数字指定的行

M 光标Ud到屏q中U?

N 使用模式查找/?Ӟ重复找下一个匹配的模式Q但方向与上ơ相

反,其功能同 n Q但方向相反

O 在当前行上开辟一新行

P 上ơ被删除的正文插入光标前面,可在其前面加~冲区编P~?

?-9用于保存被删ȝ正文Q字母名~冲区a-z供用户存放自?

义的正文

Q 从vi 推出q入ex命o状?

R 替换字符?

S 替换整行

T 字符 向左查找字符

U 当前行恢复至第一ơ修改前的状?

W 光标U至下一个字?

X 删除光标前的字符

Y 当前行存入无名~冲区,前面加数字表C存入的行数Q也可用?

名缓冲区来保存,以后可用命op或P其取出

ZZ 存盘退出vi

a 光标后插入正?

b 光标回退至上一个字?

cw 替换当前?

c) 替换当前句子

dw 删除一个字

dd 删除一?

e 光标Ud下一个字?

f 字符 在当前行向前查找字符

h 光标左移一?

i 在光标前插入正文

j 光标下移一?

k 光标上移一?

l 光标右移一?

m 字母 用字母标记当前行Q以后可?'字母使光标移动到当前行,

(参看'命o)

n 重复上次 / ?? 命o

o 在当前行下开辟一新行

p 用L冲区内容攑ֈ光标位置(参看P命o)

r 替换当前字符

s 用一串字W替换当前字W?

t 字符 光标Ud臛_W前

u 取消上次操作

w 光标U至下一字首

x 删除当前字符

yw 当前字存入无名~冲区,前面可加"xQ表C存入名字ؓx的有?

~冲?x为a-z)Q也可加数字表示存入的字敎ͼ以后可用P或p?

令取?

yy 当前行存入无名~冲区,用法参看yw

{ 光标Ud臛_一D开?

| 光标U至行首Q若前面加数字,则移到数字指定行的行?

} 光标U至下一D开?

在:提示W下Q常用命令如?

Qw 当前文g存盘

Qw! 强制存盘

Qw 文g 内容写入指定文?

Qw! 文g 强制写入指定文g

QxQy w 文g ?x?y 行写入指定文件中

Qr 文g 文件读到光标位|?

Qr ! 命o 系l命令的输出d光标位置

Qq 退出编?

Qq! 强制退?

Qx 与命令ZZ相同

Qe 文g?~辑另一文g

Qe ! 重新~辑文gQ放弃Q何改?

Qsh 执行shQ结束后回到~辑

Q? 命o 执行命o后回到编?

Qn ~辑下一文g

Qn 文g?重新定义待编辑文件表

Qset 讄 vi 的选项Q例?set nu 表示每行前显CP在选项?

加no则表C清除该选项Q例?set nonu 表示每行前不昄?

P下面是一些常用的选项:

ai 自动~进

aw ~辑下一文g前自动存?

ic 查找字符串时不区分大写

nu 每行前显C?

sm 输入)及}时显CZ之配对的( ?{

slow 插入时gq屏q刷?

ws 使查找能l过文g从头进?

wa 写文件之前不作对文g的检?/p>

allic 2007-03-26 13:07 发表评论
]]>
gcc的?/title><link>http://www.tkk7.com/huyi2006/articles/94880.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 19 Jan 2007 02:56:00 GMT</pubDate><guid>http://www.tkk7.com/huyi2006/articles/94880.html</guid><wfw:comment>http://www.tkk7.com/huyi2006/comments/94880.html</wfw:comment><comments>http://www.tkk7.com/huyi2006/articles/94880.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/huyi2006/comments/commentRss/94880.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/huyi2006/services/trackbacks/94880.html</trackback:ping><description><![CDATA[ <div id="6616166" class="postText">要想L本文Q你需要对C语言有基本的了解Q本文将介绍如何使用gcc~译器。首先,我们介绍如何在命令行方式下用编译器~译单的C源代码。然后,我们要介l一下编译器I竟作了那些工作Q以及如何控制编译过E。我们也要介l了调试器的使用Ҏ?<br /><br />GCC rules <br /><br />你能惌使用闭源代码的U有~译器编译自pY件吗Q你怎么知道~译器在你的可执行文件中加入了什么?可能会加入各U后门和木马。Ken Thompson是一个著名的黑客Q他~写了一个编译器Q当~译器编译自己时Q就?login'E序中留下后门和怹的木马。请?q里 阅读他对q个C的描q。幸q的是,我们有了gcc。当你进?configure; make; make install Ӟ gcc在幕后做了很多繁重的工作。如何才能让gcc为我们工作呢Q我们将开始编写一个纸牌游戏,不过我们只是Z演示~译器的功能Q所以尽可能地精了代码。我们将从头开始一步一步地做,以便理解~译q程Q了解ؓ了制作可执行文g需要做些什么,按什么顺序做。我们将看看如何~译CE序Q以及如何用编译选项让gcc按照我们的要求工作。步骤(以及所用工P如下Q?预编?(gcc -E)Q?~译 (gcc)Q?汇编 (as)Q和 q接 (ld)?<br /><br />开?.. <br /><br />首先Q我们应该知道如何调用编译器。实际上Q这很简单。我们将从那个著名的W一个CE序开始。(各位老前辈,请原谅我Q?<br /><br />Qi nclude <stdio.h></stdio.h><br /><br />int main() <br /><br />{ <br />printf("Hello World! <br />"); <br />} <br /><br />把这个文件保存ؓ game.c?你可以在命o行下~译它: <br /><br />gcc game.c <br /><br />在默认情况下QC~译器将生成一个名?a.out 的可执行文g。你可以键入如下命oq行它: <br /><br />a.out <br />Hello World <br /><br />每一ơ编译程序时Q新?a.out 覆盖原来的E序。你无法知道是哪个程序创Z a.out。我们可以通过使用 -o ~译选项Q告?gcc我们x可执行文件叫什么名字。我们将把这个程序叫?gameQ我们可以用Q何名字,因ؓC没有Java那样的命名限制?<br /><br />gcc -o game game.c <br /><br />game <br />Hello World <br /><br />到现在ؓ止,我们M个有用的E序q差得很q。如果你觉得沮Q你可以想一x们已l编译ƈq行了一个程序。因为我们将一点一点ؓq个E序d功能Q所以我们必M证让它能够运行。似乎每个刚开始学~程的程序员都想一下子~一?000行的E序Q然后一ơ修Ҏ有的错误。没有hQ我是说没有人,能做到这个。你应该先编一个可以运行的程序,修改它,然后再次让它q行。这可以限制你一ơ修改的错误数量。另外,你知道刚才做了哪些修改ɽE序无法q行Q因此你知道应该把注意力攑֜哪里。这可以防止q样的情况出玎ͼ你认Z~写的东西应该能够工作,它也能通过~译Q但它就是不能运行。请切记Q能够通过~译的程序ƈ不意味着它是正确的?<br /><br />下一步ؓ我们的游戏编写一个头文g。头文g把数据类型和函数声明集中C一处。这可以保证数据l构定义的一致性,以便E序的每一部分都能以同L方式看待一切事情?<br /><br />#ifndef DECK_H <br />#define DECK_H <br /><br />#define DECKSIZE 52 <br /><br />typedef struct deck_t <br />{ <br />int card[DECKSIZE]; <br />/* number of cards used */ <br />int dealt; <br />}deck_t; <br /><br />#endif /* DECK_H */ <br /><br />把这个文件保存ؓ deck.h。只能编?.c 文gQ所以我们必M?game.c。在game.c的第2行,写上 Qi nclude "deck.h"。在W?行写?deck_t deck;。ؓ了保证我们没有搞错,把它重新~译一ơ?<br /><br />gcc -o game game.c <br /><br />如果没有错误Q就没有问题。如果编译不能通过Q那么就修改它直到能通过为止?<br /><br />预编?<br /><br />~译器是怎么知道 deck_t cd是什么的呢?因ؓ在预~译期间Q它实际上把"deck.h"文g复制C"game.c"文g中。源代码中的预编译指CZ"#"为前~。你可以通过在gcc后加?-E 选项来调用预~译器?<br /><br />gcc -E -o game_precompile.txt game.c <br />wc -l game_precompile.txt <br />3199 game_precompile.txt <br /><br />几乎?200行的输出Q其中大多数来自 stdio.h 包含文gQ但是如果你查看q个文g的话Q我们的声明也在那里。如果你不用 -o 选项指定输出文g名的话,它就输出到控制台。预~译q程通过完成三个主要dl了代码很大的灵zL?<br /><br />1. ?include"的文件拷贝到要编译的源文件中?<br />2. 用实际值替?define"的文本?<br />3. 在调用宏的地方进行宏替换?<br /><br />q就使你能够在整个源文g中用符号常量(即用DECKSIZE表示一付牌中的U牌数量Q,而符号常量是在一个地方定义的Q如果它的值发生了变化Q所有用符号常量的地方都能自动更新。在实践中,你几乎不需要单独?-E 选项Q而是让它把输Z送给~译器?<br /><br /><br />~译 <br /><br />作ؓ一个中间步骤,gcc把你的代码翻译成汇编语言。它一定要q样做,它必通过分析你的代码搞清楚你I竟惌做什么。如果你犯了语法错误Q它׃告诉你,q样~译失败了。h们有时会把这一步误解ؓ整个q程。但是,实际上还有许多工作要gccd呢?<br /><br />汇编 <br /><br />as 把汇~语a代码转换为目标代码。事实上目标代码q不能在CPU上运行,但它d成已l很q了。编译器选项 -c ?.c 文g转换Z .o 为扩展名的目标文件?如果我们q行 <br /><br />gcc -c game.c <br /><br />我们p动创Z一个名为game.o的文件。这里我们碰C一个重要的问题。我们可以用L一?.c 文g创徏一个目标文件。正如我们在下面所看到的,在连接步骤中我们可以把这些目标文件组合成可执行文件。让我们l箋介绍我们的例子。因为我们正在编写一个纸牌游戏,我们已经把一付牌定义?deck_tQ我们将~写一个洗牌函数。这个函数接受一个指向deckcd的指针,q把一付随机的牌装入deckcd。它使用'drawn' 数组跟踪记录那些牌已l用q了。这个具有DECKSIZE个元素的数组可以防止我们重复使用一张牌?<br /><br />Qi nclude <stdlib.h></stdlib.h><br />Qi nclude <stdio.h></stdio.h><br />Qi nclude <time.h></time.h><br />Qi nclude "deck.h" <br /><br />static time_t seed = 0; <br /><br />void shuffle(deck_t *pdeck) <br />{ <br />/* Keeps track of what numbers have been used */ <br />int drawn[DECKSIZE] = {0}; <br />int i; <br /><br />/* One time initialization of rand */ <br />if(0 == seed) <br />{ <br />seed = time(NULL); <br />srand(seed); <br />} <br />for(i = 0; i < DECKSIZE; i++) <br />{ <br />int value = -1; <br />do <br />{ <br />value = rand() % DECKSIZE; <br />} <br />while(drawn[value] != 0); <br /><br />/* mark value as used */ <br />drawn[value] = 1; <br /><br />/* debug statement */ <br />printf("%i <br />", value); <br />pdeck->card[i] = value; <br />} <br />pdeck->dealt = 0; <br />return; <br />} <br /><br />把这个文件保存ؓ shuffle.c。我们在q个代码中加入了一条调试语句,以便q行Ӟ能输出所产生的牌受这q没有ؓ我们的程序添加功能,但是现在C关键时刻Q我们看看究竟发生了什么。因为我们的游戏q在初阶段Q我们没有别的办法确定我们的函数是否实现了我们要求的功能。用那条printf语句Q我们就能准地知道现在I竟发生了什么,以便在开始下一阶段之前我们知道牌已l洗好了。在我们对它的工作感到满意之后,我们可以把那一行语句从代码中删掉。这U调试程序的技术看h很粗p,但它使用最的语句完成了调试Q务。以后我们再介绍更复杂的调试器?<br />h意两个问题?<br /><br />1. 我们用传址方式传递参敎ͼ你可以从'&'Q取地址Q操作符看出来。这把变量的机器地址传递给了函敎ͼ因此函数自己p改变变量的倹{也可以使用全局变量~写E序Q但是应该尽量少使用全局变量。指针是C的一个重要组成部分,你应该充分地理解它?<br />2. 我们在一个新?.c 文g中用函数调用。操作系lLL名ؓ'main'的函敎ͼq从那里开始执行?shuffle.c 中没?main'函数Q因此不能编译ؓ独立的可执行文g。我们必L它与另一个具?main'函数q调?shuffle'的程序组合v来?<br /><br />q行命o <br /><br />gcc -c shuffle.c <br /><br />q确定它创徏了一个名?shuffle.o 的新文g。编辑game.c文gQ在W?行,?deck_tcd的变?deck 声明之后Q加上下面这一行: <br /><br />shuffle(&deck); <br /><br />现在Q如果我们还象以前一样创建可执行文gQ我们就会得C个错?<br /><br />gcc -o game game.c <br /><br />/tmp/ccmiHnJX.o: In function `main': <br />/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle' <br />collect2: ld returned 1 exit status <br /><br />~译成功了,因ؓ我们的语法是正确的。但是连接步骤却p|了,因ؓ我们没有告诉~译?shuffle'函数在哪里。那么,到底什么是q接Q我们怎样告诉~译器到哪里Lq个函数呢? <br /><br /><br />q接 <br /><br />q接器ldQ用下面的命oQ接受前面由 as 创徏的目标文件ƈ把它转换为可执行文g <br /><br />gcc -o game game.o shuffle.o <br /><br />q将把两个目标文件组合v来ƈ创徏可执行文?game?<br /><br />q接器从shuffle.o目标文g中找?shuffle 函数Qƈ把它包括q可执行文g。目标文件的真正好处在于Q如果我们想再次使用那个函数Q我们所要做的就是包?deck.h" 文gq把 shuffle.o 目标文gq接到新的可执行文g中?<br /><br />象这L代码重用是经常发生的。虽然我们ƈ没有~写前面作ؓ调试语句调用?printf 函数Q连接器却能从我们用 Qi nclude <stdlib.h></stdlib.h>语句包含的文件中扑ֈ它的声明Qƈ把存储在C库(/lib/libc.so.6Q中的目标代码连接进来。这U方式我们可以使用已能正确工作的其他h的函敎ͼ只关心我们所要解决的问题。这是Z么头文g中一般只含有数据和函数声明,而没有函C。一般,你可以ؓq接器创建目标文件或函数库,以便q接q可执行文g。我们的代码可能产生问题Q因为在头文件中我们没有攑օM函数声明。ؓ了确保一切顺利,我们q能做什么呢Q?<br /><br />另外两个重要选项 <br /><br />-Wall 选项可以打开所有类型的语法警告Q以便帮助我们确定代码是正确的,q且可能实现可UL性。当我们使用q个选项~译我们的代码时Q我们将看到下述警告Q?<br /><br />game.c:9: warning: implicit declaration of function `shuffle' <br /><br />q让我们知道q有一些工作要做。我们需要在头文件中加入一行代码,以便告诉~译器有?shuffle 函数的一切,让它可以做必要的查。听h象是一U狡辩,但这样做 可以把函数的定义与实现分d来,使我们能在Q何地方用我们的函数Q只要包含新的头文g q把它连接到我们的目标文件中可以了。下面我们就把这一行加入deck.h中?<br /><br />void shuffle(deck_t *pdeck); <br /><br />q就可以消除那个警告信息了?<br /><br />另一个常用编译器选项是优化选项 -O# (?-O2)?q是告诉~译器你需要什么别的优化。编译器h一整套技巧可以你的代码q行得更快一炏V对于象我们q种程序,你可能注意不到差别,但对于大型程序来_它可以大q度提高q行速度。你会经常碰到它Q所以你应该知道它的意思?<br /><br />调试 <br /><br />我们都知道,代码通过了编译ƈ不意味着它按我们得要求工作了。你可以使用下面的命令验证是否所有的L都被使用?<br /><br />game | sort - n | less <br /><br />q且查有没有遗漏。如果有问题我们该怎么办?我们如何才能深入底层查找错误呢? <br /><br />你可以用调试器查你的代码。大多数发行版都提供著名的调试器Qgdb。如果那些众多的命o行选项让你感到无所适从Q那么你可以使用KDE提供的一个很好的前端工具 KDbg。还有一些其它的前端工具Q它们都很相伹{要开始调试,你可以选择 File->Executable 然后扑ֈ你的 game E序。当你按下F5键或选择 Execution->从菜单运行时Q你可以在另一个窗口中看到输出。怎么回事Q在那个H口中我们什么也看不到。不要担心,KDbg没有出问题。问题在于我们在可执行文件中没有加入M调试信息Q所以KDbg不能告诉我们内部发生了什么。编译器选项 -g 可以把必要的调试信息加入目标文g。你必须用这个选项~译目标文gQ扩展名?oQ,所以命令行成了Q?<br /><br />gcc -g -c shuffle.c game.c <br />gcc -g -o game game.o shuffle.o <br /><br />q就把钩子放入了可执行文Ӟ使gdb和KDbg能指行情c调试是一U很重要的技术,很值得你花旉学习如何使用。调试器帮助E序员的Ҏ是它能在源代码中讄“断点”。现在你可以用右键单击调?shuffle 函数的那行代码,试着讄断点。那一行边上会出现一个红色的圆圈。现在当你按下F5键时Q程序就会在那一行停止执行。按F8可以跛_shuffle函数。呵Q我们现在可以看?shuffle.c 中的代码了!我们可以控制E序一步一步地执行Qƈ看到I竟发生了什么事。如果你把光标暂停在局部变量上Q你能看到变量的内宏V太好了。这比那?printf 语句好多了,是不是? <br /><br /><br />结 <br /><br />本文大体介绍了编译和调试CE序的方法。我们讨Z~译器走q的步骤Q以及ؓ了让~译器做q些工作应该lgcc传递哪些选项。我们简qC有关q接׃n函数库的问题Q最后介l了调试器。真正了解你所从事的工作还需要付多努力,但我希望本文能让你正地h。你可以?gcc?as ?ld?man ?info page中找到更多的信息?<br /><br />自己~写代码可以让你学到更多的东ѝ作为练习你可以以本文的U牌游戏为基Q编写一?1Ҏ戏。那时你可以学学如何使用调试器。用GUI的KDbg开始可以更Ҏ一些。如果你每次只加入一点点功能Q那么很快就能完成。切讎ͼ一定要保持E序一直能q行Q?<br /><br />要想~写一个完整的游戏Q你需要下面这些内容: <br /><br />* 一个纸牌玩家的定义Q即Q你可以把deck_t定义为player_tQ?<br />* 一个给指定玩家发一定数量牌的函数。记住在U牌中要增加“已发牌”的数量Q以便能知道q有那些牌可发。还要记住玩家手中还有多牌?<br />* 一些与用户的交互,问问玩家是否q要另一张牌?<br />* 一个能打印玩家手中的牌的函数?card {于value % 13 Q得Cؓ0?2Q,suit {于 value / 13 Q得Cؓ0?Q?<br />* 一个能定玩家手中的value的函数。Ace的value为零q且可以{于1?1。King的value?2q且可以{于10?<br /><br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=742217</p><br /></div> <img src ="http://www.tkk7.com/huyi2006/aggbug/94880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/huyi2006/" target="_blank">allic</a> 2007-01-19 10:56 <a href="http://www.tkk7.com/huyi2006/articles/94880.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://27simnjingmiguan.com" target="_blank">˾Ʒ˳</a>| <a href="http://3baimm.com" target="_blank">վɫƵѹۿ45</a>| <a href="http://yese889.com" target="_blank">˾޾ƷӰԺ߹ۿ</a>| <a href="http://cuitccol.com" target="_blank">þùӾƷŮ</a>| <a href="http://www77753.com" target="_blank">ŮvƬ</a>| <a href="http://tvnxl.com" target="_blank">޾ƷƷ˿</a>| <a href="http://srztw.com" target="_blank">һɫþ88޾Ʒۺ </a>| <a href="http://hbtelong.com" target="_blank">A߹ۿ</a>| <a href="http://857434.com" target="_blank">츾AVһ˳</a>| <a href="http://sds54.com" target="_blank">߹ۿѻվ</a>| <a href="http://nibayuan.com" target="_blank">ҹ˽ӰԺ</a>| <a href="http://606059.com" target="_blank">av뾫ƷϼӰӰԺ</a>| <a href="http://zaideqin.com" target="_blank">޾ƷƬþò</a>| <a href="http://pecbj.com" target="_blank">AVѹۿ</a>| <a href="http://w6336.com" target="_blank">ˬˬƵѿԿ</a>| <a href="http://h7277.com" target="_blank">ϺɫϺҳa</a>| <a href="http://bjyaao.com" target="_blank">һɫaƬþëƬ</a>| <a href="http://18888kj.com" target="_blank">ձѹۿ</a>| <a href="http://zz1220.com" target="_blank">Ļ</a>| <a href="http://dxj588.com" target="_blank">һ߹ۿ</a>| <a href="http://1ygogo.com" target="_blank">ѻվ߿</a>| <a href="http://lh726.com" target="_blank">޹ƷۺϾþһ</a>| <a href="http://mlhcd.com" target="_blank">mate20pro鶹</a>| <a href="http://zg8k.com" target="_blank">վѹۿ</a>| <a href="http://1777000.com" target="_blank">ɫͼۺ</a>| <a href="http://34pmpm.com" target="_blank">AVAV̾þ</a>| <a href="http://199044.com" target="_blank">Ƶ69</a>| <a href="http://56kaifa.com" target="_blank">պ</a>| <a href="http://guanxianedu.com" target="_blank">þþþþ޾Ʒ</a>| <a href="http://caocl1024liu.com" target="_blank">޳վ</a>| <a href="http://mhysf.com" target="_blank">VAĻһ</a>| <a href="http://26uuyy.com" target="_blank">Ƶ˹Ʒվ </a>| <a href="http://sdkjkj.com" target="_blank">99þ99þþƷѿ</a>| <a href="http://jgc528.com" target="_blank">Ļ</a>| <a href="http://nonobo.com" target="_blank">aŹavۺav</a>| <a href="http://www664660a.com" target="_blank">18Ʒ׽߹ۿ</a>| <a href="http://www9797nn.com" target="_blank">ÿĵӰվһ</a>| <a href="http://55xxb.com" target="_blank">ĻӰӾþþѹۿ</a>| <a href="http://rseav.com" target="_blank">ŮѹۿƬ</a>| <a href="http://xianfeng-motor.com" target="_blank">va߹ۿ</a>| <a href="http://xxxck.com" target="_blank">2019ĻߵӰ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>