C语言基础知识(3):多进程

        操作系统具有管理进程,进程调度的能力。 很多时候我们需要在同一时间干不同的任务,这就需要我们通过多进程或者多线程来进行,在我们学习和工作中我们大部分用到的都是多线程,本文主要是在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;
}


 

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值