linux之多线程fork:进程通信

++++++++++++++++++信号机制+++++++++++++++++++

接收信号

int signal(int sig,__sighandler_t handler);
int func(int sig);

sig 指明了所要处理的信号类型,handler是SIG_IGN,SIG_DFL或者返回值为整数的函数地址。

当执行了signal函数后,进程只要接收到类型为sig 的信号,就立即执行 func()函数,不管其正在执行程序的哪一部分。当func()函数执行结束后,程序返回到进程被中断的那一点继续执行。

忽略中止信号:

#include <signal.h>
int main()
{
    signal(SIGINT,SIG_IGN); /*告诉进程将 SIGINT 信号忽略*/
    cout<<"start"<<endl;
    sleep(3); /*在此期间,无法中止程序运行*/
    cout<<"end"<<endl;
    return 0;
}

捕捉中止信号:

void fun(int sig)
{
    cout<<endl;
    cout<<"ctrl-c"<<endl;
}
int main()
{
    signal(SIGINT,fun); /*告诉进程将 SIGINT 信号忽略*/
    cout<<"start"<<endl;
    sleep(3); /*中止程序运行时,调用fun函数*/
    cout<<"end"<<endl;
    return 0;
}
输出:
start
^C
ctrl-c
end

发送信号

int kill(pid_t pid,int sig);

参数 pid 指定了信号发送的对象进程。

如果 pid 为零,则信号被发送到当前进程所在的进程组的所有进程;

如果 pid 为-1,则信号按进程标识符从高到低的顺序发送给全部的进程;

如果 pid 小于-1,则信号被发送给标识符为 pid 绝对值的进程组里的所有进程。

参数 sig 指定发送的信号类型。它可以是任何有效的信号。

普通用户的进程只能向具有与其相同的用户标识符的进程发送信号。

int n=0;
void fun(int sig)
{
    cout<<n++<<endl;
}
int main()
{
    signal(SIGUSR1,fun);受到信号执行fun函数
    pid_t pid;
    pid=fork();
    if(pid==0)
    {
        pid_t ppid=getppid();
        for(int i=0;i<5;i++){kill(ppid,SIGUSR1);sleep(1);}//发送信号
    }
    else if(pid>0)
    {
        wait(NULL);
    }
    else
    {
        perror("fork Error!");
        exit(1);
    }
    return 0;
}
输出:
0
1
2
3
4

ALARM信号

当进程在alarm()的规定时间内没有完成的话,就会发送一个ALARM信号

void fun(int sig)
{
    cout<<"alarm 5"<<endl;
}
int main()
{
    signal(SIGALRM,fun);
    pid_t pid;
    pid=fork();
    if(pid==0)
    {
        pid_t ppid=getppid();
        alarm(5);//设置定时器,在5秒时将发送超时信号,并终止子进程执行。
        sleep(8);
    }
    else if(pid>0)
    {
        wait(NULL);
    }
    else
    {
        perror("fork Error!");
        exit(1);
    }
    return 0;
}

pause函数

pause等待进程捕捉并处理信号。

void fun(int sig){}
int main()
{
    signal(SIGALRM,fun);
    alarm(1);
    pause();//处于等待状态。
    return 0;
}

++++++++++++++++++管道++++++++++++++++++

实现父子进程间的通信

Pipe函数

Pipe(int fd[2])

fd用于存放 pipe()函数新建立的管道句柄

fd[0]是从管道中读出数据的句柄

fd[1]是向管道写入数据的句柄。即fd[1]的写入由 fd[0]读出。

int main()
{
    int fd[2];
    char buf[100]={'\0'};
    pid_t pid;
    pipe(fd);//建立管道
    pid=fork();//建立子进程
    if(pid>0)
    {
        close(fd[1]);//关闭不需要的句柄
        read(fd[0],buf,100);
        cout<<buf<<endl;
    }
    else if(pid==0)
    {
        close(fd[0]);//关闭不需要的句柄
        strcpy(buf,"hello");
        write(fd[1],buf,100);
    }
    else {exit(1);}
    return 0;
}

dup函数

int dup(int oldfd);

将管道句柄重定向到标准输入/输出上最小的未使用的句柄.

在使用dup函数前最好将原句柄关闭.

int main()
{
    int fd[2];
    char buf[100];
    char tmp[100];
    pid_t pid;
    pipe(fd);
    pid=fork();
    if(pid>0)
    {
        close(fd[1]);
        close(0);//关闭标准输入,0句柄是最小未使用句柄.
        dup(fd[0]);
        cin>>buf;
        cout<<buf<<endl;
    }
    else if(pid==0)
    {
        close(fd[0]);
        close(1);//关闭标准输出,1句柄是最小未使用句柄.
        dup(fd[1]);
        strcpy(tmp,"hello");
        cout<<tmp<<endl;
    }
    else {exit(1);}
    return 0;
}

dup2函数

int dup2(int oldfd,int newfd);

dup2函数相当于

close(0);

dup(fd[0]);

这两条语句.

int main()
{
    int fd[2];
    char buf[100]={'\0'};
    char tmp[100]={'\0'};
    pid_t pid;
    pipe(fd);
    pid=fork();
    if(pid>0)
    {
        close(fd[1]);
        dup2(fd[0],0);
        cin>>buf;//从管道读入
        cout<<buf<<endl;
    }
    else if(pid==0)
    {
        close(fd[0]);
        dup2(fd[1],1);
        strcpy(tmp,"hello");
        cout<<tmp<<endl;//读出到管道
    }
    else {exit(1);}
    return 0;
}

popen()/pclose()函数

FILE *popen(char *command,char *type);

int pclose(FILE *stream);

popen()函数首先调用 pipe()函数建立一个管道,然后用 fork()函数建立一个子进程,运行一个 shell 环境,然后在这个 shell 环境中运行"command"参数指定的程序。数据在管道中流向由"type"参数控制。这个参数可以是"r"或者"w",分别代表读和写。

int main()
{
    FILE *pipe_fp;
    if((pipe_fp = popen("ps -ef","r"))== NULL)//设置一个管道读.
    {
        perror("popen");
        exit(1);
    }
    char buf[100]={'\0'};
    fread(buf,1,99,pipe_fp);//从管道中读.
    cout<<buf<<endl;
    /* 关闭管道 */
    pclose(pipe_fp);
    return(0);
}

++++++++++++++++有名管道++++++++++++++++

解决管道不能提供非父/子关系进程间通信的缺陷.

在读取数据时,若管道中没有数据,有名管道会自动阻塞直到读取到数据为止.

int mknod(char *pathname,mode_t mode,dev_t dev);
pathname:要创建的文件的名称;
mode:文件类型;
dev:该文件对应的设备文件的设备号。只有当文件类型为 S_IFCHR 或 S_IFBLK 的时候该文件才有设备号,创建普通文件时传入0即可。

如下:S_IFIFO表示要创建一个FIFO文件,0666表示该文件的权限是所有人可读可写,0表示该文件不是一个设备文件。

管道读

#include <sys/stat.h>
#include <unistd.h>
int main()
{
    char buf[100]={'\0'};
    mknod("./tmpss",S_IFIFO|0666,0);
    FILE *fp=fopen("./tmpss","r");
    fgets(buf,100,fp);//阻塞等待读取数据
    fclose(fp);
    cout<<buf<<endl;
    return(0);
}

管道写.

int main()
{
    FILE *fp = fopen("./tmpss","w")
    fputs("hello",fp);
    fclose(fp);
    return(0);
}

++++++++++++++++++文件锁++++++++++++++++++

文件和记录锁定可分为咨询式锁定和强制锁定两种。

System V

int lockf(int fd,int function,long size);
function可以是以下取值:
F_ULOCK:为一个先前锁定的区域解锁
F_LOCK:锁定一个区域,如果指定文件的对应区域已被其它进程锁定,那么 lockf 的调用进程将被阻塞直到该区域解锁。
F_TLOCK:测试并锁定一个区域,如果被测试的区域上了锁,lockf便会立即返回-1,出错返回码 errno 将为 EAGAIN.
F_TEST:测试一个区域是否已经上锁。
参数 size 指明了从文件当前位置开始的一段连续锁定区域的长度,当 size 为 0 时,锁定记录将由当前位置一直扩展到文件尾。
#include <unistd.h>
int main()
{
    int fd;
    char buf[2]={'\0'};
    if((fd=open("data",O_RDWR))<=0)
    {
        perror("Can't open");
        exit(1);
    }
    if(lockf(fd,F_LOCK,0)!=-1)//阻塞直到成功加锁
    {
        int i=0;
        while(i++<3)
        {
            lseek(fd,0,0);
            read(fd,buf,1);
            buf[0]+=1;
            cout<<buf<<endl;
            lseek(fd,0,0);
            write(fd,buf,1);
        }
	ockf(fd,F_ULOCK,0);
    }
    close(fd);
    return(0);
}

连续执行两次输出

start
2
3
4
start
5
6
7

如果改为F_TLOCK,则只有第一个进程的输出.第二个进程没有输出.

BSD

int flock(int fd,int operation);
调用 flock 有两个参数:
fd:一个已打开文件的文件描述符;
operation可以是以下取值:
LOCK_SH:共享锁
LOCK_EX:互斥锁
LOCK_UN:解锁
LOCK_NB:当文件已被锁定时不阻塞
#include <sys/file.h>
int main()
{
    int fd;
    char buf[2]={'\0'};
    if((fd=open("data",O_RDWR))<=0)
    {
        perror("Can't open");
        exit(1);
    }
    if(flock(fd,LOCK_EX)!=-1)//设置阻塞性互斥锁
    {
        int i=0;
        while(i++<3)
        {
            lseek(fd,0,0);
            read(fd,buf,1);
            buf[0]+=1;
            cout<<buf<<endl;
            lseek(fd,0,0);
            write(fd,buf,1);
        }
	flock(fd,F_ULOCK);
    }
    close(fd);
    return(0);
}

连续执行两次输出:

start
2
3
4
start
5
6
7

如果是LOCK_EX | LOCK_NB,则只有第一个进程的输出.第二个进程没有输出.

++++++++++++++++消息队列++++++++++++++++

key_t ftok(char *pathname,char proj);

获取一个消息队列的标识符。

pathname和proj可以随意指定,不同的<pathname,proj>产生不同的标识符.收发消息双方应使用相同的<pathname,proj>.

如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。如果两个进程没有任何关系,就用ftok()。

int msgget(key_t key,int msgflg)

创建新的消息队列或获取已有的消息队列。

key:消息队列对象的关键字(key).

msgflg可以是以下取值:

IPC_CREAT:如果消息队列对象不存在,则创建,否则进行打开操作;

IPC_EXCL:和IPC_CREAT一起使用(用”|”连接),保证所得的消息队列对象是新创建的而不是打开已有的对象。

int msgsnd(int msqid,struct msgbuf *msgp,int msgsz,int msgflg)

msqid 是消息队列对象的标识符.

msgp 指向要发送的消息所在的内存.

msgsz 是要发送信息的长度(字节数),可以用公式计算:msgsz = sizeof(struct mymsgbuf)- sizeof(long);

msgflg可以是以下取值:

0,忽略标志位;

IPC_NOWAIT,如果消息队列已满,消息将不被写入队列,立即返回。如果不指定这个参数,线程将被阻塞直到消息被可以被写入。

int msgctl(int msgqid, int cmd, struct msqid_ds *buf)

msgqid:消息队列对象的标识符。

cmd可以是以下取值:

IPC_STAT:取出系统保存的消息队列的 msqid_ds 数据,并将其存入参数 buf 指向的 msqid_ds 结构中。

IPC_SET:设定消息队列的 msqid_ds 数据中的 msg_perm 成员。设定的值由 buf 指向的 msqid_ds结构给出。

IPC_EMID:将队列从系统内核中删除。

发送消息:

#include <sys/msg.h>
struct mymsgbuf {
	long msgtype;
	char msgtext[100]={'\0'};
};
int main()
{
	mymsgbuf buf;
	buf.msgtype=1;
	strcpy(buf.msgtext,"hello");
    key_t key = ftok(".",'a');
    int msgqueue_id = msgget(key, IPC_CREAT|IPC_EXCL|0660);
	msgsnd(msgqueue_id,(mymsgbuf*)&buf,sizeof(buf.msgtext),0);
    return(0);
}

接受消息:

struct mymsgbuf {
    long msgtype;
    char msgtext[100]={'\0'};
};

int main()
{
    mymsgbuf buf;
    key_t key = ftok(".",'a');
    int msgqueue_id = msgget(key, IPC_CREAT|0660);
    msgrcv(msgqueue_id,(mymsgbuf*)&buf,100,1,0);
    msgctl(msgqueue_id, IPC_RMID, 0);
    cout<<buf.msgtext<<endl;
    return(0);
}

输出:

hello

++++++++++++++++++信号量++++++++++++++++++

结构体:

struct sembuf {
	unsigned short sem_num;
	short sem_op;
	short sem_flg;
};

sem_num 成员为接受操作的信号量在信号量数组中的序号(数组下标)。

sem_op 成员定义了进行的操作(可以是正、负和零)。

sem_flg 是控制操作行为的标志。

如果 sem_op 是负值,就从指定的信号量中减去相应的值。

如果 sem_op 是正值,就在指定的信号量中加上相应的值。

如果 sem_op 是零,那么调用 semop()函数的进程就会被阻塞直到对应的信号量值为零。

int semget(key_t key, int nsems, int semflg);

建立新的信号量对象或者获取已有对象的标识符。

nsems:指定新生成的信号量对象中信号量的数目。

key:消息队列对象的关键字(key).

semflg可以是以下取值:

IPC_CREAT:如果消息队列对象不存在,则创建,否则进行打开操作;

IPC_EXCL:和IPC_CREAT一起使用(用”|”连接),保证所得的消息队列对象是新创建的而不是打开已有的对象。

int semop(int semid, struct sembuf *sops, unsigned nsops);

改变信号量对象中各个信号量的状态。

semid:信号量对象的标识符。

sops:sembuf数组,定义semop()函数所要进行的操作序列。

nsops:sops数组的长度。

一个进程创建并使用信号量:

#include <sys/sem.h>
union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

int main()
{
	key_t mykey = ftok(".",'b');
	sembuf sops1{0,-1,0};
	sembuf sops2{0,1,0};
	semun semopts;
	semopts.val = 1;
	int sid = semget(mykey, 1, IPC_CREAT | 0660);
	semctl(sid, 0, SETVAL, semopts);
	semop(sid, &sops1,1);
	sleep(5);
	cout<<"do something"<<endl;
	semop(sid, &sops2,1);
}

另一个进程使用信号量:

int main()
{
    key_t mykey = ftok(".",'b');
    sembuf sops1{0,-1,0};
    sembuf sops2{0,1,0};
    int sid = semget(mykey, 1, IPC_CREAT | 0660);
    semop(sid, &sops1,1);
    cout<<"another do something "<<endl;
    semop(sid, &sops2,1);
    semctl(sid, 0, IPC_RMID, 0);
}

输出:

do something
another do something

++++++++++++++++共享内存++++++++++++++++

int shmget(key_t key, int size, int shmflg);

创建新的或取得已有的共享内存。

key 是共享内存的关键字;

size 是创建的共享内存的大小,以字节为单位。

shmflg 是控制函数行为的标志量,同 msgget()及 semget()函数.

int shmat(int shmid, char *shmaddr, int shmflg);

将shmget()函数得到的共享内存,映射到进程自己的内存空间内。

第一个参数是共享内存的标识符。

第二个参数 shmaddr 指定了共享内存映射的地址。

shmflg:

SHM_RND 标志来强迫将内存大小设定为页面的尺寸。

SHM_RDONLY 共享内存将被映射成只读。

int shmctl(int shmqid, int cmd, struct shmid_ds *buf);

cmd:

PC_STAT 获得共享内存的信息。

IPC_SET 设定共享内存的信息。

IPC_RMID 删除共享内存。

int shmdt(char *shmaddr);

断开共享内存映射

一个进程发送数据

#include <sys/shm.h>
int main()
{
	key_t mykey = ftok(".",'b');
	int shmid = shmget(mykey, 100, IPC_CREAT|IPC_EXCL|0666);
	char *segptr = (char *)shmat(shmid, 0, 0);
	strcpy(segptr, "hello");
}

一个进程读取数据

int main()
{
    key_t mykey = ftok(".",'b');
    int shmid = shmget(mykey, 100, 0);
    char *segptr = (char *)shmat(shmid, 0, 0);
    cout<<segptr<<endl;
    shmctl(shmid, IPC_RMID, 0);
}

输出:

hello

linux之多线程fork:进程通信

时间: 2024-10-21 21:43:11

linux之多线程fork:进程通信的相关文章

linux之多进程fork:进程通信

++++++++++++++++++信号机制+++++++++++++++++++ 接收信号 int signal(int sig,__sighandler_t handler); int func(int sig); sig 指明了所要处理的信号类型,handler是SIG_IGN,SIG_DFL或者返回值为整数的函数地址. 当运行了signal函数后,进程仅仅要接收到类型为sig 的信号,就马上运行 func()函数,无论其正在运行程序的哪一部分.当func()函数运行结束后,程序返回到进程

Linux进阶系列 1 --- 进程通信

进程都是运行在物理内存上 linux 进程中通信方式 1.无名管道 (无文件名,适合亲缘进程通信) pipe() 函数实现 write  read  IO操作函数  以文件方式来读取,写入操作数据 因为没有文件名,无法调用open()函数打开文件 2 有名管道 (有文件名) 3.消息队列(网状通信) ipcs 命令可查看消息队列 msgsend  msgrecv 4.共享内存 5.信号量 视频地址:https://www.bilibili.com/video/BV1fE411v7Bb?p=18

linux应用程序开发-进程通信(IPC)

IPC why: 1.数据传输 2.资源共享 目的: 3.通知事件 4.进程控制 发展: 1.UNIX进程间通信 2.基于SYStem V 3.POSIX 方式分类: 1.pipe(管道) FIFO(有名管道) 2.signal 3.消息队列 4.共享内存 5.信号量 6.套接字(socket) 管道通信:单向,先进先出 创建 pipe:父子之间通讯 FIFO:任意 创建: int pipe(int filedis[2]); 管道创建时,产生两个文件描述符:filedis[0]读管道,filed

【Linux程序设计】之进程间的通信

这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的. 实验题目:Linux环境下的进程间通信 实验目的:熟悉进程通信中信号概念及信号处理:掌握进程间的管道通信编程:了解进程间的内存共享编程. 实验内容: 一.信号 设计程序,满足如下要求: 1.编程程序:每隔1秒显示“running….”一次,显示8次后,程序结束.应用函数alarm,在程序开始运行5秒后发送信号SIGALRM,并实现:1)程序接收到SIGALRM信号就被终止:2)自定义信号处理函数,在程序接

Linux间的进程通信;以及子进程的创建

1 "-----第六天-----------------------------------------------------------------------------" 2 3 1.版本控制:svn/git; 4 5 2.进程的概念: 6 1)程序和进程: 7 每个进程操作系统会为它分配 0-4G 的虚拟内存空间(32位操作系统): 其中0-3G为用户内存空间,进程可以对它进行读写操作: 3G - 4G 为系统内核空间,进程没有读写权限. 8 进程只能读写用户空间,没有权限读

Linux系统编程@进程通信(一)

进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统的一个分支) POSIX进程间通信(POSIX:可移植操作系统接口,为了提高UNIX环境下应用程序的可移植性.很多其他系统也支持POSIX标准(如:DEC OpenVMS和Windows).) 现在Linux使用的进程间通信方式包括: 管道(pipe).有名管道(FIFO) 信号(signal) 消

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication) 进程是操作系统实现程序独占系统运行的假象的方法,是对处理器.主存.I/O设备的抽象表示.每个进程都是一个独立的资源管理单元,每个进程所看到的是自己独占使用系统的假象,因此各个进程之间是不能够直接的访问对方进程的资源的,不同的进程之间进行信息交互需要借助操作系统提供的特殊的进程通信机制. 进程之间的通信,从物理上分,可以分为同主机的进程之间的通信和不同主机间的进程之间的通信.从通信内容方式上分,可以分为数据交互.同

Linux笔记--Linux进程通信

Linux进程间通信 文章来源: http://www.cnblogs.com/linshui91/archive/2010/09/29/1838770.html 一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到.C.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).D.

Linux系统编程札记:进程通信(一) &nbsp; &nbsp;

进程简单来讲就是一个程序的一次执行,这里说的进程一般都指的是运行在用户态的进程,而处于用户态的不同进程之间是彼此相互隔离的,它们必须通过某种方式来进行通信,具体理由如下: (1)数据传输:有时候一个进程需要将它的数据发送给另一个进程. (2)资源共享:有时候多个进程之间需要共享同样的资源. (3)通知事件:有时候一个进程需要向另一个或一组进程发送消息,通知它们发生了某个事件. (4)进程控制:有些进程希望能够完全控制另一个进程的执行,此时控制进程希望能够拦截另一进程的所有操作,并能够及时知道它的