操作系统具有管理进程,进程调度的能力。 很多时候我们需要在同一时间干不同的任务,这就需要我们通过多进程或者多线程来进行,在我们学习和工作中我们大部分用到的都是多线程,本文主要是在linux下探索c语言的多进程的使用方法
1.进程相关重要API
(1)创建进程fork()
(2)退出进程exit()
(3)等待子进程
(4)处理子进程退出状态值的宏
2.进程间通信
进程间的通信(IPC) 方式,总归起来主要有如下这些:
(1)无名管道(PIPE) 和有名管道(FIFO)。
(2)信号(signal)
(3)system V-IPC之共享内存。
(4)system V-IPC之消息队列。
(5)system V-IPC之信号量。
(6)套接字。
2.1.无名管道(PIPE)
示例:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
int main()
{
int pipefd[2];
char buf1[1024];
char buf2[1024];
if(pipe(pipefd)==-1){
perror("pipe failed\n");
return -1;
}
pid_t son=fork();
if(son==0){
printf("子进程,ID为%d\n",getpid());
while(1){
memset(buf1,0,1024);
scanf("%s",buf1);
write(pipefd[1],buf1,1024);
if(strcmp(buf1,"exit")==0){
break;
}
}
}
else if(son >0){
printf("父进程,ID为%d\n",getpid());
while(1){
memset(buf2,0,1024);
read(pipefd[0],buf2,1024);
printf("from son:%s\n",buf2);
if(strcmp(buf2,"exit")==0){
break;
}
}
wait(NULL);
}
else if(son==0){
perror("fork failed\n");
}
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
2.2.有名管道(FIFO)
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#define FIFO "/home/xmr/fifo1"
int main()
{
if(mkfifo(FIFO,0777)==-1){
perror("mkfifo failed\n");
}
int fd=open(FIFO,O_RDWR);
if(fd<0){
perror("open failed\n");
}
char buf1[1024],buf2[1024];
pid_t son=fork();
if(son>0){
printf("父进程,ID为%d\n",getpid());
while(1){
memset(buf2,0,1024);
read(fd,buf2,1024);
if(strlen(buf2)>0){
printf("from son:%s\n",buf2);
if(strcmp(buf2,"exit")==0){
break;
}
}
}
wait(NULL);
}
else if(son==0){
printf("子进程,ID为%d\n",getpid());
while(1){
memset(buf1,0,1024);
scanf("%s",buf1);
write(fd,buf1,1024);
if(strcmp(buf1,"exit")==0){
break;
}
}
}
else if(son<0){
perror("fork failed\n");
}
close(fd);
return 0;
}
2.3.信号
在下表中,SIGKILL和SIGSTOP是两个特殊的信号,不能被忽略、阻塞和捕捉,只能执行缺省动作
(1)向指定进程发送信号
(2)捕捉一个特定信号
(3)自己给自己发送一个指定信号
(4)将本进程挂起,直至收到一个信号
(5)信号集操作函数
(6)阻塞或者解除阻塞一个或多个信号
(7)给某进程发送一个指定的信号,同时携带一些数据
(8)捕捉一个指定信号,且可以通过扩展响应函数来获取信号携带的额外数据
2.4.共享内存
(1)获取共享内存ID
(2)对共享内存进行映射,或者解除映射
(3)获取或者设置共享内存的相关属性
(4)示例:
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#define SCHEME_SIZE 1024
int main()
{
//获取KEY值
key_t key=ftok("./",1);
//创建共享内存
int shm_id=shmget(key,SCHEME_SIZE,IPC_CREAT | 0777);
pid_t son=fork();
if(son==0){
printf("子进程,ID为%d\n",getpid());
char *buf=(char *)shmat(shm_id,NULL,0);
while(1){
scanf("%s",buf);
if(strcmp(buf,"exit")==0){
break;
}
}
shmdt(buf);
shmctl(shm_id,IPC_RMID,NULL);
}
else if(son>0){
printf("父进程,ID为%d\n",getpid());
char *buf=(char *)shmat(shm_id,NULL,0);
while(1){
if(strlen(buf)>0){
printf("from son:%s\n",buf);
if(strcmp(buf,"exit")==0){
break;
}
}
sleep(1);
}
shmdt(buf);
shmctl(shm_id,IPC_RMID,NULL);
wait(NULL);
}
else if(son<0){
perror("fork failed\n");
shmctl(shm_id,IPC_RMID,NULL);
}
return 0;
}
2.5.消息队列
(1)获取一个当前未用的IPC的key
(2)获取消息队列ID
(3)发送和接受消息
(4)设置或者获取消息队列的相关属性
(5)示例:
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//定义消息结构体
typedef struct _msg{
long mtype; //接收者类型
char mtext[100];//消息内容,可以有多个成员
}MSG;
long msg_type=10;
int main()
{
//获取KEY值
key_t key=ftok("./",1);
//创建消息队列
int msg_id=msgget(key,IPC_CREAT | 0777);
pid_t son=fork();
if(son==0){
printf("子进程,ID为%d\n",getpid());
while(1){
MSG new_msg;
new_msg.mtype=msg_type;
scanf("%s",new_msg.mtext);
msgsnd(msg_id,&new_msg,sizeof(MSG)-sizeof(long),0);
if(strcmp(new_msg.mtext,"exit")==0){
break;
}
}
}
else if(son>0){
printf("父进程,ID为%d\n",getpid());
while(1){
MSG new_msg;
msgrcv(msg_id,&new_msg,sizeof(MSG)-sizeof(long),msg_type,0);
printf("from son:%s\n",new_msg.mtext);
if(strcmp(new_msg.mtext,"exit")==0){
break;
}
}
wait(NULL);
}
else if(son<0){
perror("fork failed\n");
}
return 0;
}
2.6.信号量
(1)获取信号量ID
(2)对信号量进行P/V操作,或者等零操作
信号量操作结构体的定义如下:
struct sembuf
{
unsigned short sem_num; /*信号量元素序号(数组下标)*/
short sem_op;/*操作参数*/
short sem_flg;/*操作选项*/
};
请注意:信号量元素的序号从0开始,实际上就是数组下标。
根据sem_op的数值,信号量操作分成3种情况:
1)当sem_op大于0时:进行V操作,即信号量元素的值(semval)将会被加上sem_op 的值。如果SEM_UNDO被设置了,那么该V操作将会被系统记录。V操作永远不会导致进程阻塞。
2)当sem_op等于0时:进行等零操作,如果此时semval恰好为0,则semop()立即成功返回,否则如果IPC_NOWAIT被设置,则立即出错返回并将errno设置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:
1.semval变为0。
2.信号量被删除。(将导致semop()出错退出,错误码为EIDRM)
3.收到信号。(将导致semop()出错退出,错误码为EINTR)
3)当sem_op小于0时:进行P操作,即信号量元素的值(semval)将会被减去sem_op 的绝对值。如果semval大于或等于sem_op的绝对值,则semop()立即成功返回,semval的值将减去sem_op的绝对值,并且如果SEM_UNDO被设置了,那么该P操作将会被系统记录。如果semval小于sem_ op的绝对值并且设置了IPC_NOWAIT,那么semop()将会出错返回且将错误码置为EAGAIN,否则将使得进程进入睡眠,直到以下情况发生:
1.semval的值变得大于或者等于sem_op的绝对值。
2.信号量被删除。(将导致semop()出错退出,错误码为EIDRM)
3.收到信号。(将导致semop()出错退出,错误码为EINTR)
(3)获取或者设置信号量的相关属性
2.7.套接字
服务端
void init_server()
{
struct sockaddr_un addr_server, addr_client;
int addrlen = sizeof(struct sockaddr_un);
server_fd = socket( AF_UNIX, SOCK_STREAM, 0);
if(server_fd<0)
return;
unlink("/var/run/unixsock");
memset( &addr_server, 0, sizeof( addr_server ) );
addr_server.sun_family = AF_UNIX;
strncpy( addr_server.sun_path, "/var/run/unixsock", sizeof( addr_server.sun_path ) - 1 );
if(bind( server_fd, ( struct sockaddr * )&addr_server, addrlen ) < 0)
return;
if(listen( server_fd, 10 ) < 0)
return;
}
void recv_data()
{
int clientfd;
struct sockaddr_un addrclient;
int addrlen = sizeof(struct sockaddr_un);
char buf[1024]={0};
int readlen;
bool flag;
while(1)
{
clientfd=accept( server_fd, ( struct sockaddr * )&addrclient,(socklen_t *)&addrlen );
if(clientfd<0)
return;
flag=true;
while(flag)
{
readlen=read(clientfd,buf,sizeof(buf));
if(readlen<=0)
break;
qDebug().noquote()<<QString(buf);
memset(buf,0,sizeof (buf));
}
close(clientfd);
}
}
客户端
void init_client(void)
{
struct stat buf;
if(access(FILE_CLIENT,F_OK) < 0){
printf("%s is not access\n",FILE_CLIENT);
return;
}
if(stat(FILE_CLIENT,&buf) != 0){
return;
}
if(!S_ISSOCK(buf.st_mode)){
printf("%s is not a socket file\n",FILE_CLIENT);
return;
}
client_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(client_fd < 0){
printf("create socket failed\n");
return;
}
struct sockaddr_un addr_server;
int addrlen = sizeof(struct sockaddr_un);
memset( &addr_server, 0, sizeof( addr_server ) );
addr_server.sun_family = AF_UNIX;
strncpy( addr_server.sun_path, FILE_CLIENT, sizeof( addr_server.sun_path ) - 1 );
if(connect( client_fd, ( struct sockaddr * )&addr_server, addrlen ) < 0){
printf("connect failed\n");
return;
}
client_connect_flag = 1;
}