进程间协作、同步

来源:互联网 发布:mac梦幻西游文件夹 编辑:程序博客网 时间:2024/05/20 04:50

进程和进程之间无非是通过磁盘、内核、用户空间传输数据。

通过磁盘(也就是文件)实现进程通信这个好理解,服务器进程把计算结果写入文件,客户端进程从文件读数据就可以了。这里的竞争条件是当服务端正在写文件时,客户端是不允许读的。

命名管道把数据写入文件,因此它可以独立于进程存在。但是命名管道是一个队列而非常规的文件,当读者把数据读走后,数据就不存在了,下一次读到的是后面的内容。

普通管道位于内核,用于父子进程间通信,因此它的存在依赖于进程的存在。

进程间通过文件或FIFO传输数据时,write将数据从内存复制到内核缓冲区,read将数据从内核缓冲区复制到内存。而使用共享内存是不存在用户空间和内核空间的来回复制的。共享内存段是用户内存的一部分,不同的进程都拥有指向此内存段的指针。进程依靠访问权限来对这段内存进行读或写。

上面已提到通过文件实现进程间通信时需要对进程进行同步,用到文件锁

下面的代码服务端负责向文件写入当前时间,客户端读取该文件。

服务端:

#include<sys/file.h>#include<stdio.h>#include<fcntl.h>#include<time.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/types.h>#define oops(m,x) {\    perror(m);    \    exit(x);    \}main(int argc,char *argv[]){    int fd;    time_t now;    char* message;    if(argc!=2){        fprintf(stderr,"usage:%s filename\n",argv[0]);        exit(1);    }    if((fd=open(argv[1],O_CREAT|O_TRUNC|O_WRONLY,0644))==0)        oops(argv[1],2);    void lock_opreation(int,int);    while(1){        time(&now);        message=ctime(&now);        if(lseek(fd,0,SEEK_SET)==-1)            oops("lseek",3);        lock_operation(fd,F_WRLCK);        if(write(fd,message,strlen(message))==-1)            oops("write",4);        lock_operation(fd,F_UNLCK);        sleep(1);    }}lock_operation(int fd,int op){    struct flock lock;    lock.l_whence=SEEK_SET;    lock.l_start=0;    lock.l_len=0;    lock.l_pid=getpid();    lock.l_type=op;    if(fcntl(fd,F_SETLKW,&lock)==-1)        oops("locl operation",6);}

客户端:

#include<sys/file.h>#include<stdio.h>#include<fcntl.h>#include<time.h>#include<stdlib.h>#include<string.h>#define oops(m,x) {perror(m);exit(x);}#define BUFSIZE 50main(int argc,char *argv[]){    int fd,nread;    char buf[BUFSIZE];    if(argc!=2){        fprintf(stderr,"usage:%s filename\n",argv[0]);        exit(1);    }    if((fd=open(argv[1],O_RDONLY))==-1)        oops(argv[1],2);    while(1){        lock_operation(fd,F_RDLCK);     //读锁        nread=read(fd,buf,BUFSIZE);            lock_operation(fd,F_UNLCK); //解除读锁        if(nread>0){            printf("pid %d: %s",getpid(),buf);            bzero(buf,BUFSIZE);        }        lseek(fd,0,SEEK_SET);        sleep(3);    }    close(fd);}lock_operation(int fd,int op){    struct flock lock;    lock.l_whence=SEEK_SET;    lock.l_start=0;     //锁住文件的起始位置    lock.l_len=0;       //锁住的长度。0表示到文件结尾    lock.l_pid=getpid();    //哪个进程可能操作该文件    lock.l_type=op;     //锁的类型。读、写or解锁    if(fcntl(fd,F_SETLKW,&lock)==-1)    //通过fcntl给文件设置锁        oops("lock operation",6);}

使用共享内存的多个进程之间通过信号量来协同工作。信号量是一个内核变量,它是系统级的全局变量,可以被系统中的任何进程所访问。

下面的代码是服务端向共享内存写入当前时间:

#include<stdio.h>#include<sys/shm.h>#include<time.h>#include<sys/types.h>#include<stdlib.h>#include<string.h>#include<sys/sem.h>#include<signal.h>#define TIME_MEM_KEY 99#define TIME_SEM_KEY 9900#define SEG_SIZE ((size_t)100)#define oops(m,x) {perror(m);exit(1);}union semnum{    int val;    struct semid_ds *buf;        //信号量控制结构体    ushort *array;};int seg_id,semset_id;void clearup(int);main(){    char *mem_ptr,*ctime();        //变量和函数放在一起声明    time_t now;    int n;    seg_id=shmget(TIME_MEM_KEY,SEG_SIZE,IPC_CREAT|0777);    //共享内存有自己单独的读写权限0777    if(seg_id==-1)        oops("shmget",1);    mem_ptr=shmat(seg_id,NULL,0);    if(mem_ptr==(void*)-1)        oops("shmat",2);    semset_id=semget(TIME_SEM_KEY,2,0666|IPC_CREAT|IPC_EXCL);   //产生一个信号量集合,容量为2    if(semset_id==-1)        oops("semget",3);    set_sem_value(semset_id,0,0);       //设置第一个信号量的值    set_sem_value(semset_id,1,0);       //设置第二个信号量的值    signal(SIGINT,clearup);     //SIGINT信号到来时做一些清理工作    for(n=0;n<60;n++){        time(&now);        printf("\tshm_ts2 waiting for lock\n");        wait_and_lock(semset_id);       //锁住共享内存        printf("\tshm_ts2 updating memeory\n");        strcpy(mem_ptr,ctime(&now));    //向共享内存写入数据        release_lock(semset_id);        //释放信号量        printf("\tshm_ts2 released lock\n");        sleep(1);    }    clearup(0);}void clearup(int n){    shmctl(seg_id,IPC_RMID,NULL);       //删除共享内存    semctl(semset_id,0,IPC_RMID,NULL);  //删除信号量}set_sem_value(int semset_id,int semnum,int val){    union semnum initval;    initval.val=val;    if(semctl(semset_id,semnum,SETVAL,initval)==-1)        oops("semctl",4);}wait_and_lock(int semset_id){    struct sembuf actions[2];        //对信号量进行制作的结构体    actions[0].sem_num=0;       //操作第1个信号量    actions[0].sem_flg=SEM_UNDO;    //进程结束时,进程对信号量的操作将被撤销(信号量属于IPC对象,IPC对象是独立于进程的)    actions[0].sem_op=0;        //阻塞直到信号量变为0    actions[1].sem_num=1;    actions[1].sem_flg=SEM_UNDO;    actions[1].sem_op=+1;       //信号量的值加1,表示要释放该信号控制的资源    if(semop(semset_id,actions,2)==-1)        oops("semop:locking",10);}release_lock(int semset_id){    struct sembuf actions[1];    actions[0].sem_num=1;    actions[0].sem_flg=SEM_UNDO;        actions[0].sem_op=-1;        //如果当前信号量的值小于1,则阻塞。否则减去1。表示要获得由该信号控制的资源    if(semop(semset_id,actions,1)==-1)        oops("semop:unlocking",10);}

管道和socket也包含了锁机制。管道和socket其实也是保存数据的内存段,它将数据从源端复制到目的端。不同的是管道和socket中的锁是由内核,而不是进程来管理的。

原创粉丝点击