C语言服务器编程必备常识

入门

  • 包含了正确的头文件只能编译经过,没连接正确的库连接会报错。
  • 一些经常使用的库gcc会自动连接。
  • 库的缺省路径/lib /usr/lib /usr/local/lib
  • 不知道某个函数在那个库能够nm -o /lib *.so | grep 函数名
  • man sin 会列出包含的头文件和连接的库名。man 2 sin 2表示系统调用,3表示c库函数
  • 一旦子进程被建立,父子进程一块儿从fork处被建立。建立子进程为了争夺资源。
  • 重定向用dup2函数
  • kill -l查看信号种类
  • pthread_mutex不跨进程,ipc中的信号量跨进程,但linux不支持无名信号量。信号灯的主要用途是保护临界资源。多进程访问共享内存,用信号量同步。
  • alarm(5)5秒后向本身发送SIGALARM信号,缺省处理是结束进程,不自定义就会结束进程。经过对信号集加减信号,肯定信号屏蔽字。在信号处理程序被调用时,操做系统创建的新信号屏蔽字包括正在被递送的信号,若是此时这个信号再次发生,将阻塞到前一个处理完,屡次发生不排队只处理一次。 sa_mask会被加到信号屏蔽字中。
  • netstat -an|grep A |grep ESTABLISHED | grep B,查看ip为A的服务器是否在端口B创建了链接
  • 因为咱们的链接都是常链接,故能够按照客户端与服务器端创建的链接端口进行判断。
  • IP协议是网络层协议,主要发送数据包。UDP基于IP协议,用在传输层。TCP协议创建在IP协议之上,可靠的、按顺序发送的。
  • TCP链接三次握手:客户机向服务器发包。服务器给客户机回包。客户机收到包,向服务器发送确认信息完成链接。服务器收到确认信息也完成链接。
  • ioctl能够控制全部文件描述符的状况。
  • 循环服务器:UDP服务器,UDP是非面向链接的,没有一个客户机能够总是占着服务器。
  • TCP循环服务器一次只能处理一个,close后才能处理下一个。
  • TCP并发服务器:fork子进程来处理。建立子进程消耗资源。
  • 并发服务器:多路IO复用。
  • 当咱们建立一个正常的TCP套接字的时候,咱们只处理内容,不负责TCP头部和ip头部,本身建立头部使用setsockopt。
  • 网络程序通常是多进程加上多线程。
  • g++参数-pg产生gprof性能信息,gprof好像是g++自带的
  • (gdb)make使你能不退出gdb就能产生就从新产生可执行文件 , shell 不退出gdb就执行shell
  • file a.out能够在gdb模式下载入程序。
  • 服务器端:socket->bind->listen【创建监听队列,监听socket】->【返回链接socket】accept【从监听队列取请求,成功时返回新的socket,与传入的第一参数不一样,标识被接受的这个链接】
  • 客户端:socket->connect

    进阶

  • linux中代替数据库管理文件可能会出现文件数超过linux可管理数的问题。
  • tcpip协议族:上层协议使用下层协议提供的服务。
  • 应用层的东西最后须要在内核中实现,会须要应用空间和内核空间的切换。
  • IP数据太长要分片。IP协议的核心是数据报路由。路由表、跳转、自动更新。
  • socket含义ip:port。称其为socket地址。
  • 字节序是按字节考虑的,和位无关。cpu累加器一次装载4字节(32位机)。
  • 网络上传的必定是大端字节序,各主机按自身状况转化。java虚拟机采用大端字节序。
  • 不一样输入调用两次函数,若是发现后面结果覆盖前面结果,说明函数不可重入。函数内部若是用静态变量存储结果,就不可重入。
  • 将一个地址和socket绑定称为给socket命名。0-1023端口普通用户不能使用,有默认用途。
  • accept只是从监听队列中取出链接,不论链接处于何种状态。connect(fd,..)一旦链接创建成功,fd就惟一标识了这个链接,客户端就能够读写fd和服务器通讯。
  • 对socket执行close只减小链接数,fork会使引用数加1。不管如何都要终止链接用shutdown。
  • read和write一样适用于socket。用于TCP数据流的是send recv。UDP数据是recvfrom sendto。
  • 没有进程读管道的时候【例如close(fd[0])了】还往管道写数据将引起SIGPIPE。
  • 把STDOUT_FILENO关闭,dup(链接socket),这时dup返回最小可用描述符1【返回的文件描述符和原有描述符指向相同文件】,此时printf回返回给客户端,而不是打印在屏上。
  • sendfile将真实文件传给socket。splice用于在两个文件描述符间移动数据,零拷贝,用于socket和管道之间互相定向。tee用于两个管道之间复制数据。
  • IO处理单元是一个专门的接入服务器,它实现负载均衡。请求队列是系统内部各单元之间通讯方式的抽象,通常实现为池。
  • 阻塞和非阻塞是对文件描述符而言的。非阻塞IO通常和IO通知机制一块儿使用,如IO复用或SIGIO信号。IO复用自己是阻塞的,提升效率是由于同时监听多个事件。
  • 同步就是协同步调,按预约的前后次序进行运行。处理客户链接就是读写描述符,就是IO,因此IO单元被定义为接入服务器。
  • 并发不适用于计算密集型,由于任务切换会下降效率,适用于IO密集型,如常常读写文件、访问数据库。
  • 池就是预先静态分配资源,到时能够快速使用。避免了对内核的频繁访问。提高性能方法:池、避免数据复制、上下文切换【线程数大于cpu数时】和锁。
  • 读写锁能够减小锁的粒度适用于读多写少的状况。
  • epoll须要使用一个额外的描述符维护事件表。 EPOLLONESHOT确保只有一个线程处理某个socket。
  • sigaction结构体中的sa_mask设置信号掩码,确切的说是在进程原有信号掩码的基础上增长信号掩码,以指定哪些信号不能发送给本进程。
  • sigset_t 每一个元素的每一个位表示一个信号,因此相同的信号只能表示一次。
  • 子进程有和父进程相同的信号掩码,但挂起信号集【发送可是被阻塞的信号】为空,就是说阻塞的信号是不可能发给子进程的。
  • 应用程序使用信号集sigset_t前,应调用sigemptyset或sigfillset一次,不然信号集初始状态不清楚。
  • 最小时间堆能够达到定时器的效果。
  • waitpid用NOHANG就是非阻塞的了。socketpair建立全双工管道的系统调用。
  • 对信号量的操做成为P(传递,进入临界区)V(释放,退出临界区)。最简单的二进制信号量,只有0和1.用一个普通变量模拟是不行的,由于检测和减1没法原子完成。
  • linux上的线程使用clone系统调用建立的进程模拟的。
  • 目前能够实现跨进程的线程同步
  • 被pthread_cancel的线程能够决定是否容许被取消以及如何取消。
  • 销毁一个已经加锁的互斥量将致使不可知的后果。互斥量属性设置中能够设置跨进程共享互斥量。pthread_mutexattr_setpshared()
  • 加锁-》pthread_cond_wait暗含解锁,确保能检测到条件变量的任何变化。
  • 有些函数不可重入主要是由于内部使用了静态变量。
  • 多线程程序中的一个线程调用fork,只复制调fork的那个线程。互斥量的状态也继承,此时容易出现死锁。
  • 全部线程共享信号处理函数,共享进程的信号。因此须要专门线程处理全部信号。
  • 进程池:典型的是3-10个。线程池中的线程数量应该和cpu数量差很少。通讯【通讯:传递数据】父子进程间可使用管道,多线程间使用一个全局数据便可。
  • pthread_create当线程函数是类的成员函数时,必须为静态函数【确保没对象时也可使用】,因为静态成员函数只能访问静态成员,要访问动态成员须要函数内部用单例或将类的对象做为参数传给函数。
  • SA_RESTART被信号中断的系统调用再信号处理结束后继续执行。
  • 将线程池或进程池中个数减小为1,便于调试逻辑。而后逐步增长数量,看同步。
  • 进程类[i].fd 经过给不一样i的fd传递数据,调用不一样的进程工做。m_sub_process[i].pid=fork()【fork了maxnum次】。
  • 线程池:线程函数一块儿都启动,启动后进入while(!stop)循环,不断的锁队列,取任务。

    POSIX线程

  • 只有互斥量的主人可以解锁它。
  • 线程的堆栈受限。
  • 线程结束方式要么从线程函数return,要么调用pthread_exit,进入终止态,直到被分离或被链接。建立不须要链接的线程应该使用detachstate属性创建线程使其自动分离。
  • pthread_join会阻塞调用者,直到被join的线程结束,join返回被链接的线程也分离,因此只能被join一次,下一次就错误了。
  • 使用条件变量时必须保证若是有线程等待,则该线程等待后必然会收到信号(if/while)
  • 条件变量可使线程处于等待状态而不消耗资源。条件变量必须跟一个互斥变量一块儿使用,由于条件变量就是共享的全局数据??【条件和锁结合共同保护共享数据】status = pthread_cond_wait(&alarm_cond, &alarm_mutex);
  • 没有条件变量,程序员可用使用轮询某个变量来实现停等-通知同步,可是很是消耗系统资源。
  • 若是肯定线程不须要被Join, 则申明为Detached能够节省系统资源
  • pthread_self得到自身的ID,只能经过ID操做线程。main是主线程,主线程中止全部线程也中止,main中调用pthread_exit,这样进程就必须等待全部线程结束才能终止。
  • 经过向pthread_t(ID)=pthread_create传递线程函数地址和函数参数来建立线程。注意当前线程从pthread_create返回前,新建立的线程可能已经运行完毕了。
  • 舀水桶相似一个互斥量:桶用来保护“舀水”临界区【访问临界资源(共享数据?)的那段程序是临界区】。或者将桶理解为:用来确保一次只能由一我的舀水的不变量。
  • 在访问共享数据的代码段周围加锁互斥量,则一次只能有一个线程进入该代码段。
  • pthread_mutex_t表示互斥量,不能拷贝,能够拷贝指针。
  • 当调用pthread_mutex_lock时,若是互斥量已经被锁住,线程将被阻塞。调用pthread_mutex_trylock时不会阻塞,会返回EBASY,能够作其余的事情去。
  • 互斥量的本质是串行执行。
  • 解决死锁的两种方法:一,规定加锁顺序;二,trylock若是不行回退,解锁全部已加锁的互斥量
  • sched_yield()将处理器交给另外一个等待处理的线程,若是没有等待处理的线程。当即返回。sleep()能够确保其余线程有机会运行。
  • 按照相反的顺序解锁,有助于减小线程作回退操做的可能。由于同一个线程函数中加锁顺序是同样的。对于不一样的线程函数顺序应该不重要
  • 线程运行于解锁和阻塞之间时,其余线程才能改变共享数据状态。此时共享状态的改变,本线程是没法知道的。->须要条件变量。队列满,队列空,满空就是条件变量。
  • 动态初始化的条件变量须要pthread_cond_destroy来释放。静态初始化的没必要释放。释放前确保其余线程不使用他。
  • 在阻塞线程以前,条件变量等待操做pthread_cond_wait将解锁互斥量,从新返回线程以前,会再次锁住互斥量。子线程只在pthread_cond_wait等待的短期内能够加锁,修改共享数据,而后解锁。
  • pthread_cond_timedwait的意思就是我在这里等time时间,若是时间内条件变量变了,或者不变,我都要跳出while(谓词)的循环,按状况处理。
  • pthread_cond_wait和pthread_cond_signal必须同时发生才能成功。
  • 互斥量:条件变量是 一对多的关系
  • 当线程调用pthread_create时,她所能看到的内存值也是它创建的线程能看到的,以后的线程不必定能看到。
  • 线程解锁互斥量时所看到的的数据,也能被后来直接锁住相同互斥量的线程看到。解锁后写入的数据,没必要被其余线程看见(由于那不是用来同步的数据,不必全部人看见,同步就应该加锁)。
  • 线程终止,取消,从启动函数返回,pthread_exit时看到的数据,可以被链接该线程的其余线程看到。终止后写入的数据不会被看到。
  • 线程发信号或广播时看到的内存数据,能够被唤醒线程看到。以后写入的不会。
  • 线程分配的堆栈和堆空间是私有的,除非传给其余线程指针。register(寄存器变量)和auto变量(大部分变量默认auto)(static变量的生命期长)中的数据可随时读取,像在同步程序中同样
  • 两个处理器将各自的高速缓存中的数据写入主存的顺序是不必定的,即便写到相应高速缓存的顺序有前后之分。同一线程写数据也未必按照顺序刷新进内存,这使得其余线程读取结果不对。
  • 锁住互斥量->内存屏障->内存屏障->解锁互斥量
  • 使用线程的方式:流水线、工做组(工做线程在数据的不一样部分操做)、C/S。
  • pthread_attr_setdetachstate (&_attr, PTHREAD_CREATE_DETACHED);说明在建立线程后,咱们不在须要使用线程ID。
  • 不变量(Invariant):程序所作的一些假设,特别是指变量之间的关系。断定条件(Predicates):描述不变量状态的逻辑表达式。
  • pthread_kill(thdid, SIGTERM)给特定线程发信号
相关文章
相关标签/搜索