信号
- kill函数可以发送信号给pid的进程。
- 目标进程在收到信号时,需要定义一个接收函数来处理之。
- 为一个信号设置处理函数,可以使用signal系统调用。
- 信号也可以作为一个异步事件被复用I/O所监听,则可以统一事件源。
定时器
- Linux可用的定时器方法有三种:
- socket提供的超时选项,是对数据接受与发送的阻塞调用的参数,比如send、 sendmsg、recv、 recvmsg、 accept和connect。根据其返回值与errno来判断是否时间已到。
- SIAGALRM信号,由alarm 和 setitimer 函数设置的实时闹钟一旦超时,将触发 SIGALRM信号。因此,我们可以利用该信号的信号处理函数来处理定时任务。
- I/O 复用的超时参数。
- 存放定时器的容器可以采用时间轮或者时间堆。
多进程
-
创建多进程的系统调用是fork():
- 该函数的每次调用都返回两次,在父进程中返回的是子进程的PID,在子进程中则返回0.该返回值是后续代码判断当前进程是父进程还是子进程的依据。fork调用失败时返回-1并设置ermo。
- 代码完全相同,同时会复制进程的数据(堆数据,栈数据,静态数据)。数据复制采用写时复制。
- 子进程中的文件描述符也是打开状态,并且引用计数增加1。
-
exec()系统调用,替换当前的进程映像,执行给定的可执行文件。如不出错,exec之后的代码不执行。
-
子进程在运行完成后,不会立即退出释放内核资源,因为父进程一般需要跟踪其状态。所以父进程需要等待子进程运行完成。否则如果父进程直接退出,而子进程仍然没有退出,会变成僵尸进程,会占用内核资源。
- wait() 和 waitpid() 系统调用用来等待一个子进程的退出。wait() 阻塞地等待某一个子进程退出。而waitpid可以指定某个pid,并能且option可以设置为非阻塞的。
- 而这种非阻塞的调用,一般会在知道子进程结束的情况下调用较好。子进程结束会发送一个SIGCHLD信号,那么在信号处理函数中去调用wiatpid()则是合理的。
-
进程间通信:
- 管道:管道一般用于父子进程的通信(指的是无名管道),而如果在没有关系的两个进程间通信,需要使用有名管道(FIFO),通过打开一个路径的管道,无关进程间可以找到同一个管道进行读写,必须严格遵循FIFO。
- 信号量:取自然数,有P(wait),V(signal)种操作。Linux信号量的AP都定义在 sys/sem. h头文件中,主要包含3个系统调用; semget创建信号量, semop操作信号量(P,V), semctl(直接控制信号量,乱七八糟的功能…)。
- 共享内存:最高效的进程间通信方式,不涉及进程之间的数据传输,但是必须要保证互斥条件。shmget用于创建新的共享内存。创建之后还不能立即使用,需要shmat将共享内存关联映射到进程地址空间中,而shmdt是分离解除这个映射。shmctl用于控制属性。
- 消息队列:用于在两个进程间传递二进制块数据,不用FIFO,可以有选择地接收。发送时需要指定消息的格式,接收时也可以指定接受的消息格式。
- linux命令ipcs可以查看系统中有哪些进程间通信资源。
多线程
-
Linux下一般使用NPTL线程库,pthread.h。现代linux以及可以实现一对一的线程模型,即一个用户线程对应一个内核线程。
-
专用与线程的同步机制:
- POSIX信号量,semaphore。这个操作起来比上面那个System V的信号量简单一些。
- 互斥锁,pthread_mutex_xxx。类似二进制信号量,加锁和解锁。
- 条件变量,pthread_cond_xxx。
-
进程中的所有线程共享信号,但是每个线程可以单独设置信号掩码。还有就是线程间的信号处理函数是共享的。所以好的办法是单独一个线程负责处理所有信号。
线/进程池
- 动态创建比较耗时;再者会从父进程继承分配的文件描述符、堆内存等。
- 进程池或线程池则是服务器预先创建的一组子进程,最开始运行相同的代码,相同属性,优先级,PGID等。都相对干净,没有父进程留下的描述符或者堆内存。
- 任务来临时,则从池中选取一个为其服务。
- 还需要考虑同一个客户的多个连接是否有上下文关系,是否需要同一个进程来处理。
- 一个服务器只有一个进程池,可以使用单例设计模式。
- 以及给了几个实例。