程序
- 静态的,存放在磁盘上的指令与数据的有序集合
1. 进程
1.1 进程的含义
- 执行一个程序所分配的资源的总称
- 进程是程序的一次执行过程
- 动态的,包括创建、调度、执行、终止
1.2 进程的组成
- 正文段
- 用户数据段
- 系统数据段
- 进程控制块(pcb):存放进程属性,如进程标识pid,进程状态和优先级,进程用户,文件描述符表(当前进程打开了哪些文件)
- cpu寄存器值
- 堆栈(堆栈的区别,存储啥啥数据类型!!!)
1.3 进程的状态
1.4 查看进程信息
1.4.1 ps 查看系统进程快照
常用指令:
ps -ef 显示进程最基本的信息
ps -aux 显示进程最基本的信息&进程运行状态
关于进程状态,man ps 即可查看手册
1.4.2 top 查看进程动态信息
可以查看每个进程实时占用的资源大小,一般用于系统优化
1.4.3 /proc 进程详细信息
test进程进程号为8100,进入/proc/8100这个文件夹查看详细信息
yike@yike-virtual-machine:/proc/8100$ ls #进入/proc/8100目录
arch_status cwd mem patch_state stat
attr environ mountinfo personality statm
autogroup exe mounts projid_map status
auxv fd mountstats root syscall
cgroup fdinfo net sched task
clear_refs gid_map ns schedstat timens_offsets
cmdline io numa_maps sessionid timers
comm limits oom_adj setgroups timerslack_ns
coredump_filter loginuid oom_score smaps uid_map
cpu_resctrl_groups map_files oom_score_adj smaps_rollup wchan
cpuset maps pagemap stack
# fd储存的是当前进程打开的文件信息,不一一介绍
yike@yike-virtual-machine:/proc/8100$ cat status #查看status该文件
Name: test #进程名称
Umask: 0002
State: S (sleeping)
Tgid: 8100 #进程号
Ngid: 0
Pid: 8100
PPid: 2668
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 4 24 27 30 46 120 133 134 1000
NStgid: 8100
NSpid: 8100
NSpgid: 8100
NSsid: 2668
VmPeak: 2496 kB
VmSize: 2496 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 576 kB
VmRSS: 576 kB
RssAnon: 64 kB
RssFile: 512 kB
RssShmem: 0 kB
VmData: 176 kB
VmStk: 132 kB
VmExe: 4 kB
VmLib: 1652 kB
VmPTE: 44 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
THP_enabled: 1
Threads: 1
SigQ: 0/15242
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Seccomp_filters: 0
Speculation_Store_Bypass: thread vulnerable
SpeculationIndirectBranch: conditional enabled
Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
Cpus_allowed_list: 0-127
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 22
nonvoluntary_ctxt_switches: 1
1.4.4 nice & renice 优先级
nice 按照用户指定的优先级运行进程,普通用户只能指定0~20,管理员可以指定任意优先级
nice -n 2 ./test 运行test并指定其优先级为2
renice 改变正在运行的进程优先级
renice -n 3 pid 用某进程的pid去修改其优先级
1.4.5 jobs
用于查看该终端下有哪些后台进程(后台作业)
有2个后台进程,不过已经被我killed!
1.4.6 fg
fg pid 将后台进程转化为前台运行
1.5 进程的创建
NAME
fork - create a child process
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
成功返回子进程的pid,失败返回-1
示例:创建子进程
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t ret;
ret = fork();
if(ret < 0){
perror("fork\n");
return -1;
}else if(ret == 0){
printf("child pid : %d\n", getpid());
}else{
printf("parent pid : %d\n", getpid());
}
return 0;
}
父子进程:
- 子进程继承了父进程的内容
- 父子进程各自拥有独立的地址空间,互不影响
- 若父进程先结束
- 子进程称为孤儿进程,被init进程收养(注:init进程是系统启动之后创建的第一个用户进程,pid==1,系统规定,子进程必须被父进程回收,现在老爹死了只能由init去回收 )
- 子进程自动变为后台进程
- 若子进程先结束
- 父进程没有及时回收,子进程变为僵尸进程(子进程结束后,其相关资源被释放,但是进程的pcb未被释放,因为pcb中存放返回值和结束方式,这些信息系统规定必须由父进程回收,而刚好父进程没有及时回收,那子进程就成了僵尸!)
那么子进程从何处开始运行呢?
答:从fork语句的下一条指令开始运行,所以子进程并没有执行fork!因为子进程继承父进程几乎所有的内容,包括pc(程序计数器,存放下一条指令的地址),而父进程从main函数头部开始运行,pc值也跟着变化,子进程继承的pc值也就不可能是起始位置,而是fork语句的下一条指令的pc值
如何结束进程?
答:一般用exit,该函数结束进程时会刷新(流)缓冲区,函数原型:void exit (int status);
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello");
//print为标准输出流,且为行缓冲类型,这里没有换行符是不会输出的,但是exit会刷新缓冲区,最终正常显示
exit(0);
return 0;
}
1.6 进程回收
父进程调用wait (&status)回收
WIFEXITED(status) 判断子进程是否正常结束
WEEXITSTATUS(status) 判断子进程返回值
WIFSIGNALED(status) 判断子进程是否被信号结束
WTERMSIG(status) 获取结束子进程的信号类型
AME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus); # 成功返回子进程pid,失败返回-1
# status指定保存子进程返回值和结束方式的地址
# status为NULL表示直接释放子进程pcb,不接受返回值
pid_t waitpid(pid_t pid, int *wstatus, int options);
# pid用于指定某个子进程,如果pid为-1表示回收任意子进程
# 成功返回子进程pid或0,失败返回-1;
# pid用于指定回收哪个子线程或任意子进程
# option指定回收方式,0(阻塞方式)或 WNOHANG(非阻塞)
阻塞方式:子进程未退出,父进程保持阻塞状态,不会返回子进程status
非阻塞方式:子进程未退出,父进程不会阻塞,而是返回0;如果子进程退出,父进程返回子进程pid
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
int status;
ret = fork();
if(ret < 0){
perror("fork\n");
return -1;
}else if(ret == 0){
sleep(2); //子进程创建成功,先休眠2s
exit(1); //然后退出子进程
}else{
wait(&status); //回收子进程,用status存放子进程的pcb
printf("%x\n", status); //打印子进程的pcb地址,十六进制形式
}
return 0;
}
1.7 exec函数族
- 进程调用exec函数族执行某个程序
- 进程当前内容被指定的程序替换
- 实现让父子进程执行不同的程序
- 父进程创建子进程
- 子进程调用exec函数族
- 父进程不受干扰
exec函数族包括:
- execl / execlp
- execv / execvp
- system
1.8 守护进程
- 守护进程(daemon)是Linux三种进程类型之一
- 通常在系统启动时运行,系统关闭时结束,且始终在后台运行
- 和交互进程不同,守护进程独立于任何终端
- Linux中很多服务程序都以守护进程形式运行
- 守护进程周期性的执行某种任务或等待处理特定事件
会话、控制终端
- Linux以会话(session)、进程组的方式管理进程
- 每个进程属于一个进程组
- 会话是一个或多个进程组的集合,用户打开一个终端时,系统会创建一个会话;所有通过终端运行的进程都属于这个会话
- 终端关闭时,所有相关进程会被结束
2. 线程
个人理解:线程与进程最大的区别在于是否拥有独立的地址空间!
线程之间共享的资源:可执行指令、静态数据、进程中打开的文件描述符、当前工作目录、用户ID,用户组ID
线程之间私有的资源:线程ID、PC(程序计数器)和相关寄存器、堆栈、错误号(errno)、优先级、执行状态和属性
2.1 线程的创建 / 回收 / 结束
int pthread_create(pthread_t *thread, const pthread_attr_t &attr,
void *(start_routine)(void *), void *arg);
# 成功返回0,失败返回错误码
# thread 线程对象
# attr 线程属性,NULL代表默认属性
# routine 表示线程执行的函数
# arg 传递给routine函数的参数
int pthread_join(pthread_t thread, void **retval);
# 成功返回0,失败返回错误码
# thread 要回收的线程对象
# 调用线程阻塞直到thread结束
# *retval用于接收线程thread的返回值
void pthread_exit(void *retval);
# 结束当前线程,不能用exit,exit用于结束进程!
# retval可被其他线程通过pthread_join获取
# 线程私有数据被释放
2.2 同步机制——信号量的P/V操作
已知线程共享同一进程的地址空间,所以优点就是线程之间可以通过全局变量进行数据交换,线程间通信非常容易,但是多个线程访问共享数据时需要同步或互斥机制。
同步:多个任务按照约定先后次序相互配合完成一件事情
基于信号量的同步机制:由信号量决定线程继续运行还是阻塞等待
信号量代表一类资源,其值表示系统中该资源的数量,其可以通过初始化、P/V操作进行访问
初始化
int sem_init(sem_t *sem,int pshared,unsigned int value);
//pshared 0表示线程 1表示进程
//value 信号量初始值,大于0表示有资源
P/V操作
int sem_wait(sem_t *sem); //P
int sem_post(sem_t *sem); //V
sem_t sem;
/* P(S)含义 */
if(sem > 0)
{
申请资源的任务继续运行;
sem--;
}else{
申请资源的任务阻塞
}
/* V(S)含义 */
sem++;
if(有任务在等待资源)
{
唤醒等待的任务,让其继续运行
}
!!需要注意的是:执行V操纵不会引起阻塞,因为它是在释放资源
2.3 互斥机制——互斥量
临界资源:一次只允许一个进程/线程访问的共享资源
互斥锁初始化
int pthread_mutex_init(phtread_mutex_t *mutex, const pthread_mutexattr_t *restrict attr);
这个太麻烦,用这个:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER;
互斥量加锁(阻塞)/解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
3. 进程间通信机制
unix进程间通信方式
- 无名管道(pipe)
- 有名管道(fifo)
- 信号(signal)
System V IPC
- 共享内存(share memory)
- 消息队列(message queue)
- 信号灯集合(semaphore set)
套接字(socket)
3.1 无名管道
- 无名管道由内核创建
- 只能用于具有亲缘关系(父子进程)的进程之间的通信(注:无名管道没有路径,在文件系统中是不可见的,仅仅是在内存中存在,并且无名管道是由某个进程创建的,其他进程只能通过继承的方式打开其他进程创建的无名管道;对于普通文件,不同的进程可以通过路径打开同一个文件)
- 单工通信,具有固定的读端和写端
#include <unitd.h>
int pipe(int pfd[2]); pfd包含两个元素的整形数组,用于保存文件描述符
pfd[0]用于读管道,pfd[1]用于写管道
/* 示例:
* 子进程1和2分别往管道中写入字符串,父进程读管道内容并打印
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t pid1, pid2;
char buf[32];
int pfd[2]; //该数组用于创建无名管道
/* 一定是先创建无名管道,然后创建子进程,不然怎么继承 */
if(pipe(pfd) < 0){
perror("pipe");
return -1;
}
if((pid1 = fork()) < 0){
perror("fork"); //小于0,子进程1创建失败
return -1;
}else if(pid1 == 0){
strcpy(buf, "I'm process1"); //等于0,创建成功,子进程1执行拷贝
write(pfd[1], buf, 32); //pfd[1]执行写操作
return -1;
}else{
if((pid2 = fork()) < 0){
perror("fork");
return -1;
}else if(pid2 == 0){
sleep(2); //子进程1先写管道,然后2s后子进程2写管道
strcpy(buf, "I'm process2"); //等于0,创建成功,子进程2执行拷贝
write(pfd[1], buf, 32); //pfd[1]执行写操作
}else{ //父进程
wait(NULL); //等待回收任意子进程,这里回收process1
read(pfd[0], buf, 32);
printf("%s\n", buf);
wait(NULL); //等待回收process2
read(pfd[0], buf, 32);
printf("%s\n", buf);
}
}
return 0;
}
无名管道有多大?
/* 通过循环写操作计算无名管道的大小
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int count = 0;
int pfd[2];
char buf[1024];
if(pipe(pfd) < 0){ //创建管道
perror("pipe");
return -1;
}
while(1){ //循化往pipe中写数据直至阻塞
write(pfd[1], buf, 1024);
printf("has wrote %dk bytes\n", ++count); // 一般以k作为单位
}
return 0;
}
输出结果
has wrote 58k bytes
has wrote 59k bytes
has wrote 60k bytes
has wrote 61k bytes
has wrote 62k bytes
has wrote 63k bytes
has wrote 64k bytes 通过输出结果可以得出无名管道的内存大小为64bytes
^C
yike@yike:~/yike$ vi pipesize.c
无名管道的读写特性
读端存在 | 有空间 | write返回实际写入的字节数 |
无空间(空间不足) | 进程写阻塞 |
写端存在 | 有数据 | read返回实际读取的字节数 |
无数据 | 进程读阻塞 | |
写端不存在 | 有数据 | read返回实际读取的字节数 |
无数据 | read返回0 |
3.2 有名管道
- 对应管道文件,可用于任意进程之间的通信
- 打开管道时可指定读写方式
- 通过文件IO操作,内容存放在内存中
/* 函数原型 */
int mkfifo(const char *path, mode_t mode);
path 创建的管道文件路径
mode 管道文件的权限
/* 示例代码:
* 进程A: 循环从键盘输入并写入有名管道myfifo, 输入quit时退出
* 进程B:循环统计进程A每次写入myfifo的字符串长度
* 所以需要3个程序,创建(create_myfifo.c)、读(read_myfifo.c)、写(write_myfifo.c )
*/
/* create_myfifo.c */
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
/*
* 创建又名管道myfifo
*/
int main(){
if(mkfifo("myfifo", 0666) < 0){
perror("mkfifo");
return -1;
}
printf("myfifo is created\n");
return 0;
}
/* write_myfifo.c */
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#define N 32
int main(){
char buf[N];
int pfd;
if((pfd = open("myfifo", O_WRONLY)) < 0){
perror("open");
return -1;
}
printf("myfifo is opened\n");
while(1){
fgets(buf, N, stdin);
if(strcmp(buf, "quit\n") == 0)
break;
write(pfd, buf, N);
}
close(pfd);
return 0;
}
/* read_myfifo.c */
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define N 32
int main(){
char buf[N];
int pfd;
if((pfd = open("myfifo", O_RDONLY)) < 0){
perror("open");
return -1;
}
printf("myfifo is opened\n");
while(read(pfd, buf, N) > 0){
printf("the length of string is %ld\n", strlen(buf));
}
close(pfd);
return 0;
}
/* 输出结果 */
book@100ask:~/yike$ ll
total 56
drwxrwxr-x 2 book book 4096 Sep 5 01:14 ./
drwxr-xr-x 26 book book 4096 Sep 5 01:15 ../
-rwxrwxr-x 1 book book 8400 Sep 5 01:07 create_myfifo*
-rw-rw-r-- 1 book book 310 Sep 5 01:07 create_myfifo.c
prw-rw-r-- 1 book book 0 Sep 5 01:17 myfifo|
//有名管道文件,大小一直为0,因为其保存在内存中,
-rwxrwxr-x 1 book book 8616 Sep 5 01:12 read_myfifo*
-rw-rw-r-- 1 book book 439 Sep 5 01:02 read_myfifo.c
-rwxrwxr-x 1 book book 8664 Sep 5 01:14 write_myfifo*
-rw-rw-r-- 1 book book 499 Sep 5 01:14 write_myfifo.c
3.3 信号
- 信号是在软件层上对中断机制的一种模拟,是一种异步通信方式(一个进程在任何情况下都能接收信号)
- Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件(kill -l 可查看信号类型)
- 进程对信号有不同的响应方式(缺省方式、忽略信号、捕捉信号)
信号相关指令:
#include <unistd.h>
#include <signal.h>
int kill(pid_t pid, int sig);
pid: 进程号,等于0时代表同组进程,等于-1时代表所有进程
sig:信号类型
int raise(int sig);
自己给自己发信号,所以不需要指定pid
int alarm(unsigned int seconds);
创建定时器,时间到了就发送信号SIGSLARM
Linux下,一个程序只能有一个定时器,设定了新的,原先的就会失效,返回上个定时器
的剩余时间
int pause(void);
让进程一直阻塞,直到等到信号
sighandler_t signal(int signum, sighandler_t handler);
这个函数不会发送任何信号,它只是将一个信号量和信号处理函数关联起来
成功返回原先的信号处理函数,失败返回SIG_ERR
这个函数具体可以看看韦东山的驱动-异步通知部分
3.4 System V IPC
- IPC包括:共享内存、消息队列、信号灯集
- 每个IPC对象有唯一的ID(只有创建该IPC的进程知道,其他进程不知道)
- IPC对象创建后一直存在,直到被显示的删除
- 每个IPC对象有一个关联的KEY(允许其他进程通过KEY获取该IPC的ID)
-
yike@yike-virtual-machine:~/yike$ ipcs 查看系统所有IPC对象 ------ Message Queues -------- 消息队列 key msqid owner perms used-bytes messages ------ Shared Memory Segments -------- 共享内存 key shmid owner perms bytes nattch status 0x00000000 6 yike 600 524288 2 dest key = 0x00000000表示该IPC为私有属性,不会被其他进程所获得,如果key非零,那其与shmid是一一对应的 其他进程就可以通过key值获得与其对应的唯一id ------ Semaphore Arrays -------- 信号灯集 key semid owner perms nsems yike@yike-virtual-machine:~/yike$ ipcrm 用于删除某IPC对象
key值通过ftok创建,再由key值创建IPC,然后根据id进行相应的操作。通常要利用IPC进行进程间通信,所以要指定非零的key值。若key值设为IPC_PRIVATE(也就是0x00000000),表明其为私有属性,其他进程不可以访问。
NAME
ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
成功返回合法的key值
path 存在且可访问的文件路径
proj_id 用于生成key的数字,不能为0
3.4.1 共享内存
- 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存而不需要任何数据的拷贝
- 共享内存在内核空间创建,被进程映射到用户空间后可以直接读写操作
- 由于共享内存可被多个进程同时访问,所以需要配合同步互斥机制
共享内存创建
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
成功返回共享内存的id
shmflg 共享内存标志位 IPC_CREAT | 0666
示例1:
int shmid;
if((shmid = shmget(IPC_PRIVATE, 512, 0666)) < 0){
perror("shmget"); //创建私有共享内存,大小512字节、权限0666
return -1;
}
示例2:
/* 创建并打开一个和key关联的共享内存,大小1024字节、权限0666 */
int shmid;
key_t key;
if((key = ftok(".", 'm')) == -1){
perror("ftok"); //相关的进程里都要调用ftok,并且指定相同的参数,这样才能得到相同的key
return -1;
}
if((shmid = shmget(key, 1024, IPC_CREAT | 0666)) < 0){
perror("shmget");
return -1;
}
共享内存映射
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
成功返回映射后的地址
shmid 要映射的共享内存id
shmaddr 映射后的地址,NULL表示由系统自动映射(推荐)
shmflg 0表示可读写,SHM_RDONLY表示只读
共享内存读写
通过指针访问共享内存,指针类型取决于共享内存中存放的数据类型
示例:
在共享内存中存放键盘输入的字符串
char *addr;
int shmid;
......
if((shmid = (char *)shmat(shmid, NULL, 0)) == (char *)-1){
perror("shmat");
return -1;
}
fgets(addr, N, stdin);
......
共享内存撤销映射
SYNOPSIS
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
shmaddr就是前面映射函数中的首地址
不使用共享内存时需要撤销映射,及时进程结束时系统会自动撤销
共享内存控制
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
成功返回0
cmd 需要执行的操作 IPC_STAT IPC_SET IPC_RMID
获取共享内存的属性 设置共享内存的属性 删除共享内存id
buf 保存或设置共享内存属性的地址
3.4.2 消息队列
- 消息队列由消息队列ID唯一标识
- 消息队列可以按照类型发送/接收消息
消息格式:
首成员类型必须为long(正整数),其他成员都属于消息正文
typedef struct {
long mtype;
char mtext[64];
}MSG;
#define MSGLEN (sizeof(MSG) - sizeof(long)) //这才是消息的长度
int main()
{
......
MSG msgBuf;
msgBuf.mtype = 100; //填充消息的类型,得是个正整数
fgets(msgBuf.mtext, 64, stdin); //直接键盘输入到msgBuf的text中
msgsnd(msgid,&msgBuf, LEN, 0); //发送消息到消息队列中
......
return 0;
}
消息队列创建
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
消息队列发送/接收消息
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgp 要发送的消息地址
msgsz 消息正文长度
msgflg 0表示发送成功再返回(消息队列满了会等待,直到发
送成功,阻塞方式),IPC_NOWAIT(消息队列满了会返回-1,并设置错误号)
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
返回收到的消息的长度
msgp 消息缓冲区(结构体)地址
msgtyp 指定接收的消息类型
msgflg 0 或者 IPC_NOWAIT
消息队列控制
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
示例:
两进程通过消息队列轮流将键盘输入的字符串发送给对方,接手并打印对方发送的消息
/* clientA.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct {
long mtype;
char mtext[64];
}MSG;
#define MSGLEN (sizeof(MSG) - sizeof(long))
#define typeA 100
#define typeB 200
int main()
{
key_t key;
int msgid;
MSG msgBuf;
if((key = ftok(".", 'q')) == -1){
perror("ftok");
return -1;
}
if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){
perror("msgget");
return 0;
}
while(1){
msgBuf.mtype = typeB;
printf("input > ");
fgets(msgBuf.mtext, 64, stdin);
msgsnd(msgid, &msgBuf, MSGLEN, 0);
if(strcmp(msgBuf.mtext, "quit\n") == 0) break;
if(msgrcv(msgid, &msgBuf, MSGLEN, typeA, 0) < 0){
perror("msgrcv");
return -1;
}
if(strcmp(msgBuf.mtext, "quit\n") == 0){
msgctl(msgid, IPC_RMID, 0);
return 0;
}
printf("recv from clientB : %s\n", msgBuf.mtext);
}
return 0;
}
/* clientB.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct {
long mtype;
char mtext[64];
}MSG;
#define MSGLEN (sizeof(MSG) - sizeof(long))
#define typeA 100
#define typeB 200
int main()
{
key_t key;
int msgid;
MSG msgBuf;
if((key = ftok(".", 'q')) == -1){
perror("ftok");
return -1;
}
if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){
perror("msgget");
return 0;
}
while(1){
if(msgrcv(msgid, &msgBuf, MSGLEN, typeB, 0) < 0){
perror("msgrcv");
return -1;
}
if(strcmp(msgBuf.mtext, "quit\n") == 0){
msgctl(msgid, IPC_RMID, 0);
return -1;
}
printf("recv from clientA : %s\n", msgBuf.mtext);
msgBuf.mtype = typeA;
printf("input > ");
fgets(msgBuf.mtext, 64, stdin);
msgsnd(msgid, &msgBuf, MSGLEN, 0);
if(strcmp(msgBuf.mtext, "quit\n") == 0) break;
}
return 0;
}
改进思路:
通过多线程实现同时收发消息