进程与线程及通信总结

来源:互联网 发布:淘宝店铺怎么优化标题 编辑:程序博客网 时间:2024/05/01 21:10
进程与线程及通信总结
一:
进程,程序与线程的区别:
程序:是静态的,存储在磁盘上,是指令与数据的有序集合。
进程:是动态的,一个程序执行一次就是一个进程,包括创建,执行,调度与消亡。
用fork()函数创建进程时,实现的是对程序的拷贝。不同的进程之间有各自的空间。
线程:是轻量级的进程,与进程的不同点是,不同纯程之间,共用进程相同的地址空间。
程序包括用户数据段(静态数据,全局变量),正文段。而进程还包括系统数据段(函数局部变量,形参)
二:
进程类型:
1,交互进程(shell)可以在前台与后台运行。
2,批处理进程:不属于某个终端,它被提交到一个队列中以便顺序执行。
3,守护进程:在后台进行。一般在开启时启动,在结束时结束。
     创建守护进程的过程:
a,创建一个子进程,结束父进程,(为了使子进程成为孤独进程,从而在后台运行)
b,利用setsid()函数创建一个新的的会话(使子进程成为新的会话组长,使它完全独立出来,从而脱离所有其他进程的控制)一个    
            会话与一个终端相连。
这里需要理解,进程组(多个进程的集合)与会话(会话包括多个进程组),终端(一级用于交互的调和如输入与输出),
c,用chdir("pathname")改变当前的路径一般改为"/","/tmp"下两者的区别是权限不一样。
d,设置文件限的掩码umask(0);
e,关闭所有的文件,getdtablesize()可以获得文件的数量。
********************************
三:
进程的运行状态:
1,运行态(running与runnable)
2,等待态(可中断与不可中断)
3,停止态(进程被中止,例断点的设置)
4,死亡态
它们之间的相互转换是一个重点。  
**********************************
四:
进程的执行模式:
用户模式与内核模式
五:
_exit(),exit(),pthread_exit(),return()的区别:
return()用来返回调用自己的函数而不是进程。
_exit(),exit()是用来结束一个进程的而_exit()不清理I/o缓冲,而exit()则清理I/o缓冲,它们需要的头文件也不相同,
return()与exit()在主函数中区别不大。
pthread_exit()只能用于线程。

线程: 
1,线程相对于进程的优点与缺点:
优点:大大提高的任务切换的效率(线程之间共享相同的地址空间)
           容易进行通信(通过全局变量实现数据的共享与交换)
           避免了额外的TLB&cache的刷新。(同上)
缺点:需要引出同步与互斥机制。
2,各个线程之间也有各自的私有空间(堆栈线程ID错误号,优先级,执行状态和属性)
3,同步与互斥的操作:
初始化,
p操作
v操作。
同步:是多个任务按照约定的顺序相互配合完成一件事情。
互斥:需要理解临界资源(只能被一个任务访问)与临界区(访问临界资源的代码)
********************************************************
信号量:它是一各资源,数量(非负整数)的大小代表资源的个数
它与信号是不同
********************************************************
具体的函数可以man一下
int sem_init(int *sem,int pshared,unsigned value)
int sem_wait(int *sem)
int sem_post(int *sem)
4,***************************************************************
在用 gcc 编译线程程序时需要加入纯种库 ,例:gcc -o t t.c -lpthread
******************************************************************
进程之间的通信:
传统的进程之间的通信:
无名管道(pipe),
有名管道(fifo),
信号(signal)
system v IPC对象间通信:
共享内存(share memory),
消息队列(message queue),
信号灯集(semaphore与信号量相同)
BCD:
套接字(socket)
I)
1,无名管道
特点:
a只能用于亲缘关系的进程通信
b半双工通信
c不能用lseek()定位,它只能用于常规文件。
2,一些概念:
读端:用于读管道的文件描述符
写端:用于写管道的文件描述符
无读端:是关闭所有的读端
无写端:是关闭所有的写端
所有的读端与写端被关闭时,无各管道就被释放了。
3,创建:
pipe(fd[2])    fd[0]  读端        fd[1]   写端
***************************************************************************************************
4,无名管道操作:
有写端有数据  则读端返回实际的读取的字节数
        无数据  进程会被阻塞
无写端有数据  则读端返回实际的读取的字节数
         无数据  read()函数立即返回0.
 
有读端有空间  回实际的写入的字节数
无空间  会阻塞(非原子性写入,即有多少空间就写多少空间)
利用此特性可以测出无名管道的大小(64K)
无读端写被SIGIPE中断。
******************************************************************************************************** 
II)有管道(通过文件I/O来操作的)
注意点:只有读端或只有写端时***打开文件****会阻塞这一点是很重要的呀!
有名管道承认的无名管道几乎所有的读写特性。
所有读端与写端被关时,FIFO会被释放。
管道中数据被读走了,就不存在了。
管道的大小始终为0,数据是通过内核来传输的。
下面是利用有名管道来实现不同进程间的文件拷贝
*********reader.c中有名管道的打开方式只能是O_RDONLY  writer.c中有名管道的打开方式只能是O_WRONLY  *********
********writer.c中有名管道的打开方式若为O_RDWR,在打开文件时不会阻塞,很快数据就写完了,如果之前  *********
*********没有打开reader.c则程序就结束了,当所有的读端与写端被关闭时管道就被释放了,reader.c中有         *********
*********名管道的打开方式若为O_RDWR在writer.c程序结束时,因为管道中既有读端又有写端当管道中没有    **********
*********数据时就会阻塞,所以reader.c程序就不会结束。故reader.c中有名管道的打开方式只能是O_RDONLY*********
**********writer.c中有名管道的打开方式只能是O_WRONLY                                                                               **********
reader.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

#define  N  64

#define  MYFIFO  "myfifo"

int main(int argc, char *argv[])
{
int fdd, fdf, nbyte;
char buf[N];

if (argc < 2)
{
printf("Usage : %s <file>\n", argv[0]);
return -1;
}

if ((fdf = open(MYFIFO, O_RDONLY)) < 0)
{
perror("fail to open fifo");
exit(-1);
}

if ((fdd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
{
perror("fail to open");
exit(-1);
}

while ((nbyte = read(fdf, buf, N)) > 0)
{
write(fdd, buf, nbyte);
}
close(fdf);
close(fdd);

return 0;
}
writer.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

#define  N  64

#define  MYFIFO  "myfifo"

int main(int argc, char *argv[])
{
int fds, fdf, nbyte;
char buf[N];

if (argc < 2)
{
printf("Usage : %s <file>\n", argv[0]);
return -1;
}

if ((fdf = open(MYFIFO, O_WRONLY)) < 0)
{
perror("fail to open fifo");
exit(-1);
}

if ((fds = open(argv[1], O_RDONLY)) < 0)
{
perror("fail to open");
exit(-1);
}

while ((nbyte = read(fds, buf, N)) > 0)
{
write(fdf, buf, nbyte);
}
close(fdf);
close(fds);

return 0;
}
III)
1,概念:
信号:是唯一一种异步通信的方式。可以理解为软件方面的异步中断。
内核可以发信号,进程与进程之间也可以发信号。
2,对信号的响应方式:
a忽略信号(SIGKILL,SIGSTOP不能被忽略)
b捕捉信号
c执行缺少操作
3,捕捉信号函数原型
*************************************************
void (*signal(int signo,void(*handler)(int)))(int)
此函数有两个参数一个是int signo,另一个是函数指针void (*handler)(int)
这个函数指针指向的函数返回值为void形参是int而signal()函数的返回值也是一个
函数指针,函数指针指向的函数返回值为void形参是int
***************************************************
利用信号来实现不同进程之间的通信
bus.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

pid_t pid;

void ticketer_handler(int signo)
{
switch ( signo )
{
case SIGINT:
kill(getppid(), SIGUSR1);
break;
case SIGQUIT:
kill(getppid(), SIGUSR2);
break;
case SIGUSR1:
printf("please get off the bus\n");
exit(0);
}
}

void driver_handler(int signo)
{
switch ( signo )
{
case SIGUSR1:
printf("gogogo\n");
break;
case SIGUSR2:
printf("stop the bus\n");
break;
case SIGTSTP:
kill(pid, SIGUSR1);
wait(NULL);
exit(0);
}
}

int main()
{
if ((pid = fork()) < 0)
{
perror("fail to fork");
exit(-1);
}
else if (pid == 0)  // ticketer
{
signal(SIGINT, ticketer_handler);
signal(SIGQUIT, ticketer_handler);
signal(SIGUSR1, ticketer_handler);
signal(SIGTSTP, SIG_IGN);
while ( 1 )
{
pause();
}
}
else  // driver
{
signal(SIGUSR1, driver_handler);
signal(SIGUSR2, driver_handler);
signal(SIGTSTP, driver_handler);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
while ( 1 )
{
pause();
}
}

return 0;
}
IV)
前导:
所有的IPC最后都需要程序是去主支释放,系统不会支释放IPC对象。
1,概念
共享内存:是一种最为高效率的进程间通信的方式 ,进程之间可以直接读写内存,而不需要任何的数据拷贝。
  但是,需要借助同步与互斥机制。
****************************************************************************
重点:用户是不能直接访问内核空间的但是IPC对象有唯一的ID(随机的,不能确定)但是我们可以通过ftok()
来产生一key用它来访问IPC对象。
2,共享内存的使用
a创建、打开共享内存
b映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问(用户不能直接访问内核,需要映射)
c撤销内存映射
d删除共享内存对象
3,使用共享内存需要注意的地方
a当我们需要对内存初始化时需要判断是谁创建的,因为初始化只能进行一次
b当我们释放内存的时候,共享内存并不会直接被删除,需要判断是否还有其它进程在使用
 当没有的时候,才删除。
读与写的应用
reader.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define  N  64

typedef struct 
{
pid_t pid;
char buf[N];
} SHM;

void handler(int signo)
{
}

int main()
{
int shmid;
SHM *p;
key_t key;
pid_t pid;

if ((key = ftok(".", 'm')) < 0)
{
perror("fail to ftok");
exit(-1);
}

signal(SIGUSR1, handler);
if ((shmid = shmget(key, sizeof(SHM), IPC_CREAT|IPC_EXCL|0666)) < 0)
{
if (EEXIST == errno)
{
shmid = shmget(key, sizeof(SHM), 0666);
p = (SHM *)shmat(shmid, NULL, 0);
pid = p->pid;
p->pid = getpid();
kill(pid, SIGUSR1);
}
else
{
perror("fail to shmget");
exit(-1);
}
}
else  // I'm the first process
{
p = (SHM *)shmat(shmid, NULL, 0);
p->pid = getpid();
pause();
pid = p->pid;
}

while ( 1 )
{
pause();
if (strcmp(p->buf, "quit\n") == 0) break;
printf("read from shm : %s", p->buf);
kill(pid, SIGUSR1);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);

return 0;
}
writer.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define  N  64

typedef struct 
{
pid_t pid;
char buf[N];
} SHM;

void handler(int signo)
{
}

int main()
{
int shmid;
SHM *p;
key_t key;
pid_t pid;

if ((key = ftok(".", 'm')) < 0)
{
perror("fail to ftok");
exit(-1);
}

signal(SIGUSR1, handler);
if ((shmid = shmget(key, sizeof(SHM), IPC_CREAT|IPC_EXCL|0666)) < 0)
{
if (EEXIST == errno)
{
shmid = shmget(key, sizeof(SHM), 0666);
p = (SHM *)shmat(shmid, NULL, 0);
pid = p->pid;
p->pid = getpid();
kill(pid, SIGUSR1);
}
else
{
perror("fail to shmget");
exit(-1);
}
}
else  // I'm the first process
{
p = (SHM *)shmat(shmid, NULL, 0);
p->pid = getpid();
pause();
pid = p->pid;
}

while ( 1 )
{
printf("write to shm : ");
fgets(p->buf, N, stdin);
kill(pid, SIGUSR1);
if (strcmp(p->buf, "quit\n") == 0) break;
pause();
}
shmdt(p);

return 0;
}
V)
消息队列:
********************************************
使用消息队列的时候,需要指定消息的格式(用结构体来表示)
消息队列在创建、打开的时候不需要判断是谁创建的
*********************************************

clientA.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define  N  64
#define  TypeA  100
#define  TypeB  200
#define  LEN (sizeof(MSG) - sizeof(long))

typedef struct
{
long mtype;
char text[N];
} MSG;

int main()
{
int msgid;
key_t key;
MSG buf;

if ((key = ftok(".", 'q')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((msgid = msgget(key, IPC_CREAT|0666)) < 0)
{
perror("fail to msgget");
exit(-1);
}

while ( 1 )
{
printf("send message : ");
fgets(buf.text, N, stdin);
buf.mtype = TypeB;
msgsnd(msgid, &buf, LEN, 0);
if (strcmp(buf.text, "quit\n") == 0) break;
msgrcv(msgid, &buf, LEN, TypeA, 0);
if (strcmp(buf.text, "quit\n") == 0)
{
msgctl(msgid, IPC_RMID, NULL);
break;
}
printf("recv message : %s", buf.text);
}

return 0;
}
clientB.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define  N  64
#define  TypeA  100
#define  TypeB  200
#define  LEN (sizeof(MSG) - sizeof(long))

typedef struct
{
long mtype;
char text[N];
} MSG;

int main()
{
int msgid;
key_t key;
MSG buf;

if ((key = ftok(".", 'q')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((msgid = msgget(key, IPC_CREAT|0666)) < 0)
{
perror("fail to msgget");
exit(-1);
}

while ( 1 )
{
msgrcv(msgid, &buf, LEN, TypeB, 0);
if (strcmp(buf.text, "quit\n") == 0)
{
msgctl(msgid, IPC_RMID, NULL);
break;
}
printf("recv message : %s", buf.text);
printf("send message : ");
fgets(buf.text, N, stdin);
buf.mtype = TypeA;
msgsnd(msgid, &buf, LEN, 0);
if (strcmp(buf.text, "quit\n") == 0) break;
}

return 0;
}
改进:
clientA.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define  N  64
#define  TypeA  100
#define  TypeB  200
#define  LEN (sizeof(MSG) - sizeof(long))

typedef struct
{
long mtype;
char text[N];
} MSG;

int main()
{
int msgid;
key_t key;
MSG buf;
pid_t pid;

if ((key = ftok(".", 'q')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((msgid = msgget(key, IPC_CREAT|0666)) < 0)
{
perror("fail to msgget");
exit(-1);
}

if ((pid = fork()) < 0)
{
perror("fail to fork");
exit(-1);
}
else if (pid == 0)  // receive message
{
while ( 1 )
{
msgrcv(msgid, &buf, LEN, TypeA, 0);
if (strcmp(buf.text, "quit\n") == 0)
{
kill(getppid(), SIGUSR1);
msgctl(msgid, IPC_RMID, NULL);
break;
}
printf("\n ***%s", buf.text);
}
}
else
{
buf.mtype = TypeB;
while ( 1 )
{
printf("input > ");
fgets(buf.text, N, stdin);
msgsnd(msgid, &buf, LEN, 0);
if (strcmp(buf.text, "quit\n") == 0) 
{
kill(pid, SIGUSR1);
break;
}
}

}

return 0;
}
clientB.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define  N  64
#define  TypeA  100
#define  TypeB  200
#define  LEN (sizeof(MSG) - sizeof(long))

typedef struct
{
long mtype;
char text[N];
} MSG;

int main()
{
int msgid;
key_t key;
MSG buf;
pid_t pid;

if ((key = ftok(".", 'q')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((msgid = msgget(key, IPC_CREAT|0666)) < 0)
{
perror("fail to msgget");
exit(-1);
}

if ((pid = fork()) < 0)
{
perror("fail to fork");
exit(-1);
}
else if (pid == 0)  // receive message
{
while ( 1 )
{
msgrcv(msgid, &buf, LEN, TypeB, 0);
if (strcmp(buf.text, "quit\n") == 0)
{
kill(getppid(), SIGUSR1);
msgctl(msgid, IPC_RMID, NULL);
break;
}
printf("\n ***%s", buf.text);
}
}
else
{
buf.mtype = TypeA;
while ( 1 )
{
printf("input > ");
fgets(buf.text, N, stdin);
msgsnd(msgid, &buf, LEN, 0);
if (strcmp(buf.text, "quit\n") == 0) 
{
kill(pid, SIGUSR1);
break;
}
}

}

return 0;
}
VI)
1,
信号灯集(与信号量相同)
system v 的信号灯集是一个或多个信号灯的集合主要是为了解决单个信号灯造成的死锁问题
2,不同信号灯的比较
posix 信号量分为:
无名信号量(主要用于线程之间)
有名信号量(主要用于进程之间)
system v 信号灯
一个或多个信号灯的集合(用于进程之间为了解决死锁问题)
举例应用:
reader.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define  N  64
#define  READ  0
#define  WRITE 1

union semun {
int              val;    /* Value for SETVAL */
struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
unsigned short  *array;  /* Array for GETALL, SETALL */
struct seminfo  *__buf;  /* Buffer for IPC_INFO
(Linux-specific) */
};

void init_sem(int semid, int array[], int num)
{
int i;
union semun sem;

for (i=0; i<num; i++)
{
sem.val = array[i];
semctl(semid, i, SETVAL, sem);
}
}

void pv(int semid, int num, int op)
struct sembuf buf;

buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
if (semop(semid, &buf, 1) < 0)
{
exit(0);
}
}

int main()
{
key_t key;
int shmid, semid, array[] = {0, 1};
char *shmadd;
if ((key = ftok(".", 'm')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666)) < 0)
{
if (EEXIST == errno)
if ((semid = semget(key, 2, 0666)) < 0)
{
perror("fail to semget");
exit(-1);
}
}
else
{
perror("fail to semget");
exit(-1);
}
}
else
{
init_sem(semid, array, 2);
}

if ((shmid = shmget(key, N, IPC_CREAT|0666)) < 0)
{
perror("fail to shmget");
exit(-1);
}

shmadd = (char *)shmat(shmid, NULL, 0);

while ( 1 )
{
pv(semid, READ, -1);
printf("read from shm : %s", shmadd);
pv(semid, WRITE, 1);
}

return 0;
}
writer.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define  N  64
#define  READ  0
#define  WRITE 1

union semun {
int              val;    /* Value for SETVAL */
struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
unsigned short  *array;  /* Array for GETALL, SETALL */
struct seminfo  *__buf;  /* Buffer for IPC_INFO
(Linux-specific) */
};

void init_sem(int semid, int array[], int num)
{
int i;
union semun sem;

for (i=0; i<num; i++)
{
sem.val = array[i];
semctl(semid, i, SETVAL, sem);
}
}

void pv(int semid, int num, int op)
struct sembuf buf;

buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
if (semop(semid, &buf, 1) < 0)
{
exit(0);
}
}

int main()
{
key_t key;
int shmid, semid, array[] = {0, 1};
char *shmadd;
if ((key = ftok(".", 'm')) < 0)
{
perror("fail to ftok");
exit(-1);
}

if ((semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666)) < 0)
{
if (EEXIST == errno)
if ((semid = semget(key, 2, 0666)) < 0)
{
perror("fail to semget");
exit(-1);
}
}
else
{
perror("fail to semget");
exit(-1);
}
}
else
{
init_sem(semid, array, 2);
}

if ((shmid = shmget(key, N, IPC_CREAT|0666)) < 0)
{
perror("fail to shmget");
exit(-1);
}

shmadd = (char *)shmat(shmid, NULL, 0);

while ( 1 )
{
pv(semid, WRITE, -1);
printf("write to shm : ");
fgets(shmadd, N, stdin);
if (strcmp(shmadd, "quit\n") == 0) break;
pv(semid, READ, 1);
}
semctl(semid, 0, IPC_RMID);
shmctl(shmid, IPC_RMID, NULL);

return 0;
}
进程间通讯方式比较总结:
pipe:具有亲缘关系的进程之间(不一定限于父子进程),单工,数据在内存中。
fifo:  可用于任意进程间的,双工,有文件名,数据在内存中。
signal:唯一的异步通信方式。
msg:常用于cs模式中,按消息的类型访问,可有优先级。
shm:效率最高,(直接访问内存),需要同步与互斥机制。
sem:配合共享内存使用,用以实现同步与互斥。
原创粉丝点击