多进程编程的一些回忆:
1.进程的创建
最常用fork();
pid_t pid;
pid=fork();
if(pid==0){//pid=0表示该进程为子进程
}
else if(pid>0){//否则为父进程
}
else{//创建进程失败
}
2.进程的回收
回收对象:孤儿进程、僵尸进程
进程
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
//等待回收子进程
pid_t waitpid(pid_t pid,int * status,int options);
//waitpid会暂时停止目前进程的执行,直到有信号来到或子进程结束。
//pid为等待回收进程的Id
//status存储子进程结束后返回的状态
//options 有两个宏
//WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
//WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。
多进程开发示例
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
#include<stdlib.h>
#define SERV_PORT 8000
//捕捉的SIGCHLD信号,然后对其进行回收,当进程结束后会向其父进程发送信号,等待回收
void do_sigchild(int num)
{
while (waitpid(0, NULL, WNOHANG) > 0);
}
int main(void)
{
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_addr_len;
int listen_fd, conn_fd;
pid_t pid;
char str[INET_ADDRSTRLEN];
//初始化ser_addr,ser_addr用于保存服务器端的套接字信息
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
//用于监听的套接字
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
// setsockopt函数目的是使得当端口调用close后,依然可以重新使用该端口
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
//将serv_addr和listen_fd进行绑定
bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
//监听。最多可连接20个客户端
listen(listen_fd, 20);
while (1)
{
//cli_addr用于保存连接的客户端信息
cli_addr_len = sizeof(cli_addr);
conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
//父进程会一直阻塞在accept函数等待客户端连接,但是当有子进程退出时,
//父进程将会去进行执行信号处理函数,执行完成后并不会继续阻塞,而是往下继续子执行,且此时
//accpt将返回-1,所以用while(conn_fd==-1)让父进程继续等待客户端。
while(conn_fd==-1)
{
conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
};
// 有新的连接则创建一个进程
pid = fork();
if (pid == 0)
{
//打印客户端信息
printf("%s with port %d connected\n",
inet_ntop(AF_INET, &cli_addr.sin_addr, str, sizeof(str)),
ntohs(cli_addr.sin_port));
//关闭监听端口
close(listen_fd);
//用于交换信息的缓冲区
char buf[1024];
while (1)
{
int n=0;
memset(buf,0,1024);
n = read(conn_fd, buf, sizeof(buf));
if (n==0)
{
printf("client has been closed.\n");
break;
}
else if(n<0)
{
printf("read err\n");
break;
}
//打印客户端端口信息
memset(str,0,INET_ADDRSTRLEN);
printf("received massage from %s with port %d\n",
inet_ntop(AF_INET, &cli_addr.sin_addr, str, sizeof(str)),
ntohs(cli_addr.sin_port));
printf("Massage read:%s\n",buf);
//缓冲区,并写入信息
char buf1[256]="Hello,this is server end";
//发送信息到服务器端
write(conn_fd, buf1, sizeof(buf1));
}
close(conn_fd);
return 0;
}
//父进程负责回收子进程
else if (pid > 0)
{
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = do_sigchild;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, NULL);
close(conn_fd);
}
else
{
printf("fork() error\n");
exit(1);
}
}
return 0;
}