C 多进程

参考:https://blog.csdn.net/wucz122140729/article/details/105113379

在liunx中一些与进程相关的命令:

ps 查看当前终端的进程

ps -ef 查看系统的全部进程

ps -ef |grep test查看系统的全部进程并且包含test字符的记录

在上面的查询结果中各个字段的意义:UID :启动进程的操作系统用户。PID :进程编号。PPID :进程的父进程的编号。C :CPU使用的资源百分比。STIME :进程启动时间。CMD :执行的是什么指令。

在C语言中用getpid()库函数来获取当前程序运行时的进程编号。

如:

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main()

{

printf("本程序的进程编号是:%d\n",getpid());

}

在C的代码中开启新的进程,进行多进程编程,主要是使用fork()函数,这个函数执行后会开启一个新的子进程,该子进程会复制本进程在执行fork()函数前的所有数据。调用fork()函数后,后面的代码就会有两个进程分别来执行,就是说后面的代码会被执行两次,彼此之间互不干扰。

fork()函数的返回值,是一个整数。在父进程中返回值是子进程的编号,在子进程中返回值是0。所以在后面运行的代码中就可以通过判断fork()函数的返回值来判断此次运行是父进程还是子进程。

如:

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main()

{

printf("本程序的进程编号是:%d\n",getpid());

int ipid=fork();

printf("p,ipid);

if (ipid!=0) printf("父进程编号是:%d\n",getpid());

else printf("子进程编号是:%d\n",getpid());

}

多进程的应用。我们可以利用多线程将之前的那个socket简单的聊天通讯做成多线程的,即可以允许多个客户端来连接我的服务器端。客户端的代码不用修改,主要是修改服务器端的代码。

基本思想是,我们利用主线程来专门监听来连接的客户端,当监听到有一个客户端连接上来后就开启一个子线程来与该连接上来的客户端进行通讯。主线程则继续监听客户端的连接。这样就为每一个连接上来的客户端开辟了一个子线程来进行通信,就不会出现线程的堵塞。

服务器端加上多线程后的代码:

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <netdb.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

int main()

{

// signal(SIGCHLD,SIG_IGN); // 忽略子进程退出的信号,避免产生僵尸进程

// 第1步:创建服务端的socket。

int listenfd = socket(AF_INET,SOCK_STREAM,0);

// 第2步:把服务端用于通信的地址和端口绑定到socket上。

struct sockaddr_in servaddr; // 服务端地址信息的数据结构。

memset(&servaddr,0,sizeof(servaddr));

servaddr.sin_family = AF_INET; // 协议族,在socket编程中只能是AF_INET。

servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。

//servaddr.sin_addr.s_addr = inet_addr("118.89.50.198"); // 指定ip地址。

servaddr.sin_port = htons(5051); // 指定通信端口。

if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )

{ perror("bind"); close(listenfd); return -1; }

// 第3步:把socket设置为监听模式。

if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }

// 第4步:接受客户端的连接。

while(1){

int clientfd; // 客户端的socket。

int socklen=sizeof(struct sockaddr_in); // struct sockaddr_in的大小

struct sockaddr_in clientaddr; // 客户端的地址信息。

clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);

printf("客户端(%s)已连接。\n",inet_ntoa(clientaddr.sin_addr));

if(fork()>0) {continue;close(clientfd); } // 父进程回到while,继续Accept。因为父进程不需要用clientfd与客户端进行通信

close(listenfd);//这里关闭掉监听的socket,因为子进程会复制父进程中所有的数据,而监听的socket再子进程中是没有用的,所以这里关闭掉释放资源

// 第5步:与客户端通信,接收客户端发过来的报文后,回复ok。

char buffer[1024];

while (1)

{

memset(buffer,0,sizeof(buffer));

if (recv(clientfd,buffer,sizeof(buffer),0)<=0) break; // 接收客户端的请求报文。

printf("接收:%s\n",buffer);

memset(buffer,0,sizeof(buffer));

printf("发送:");

scanf("%s",buffer);

if (send(clientfd,buffer,strlen(buffer),0)<=0) break; // 向客户端发送响应结果。

}

printf("客户端已断开连接。\n");

return 0; // 或者exit(0),子进程退出。

}

}

加上signal(SIGCHLD,SIG_IGN); 这一句的作用是为了清理掉僵尸进程,僵尸进程就是子进程再return或者exit之后,如果父进程还在执行。实际上是没有完全将子进程所占用的资源清理掉的。加上这句代码后可以清除掉这些僵尸进程。