经验小结

1、程序单独解调一个传真的时候没有问题,但是解调多个传真的时候出现只能解第一个传真不能解后面的,问题在于解调第一个传真的时候某些环境变量已经改变,在解调第二个传真的时候需要吧这些变量重新初始化一遍。
2、接口函数为什么要fork出一个线程?
   因为接口函数的执行尽量不要影响主程序的执行,如果不fork出一个新的进程的话,接口函数就会和主程序共用一个进程,会阻断或者延迟主程序的执行。
3、实现fork一个不影响父进程的子进程的方法:

【例】
//信号处理函数,检测子进程exit的不同值,使之代表不同的意义
static void wait_chld(int signo)
{
¦   pid = waitpid(-1, &status, WNOHANG);
¦   if(WIFEXITED(status))
¦   {
¦   ¦   ret = WEXITSTATUS(status);
¦   }
}

int fax_decode(const char* filename)
{
¦   if( (pid = fork()) < 0)
¦   {
¦   ¦   return -1;
¦   }
¦   //父进程挂起SIGCHLD的信号处理函数,异步等待子进程结束
¦   else if(pid > 0)
¦   {
¦   ¦   if(signal(SIGCHLD,wait_chld) == SIG_ERR)
¦   ¦   {
¦   ¦   ¦   return -2;
¦   ¦   }
¦   ¦   return ret;
¦   }
    //子进程执行fax解码,并且根据不同的返回结果exit不同值
¦   else
¦   {
¦   ¦   ret = convert_to_tif(filename);
¦   ¦   switch(ret)
¦   ¦   {
¦   ¦   ¦   case 0:exit(0);
¦   ¦   ¦   case 1:exit(7);
¦   ¦   }
¦   }
}

4、调用某程序的时候发现程序执行结束后没有命令提示符号,检查发现命令提示符号在前面已经被打印了出来
   可能的情况:父进程调用了子进程,并且在父进程中挂起了一个检查SIGCHLD的信号处理函数,然后退出;在子进程执行完毕的时候会产生SIGCHLD函数,此时系统会捕捉到SIGCHLD信号,然后执行父进程中挂起的信号处理函数,但是父进程在挂起信号处理函数之后就退出了,所以父进程退出之后马上打印出命令提示符,后面的打印出来的信息都是子进程的,所以子进程打印完信息之后没有新的命令提示行出现。

    另外不管什么情况,一般程序运行结束之后如果没有出现命令提示行则敲回车,如果可以得到一个新的命令提示行则说明程序执行没有问题,如果没有出现新的命令提示行说明程序卡死
    
5、
    ps-aux   
         z    僵尸进程
            S    睡眠进程
        s    带有子进程的进程
        +    在后台执行的进程

杀死进程的时候,先杀带有子进程的进程,然后再杀死其子进程

6、
消息队列中,msgrcv函数一直错误返回-1,
ipcs -q 查看现在有几个临时的消息队列(这里的-q就是消息队列,如果不加-q就是消息)
ipcrm -Q key值     杀死指定的消息队列  

如何在代码中杀死消息队列?

设置信号处理函数,捕捉到信号之后执行msgctl
signal(SIGINT,remove_queue);

void remove_queue(int qid)
{
    /* Remove the queue */
    msgctl(qid, IPC_RMID, 0);
}

7、文件描述符和文件指针的相互转换

文件路径 到 文件指针:filepath --fopen()-->FILE*;
文件路径 到 文件描述符:filepath--open()--fd;
文件描述符 到 文件指针:fd--fdopen()-->FILE*;
文件指针 到 文件描述符:FILE*--fileno()--->fd;

8、
替换字母:
    先按R
    再按要替换的字母

9、
换行:
    按O
10、
daemonize守护进程中路径不是当前文件夹的路径

11、
函数名称:     getcwd
函数原型:     char *getcwd(char *dir,int len)
函数功能:     得到当前路径名称
函数返回:     指向dir的指针
参数说明:     len-路径最大长度.dir-路径字符串.
所属文件:     <dir.h>

#include <stdio.h>
#include <dir.h>
int main()
{
    char buffer[MAXPATH];
    getcwd(buffer, MAXPATH);
    printf("The current directory is: %s", buffer);
    return 0;
}     

12、
fork出来的子进程面的东西可以通过printf打印到终端
pthread_create出来的子线程里的东西不能通过printf直接打印到终端

13、
多进程<同步处理>的实现方法:
    在main级别的代码中写回收函数(waitpid),然后在同一级别代码中写循环fork

结构:
main
{
    for(10)
    {
        fork()
        if(pid==0)
        {
            do sth;
        }
        if(pid>0)
        {
            waitpid();  或者 signal(SIGCHLD,SIGIGN);
        }
    }
    waitpid();
}

14、
撤销快捷键:ctrl + z
反撤销快捷键: ctrl + shift + z

15、
创建网络套接字的时候如果创建的是

流式套接字的话,则分为:
    创建socket
    初始化本端并绑定
       listen端口
    建立连接

包式套接字的话,则分为:
    创建socket
    初始化本端并绑定
    listen端口

16、
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

为什么要调用这句话?
    第一次运行程序的时候,某个资源会被内核设置成在用状态,如果不加这句话,则再次运行这个程序的时候,创建socket套接字的时候会返回失败。只有设置成复用状态才可以在第二次打开的时候避免检测到在用状态失败。

例子:
1. 允许一个监听服务器到,bind到现在使用的端口上,即使之前存服务端口的连接存在

    例如: (1)启动服务端口:45001,该端口处于listen状态

          (2)有一个客户端连接到该端口上,可以派生子进程来处理该链路

          (3)关闭监听端口,但是步骤2中的链路存在

          (4)重新启动监听端口45001

     步骤(4)在socket后执行bind的时候会报错“Address already in use”,如果该进程在socket之后和bind之前设置了SO_REUSEADDR,bind将会成功。

17、
关于流式套接字中accept函数的解释:

摘要:对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了。

accept函数等待并接受客户请求:

#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
返回:非负描述字——成功, -1——失败

accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。此时我们需要区分两种套接字,一种套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,一个套接字会从主动连接的套接字变身为一个监听套接字;而accept返回是一个连接套接字,它代表着一个网络已经存在的点点连接。自然要问的是:为什么要有两种套接字?原因很简单,如果使用一个描述字的话,那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的描述字。


参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。

accept返回非负数就是成功了

18、今天解码传真的时候出现了一个问题,第一次并行解码成功,但是从第二次开始就失败,在确认程序逻辑没问题之后分析原因,得到肯定是前一个套接字中遗留的信息对后面的套接字产生了影响,需要对accept进行反操作才能解决这个问题,所以在每一次解码完毕之后执行close(sd),问题解决
流式套接字需要关闭套接字描述符,但是包式套接字好像不用

第二天再看发现父进程和子进程结束的时候都需要close(sd),才能彻底解决问题,只在父进程或者子进程中close(sd)没有作用

19、学会了opendir函数和scandir函数

20、realloc函数可以在不改变首地址的情况下在当前申请内存空间的基础上在申请一个空间

21、网络套接字上传下载文件的时候需要结构体中有两类数据,一类是cmd,一类是data

22、
    在程序中删除文件使用:
    remove(char *)函数

23、
在程序中执行外部命令使用system函数

24、
错误 connection reset by peer
起因 在进行流式套接字send或者recv操作的时候,套接字描述符已经被close掉,在一个无效的sd上执行send和recv出现的这个错误
解决办法 查看send和recv的匹配情况,匹配度没有问题的话就不会出现这个错误

25、
不用数行数的复制和删除方法
: 起始行 , 终止行 , d或者y

26、
进程一章讲过用wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一下,程序实现复杂。
其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
事实上,由于UNIX的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户用sigaction函数自定义的忽略通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证在其它UNIX系统上都可用。请编写程序验证这样做不会产生僵尸进程。

27、
exit(int status)函数能够返回的status取值范围在0-254之间

28、linux应用程序安装
对于应用程序安装方法,Linux和Win完全不一样,win习惯把一个软件安装在一个文件夹里面,而Linux是将一个软件分散安装的不同的文件夹。比如软件的配置文件全部安装在etc这个文件夹里面,执行程序可能按照在/usr下面, 日志文件又会在/var/log下面,都不一样的!你man以下apt看看,有专门的查看命令。 其实你根本不用关心安装在那里,删除的时候用apt删就行了

29、
压缩一个文件夹,并且设置解压缩时的密码
zip -r sshkey.zip sshkey/ --password xxxx

zip -r 文件名 --password 密码

30、listen的监听队列中最多可以存放50个连接请求,第51个之后的连接请求会被直接抛弃
    
31、select函数
    select(最大fd值+1, 含有读文件的fd集合, 含有写文件的fd集合, 含有出错文件的fd集合, 延时);
例子:

    int main()  
    {  
        int sock;  
        FILE *fp;  
        struct fd_set fds;  
        struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0  
        char buffer[256]={0}; //256字节的接收缓冲区  
        /* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开
        sock=socket(...);
        bind(...);
        fp=fopen(...); */  
        while(1)  
        {  
            FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化  
            FD_SET(sock,&fds); //添加描述符  
            FD_SET(fp,&fds); //同上  
            maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1  
            switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用  
            {  
                case -1: exit(-1);break; //select错误,退出程序  
                case 0:break; //再次轮询  
                default:  
                if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据  
                {  
                    recvfrom(sock,buffer,256,.....);//接受网络数据      
                    if(FD_ISSET(fp,&fds)) //测试文件是否可写  
                    fwrite(fp,buffer...);//写入文件  
                    //buffer清空;  
                }// end if break;  
            }// end switch  
        }//end while  
    }//end main  

32、
一个进程默认可以打开1024个文件描述符

33、
向一个对端socket已经关闭了的连接write或者send数据,则会产生SIGPIPE信号,退出当前进程,阻止其退出当前进程的方法是:
使用signal(SIGPIPE, SIG_IGN);忽略SIGPIPE信号, 该信号的默认行为是终止进程.

34、
close 一个NULL指针,也会导致结束进程,应该是触发了某种信号,然后这个信号的默认处理方式就是退出当前进程

35、
系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的 PID号为目录名,它们是读取进程信息的接口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值