进程
进程通信
共享内存
消息队列
信号量
无名管道
有名管道
Socket
(1) 进程是资源分配的最小单位
父子进程间遵循读时共享写时复制的原则
(2) 进程状态
进程基本的状态有5种。分别为初始态,就绪态,运行态,挂起态与终止态。其中初始态为进程准备阶段,常与就绪态结合来看。

(3) linux系统调用函数
头文件:#include〈unistd〉
创建函数 fork() (调用一次返回两次)
等待回收:wait()等待子进程结束
结束:exit()
补充:
system();调用shell命令在当前进程开始进程
exec();调用新路径或新文件的可执行程序来代替原有进程
eg:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int var = 34;
int main(void)
{
pid_t pid;
/* system("ping www.baidu.com -c 2");
char *args[]={"/bin/ls",NULL};
printf("系统分配进程号:%d\n",getpid());
(execve("/bin/ls",args,NULL)<0)
printf("出错\n");
*/
pid = fork();
if (pid == -1 ) {
perror("fork");
exit(0);
}
else if (pid > 0) {
sleep(2);
var = 55;
printf("I'm parent pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
system("ping www.baidu.com -c 2");
char *args[]={"/bin/ls",NULL};
printf("系统分配进程号:%d\n",getpid());
if(execve("/bin/ls",args,NULL)<0)
printf("出错\n");
} else if (pid == 0) {
var = 100;
printf("child pid = %d, parentID=%d, var = %d\n", getpid(), getppid(), var);
}
printf("var = %d\n", var);
return 0;
}
进程常用命令:
#ps aux 显示内存中进程
#ps -lA 显示所有进程
#kill 进程id 杀死进程
#top –d 2 每两分钟持续侦测进程状态
#ptree 列出进程树
(4) 僵尸进程与孤儿进程
僵尸进程:
一个父进程利用fork创建子进程,如果子进程退出,而父进程没有利用wait 或者 waitpid 来获取子进程的状态信息,那么子进程的状态描述符依然保存在系统中。
孤儿进程
一个父进程退出, 而它的一个或几个子进程仍然还在运行,那么这些子进程就会变成孤儿进程,孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集的工作
如何避免僵尸进程:
通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收
杀死父进程,变成孤儿进程
(5) 进程通信方式
共享内存
共享内存就是允许两个或多个进程共享一定的存储区,就是让两个进程地址通过页表映射到同一片物理地址以便于通信,你可以给一个区域里面写入数据,理所当然你就可以从中拿取数据,这也就构成了进程间的双向通信。
a) 头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
b) 函数:
创建共享内存:shmget()
获取共享内存地址:shmat()
删除共享内存:shmdt()
共享内存控制:shmctl()
/*shmdata.h*/
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{
int written;//作为一个标志,非0:表示可读,0表示可写
char text[TEXT_SZ];//记录写入和读取的文本
};
#endif
/*shmread.c*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
int main()
{
int running = 1;//程序是否继续运行的标志
void *shm = NULL;//分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid;//共享内存标识符 //创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
} //将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);//获取共享内存地址
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %X\n", (int)shm); //设置共享内存
shared = (struct shared_use_st*)shm;
shared->written = 0;
while(running)//读取共享内存中的数据
{ //没有进程向共享内存定数据有数据可读取
if(shared->written != 0)
{
printf("You wrote: %s", shared->text);
sleep(rand() % 3); //读取完数据,设置written使共享内存段可写
shared->written = 0; //输入了end,退出循环(程序)
if(strncmp(shared->text, "end", 3) == 0)
running = 0;
}
else//有其他进程在写数据,不能读取数据
sleep(1);
} //把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
} //删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
/*shnwrite.c*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/shm.h>
#include "shmdata.h"
int main()
{
int running = 1;//程序是否继续运行的标志
void *shm = NULL;//分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid;//共享内存标识符 //创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);//创建共享内存,IPC_CREAT如果内核不存在则创建它
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
} //将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %X\n", (int)shm); //设置共享内存
shared = (struct shared_use_st*)shm;
shared->written = 0;
while(running)//读取共享内存中的数据
{ //没有进程向共享内存定数据有数据可读取
if(shared->written != 0)
{
printf("You wrote: %s", shared->text);
sleep(rand() % 3); //读取完数据,设置written使共享内存段可写
shared->written = 0; //输入了end,退出循环(程序)
if(strncmp(shared->text, "end", 3) == 0)
running = 0;
}
else//有其他进程在写数据,不能读取数据
sleep(1);
} //把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
} //删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
消息队列
消息队列是内核地址空间中的内部链表,通过linux内核在各进程之间传递内容。将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序,每个消息都有唯一的IPCb标识符。可以解决两个进程的读写速度不同(处理数据速度不同),系统耦合等问题,而且消息队列里的消息哪怕进程崩溃了也不会消失。
a) 头文件
#include<linux/msg.h>
#include<sys/types.h>
#include<sys/ipc.h>
b) 函数
键值构建:ftok() 将路径名转化为IPC键值
获得消息:msgget()
发送消息:msgsnd()
接受消息:msgrcv()
消息控制:msgctl()
Eg:
/*meque_write.c*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>
#include<sys/ipc.h>
struct mymesg{
long int mtype;
char mtext[512];
};
int main()
{
int id = 0;
struct mymesg ckxmsg;
key_t key = ftok("/tmp",66);//获取PIC键值
id = msgget(key,IPC_CREAT | 0666);// IPC_CREAT不存在即创建
if(id == -1)
{
printf("create msg error \n");
return 0;
}
while(1)
{
char msg[512];
memset(msg,0,sizeof(msg));
ckxmsg.mtype = 1;//设置消息类型
printf("input message:");
fgets(msg,sizeof(msg),stdin);
strcpy(ckxmsg.mtext,msg);//获取数据
if(msgsnd(id,(void *)&ckxmsg,512,0) < 0)
{
printf("send msg error \n");
return 0;
}
if(strncmp(msg,"QUIT",4) == 0)
break;
}
if(msgctl(id,IPC_RMID,NULL) < 0)
{
printf("del msg error \n");
return 0;
}
return 0;
}
/*meque_read.c*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/msg.h>
#include<sys/ipc.h>
struct mymesg{
long int mtype;
char mtext[512];
};
int main()
{
int id = 0;
struct mymesg ckxmsg;
key_t key = ftok("/tmp",66);
id = msgget(key,0666|IPC_CREAT);
if(id == -1)
{
printf("open msg error \n");
return 0;
}
while(1)
{
if(msgrcv(id,(void *)&ckxmsg,512,1,0) < 0)
{
printf("receive msg error \n");
return 0;
}
printf("data:%s\n",ckxmsg.mtext);
if(strncmp(ckxmsg.mtext,"QUIT",4) ==0)
break;
}
return 0;
}
信号量
信号量是一种计数器,用来控制多个进程共享的资源进行的访问,常常被用作一个锁机制,在某个进程对特定资源进行操作时,信号量可以防止另一个进程去访问它。
1) 头文件:
#include<linux/sem.h>
#include<sys/types.h>
#include<sys/ipc.h>
/信号量数据结构体定义/
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
2) 函数:
a) 创建信号量:semget()
b) 信号量操作:semop():p/v操作 (P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1)
c) 控制信号量:semctl()
Eg:
1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/sem.h>
9 /*信号量数据结构*/
10 union semun
11 {
12 int val;
13 struct semid_ds *buf;
14 unsigned short *arry;
15 };
16
17 static int sem_id = 0;
18
19 static int set_semvalue();
20 static void del_semvalue();
21 static int semaphore_p();
22 static int semaphore_v();
23
24 int main(int argc, char *argv[])
25 {
26 char message = 'X';
27 int i = 0;
28
29 //创建信号量
30 sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
31
32 if(argc > 1)
33 {
34 //程序第一次被调用,初始化信号量
35 if(!set_semvalue())
36 {
37 fprintf(stderr, "Failed to initialize semaphore\n");
38 exit(EXIT_FAILURE);
39 }
40 //设置要输出到屏幕中的信息,即其参数的第一个字符
41
42 message = argv[1][0];
43
44 //向屏幕中输出数据
45 printf("%c\n", message);
46 sleep(2);
47 }
48 for(i = 0; i < 4; ++i)
49 {
50 //进入临界区
51 if(!semaphore_p())
52 exit(EXIT_FAILURE);
53 //向屏幕中输出数据
54 printf("%c", message);
55 //清理缓冲区,然后休眠随机时间
56 fflush(stdout);
57 sleep(rand() % 3);
58 //离开临界区前再一次向屏幕输出数据
59 printf("%c", message);
60 fflush(stdout);
61 //离开临界区,休眠随机时间后继续循环
62 if(!semaphore_v())
63 exit(EXIT_FAILURE);
64 sleep(rand() % 2);
65 }
66
67 sleep(10);
68 printf("\n%d - finished\n", getpid());
69
70 if(argc > 1)
71 {
72 //如果程序是第一次被调用,则在退出前删除信号量
73 sleep(3);
74 del_semvalue();
75 }
76 exit(EXIT_SUCCESS);
77 }
78
79 static int set_semvalue()
80 {
81 //用于初始化信号量,在使用信号量前必须这样做
82 union semun sem_union;
83
84 sem_union.val = 1;
85 if(semctl(sem_id, 0, SETVAL, sem_union) == -1)//setval 把单个信号量的值设置为联合体val的值
86 return 0;
87 return 1;
88 }
89
90 static void del_semvalue()
91 {
92 //删除信号量
93 union semun sem_union;
94
95 if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
96 fprintf(stderr, "Failed to delete semaphore\n");
97 }
98
99 static int semaphore_p()
100 {
101 //对信号量做减1操作,即等待P(sv)
102 struct sembuf sem_b;//sembuf结构体
103 sem_b.sem_num = 0;
104 sem_b.sem_op = -1;//P()
105 sem_b.sem_flg = SEM_UNDO;
106 if(semop(sem_id, &sem_b, 1) == -1)
107 {
108 fprintf(stderr, "semaphore_p failed\n");
109 return 0;
110 }
111 return 1;
112 }
113
114 static int semaphore_v()
115 {
116 //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
117 struct sembuf sem_b;
118 sem_b.sem_num = 0;
119 sem_b.sem_op = +1;//V()
120 sem_b.sem_flg = SEM_UNDO;
121 if(semop(sem_id, &sem_b, 1) == -1)
122 {
123 fprintf(stderr, "semaphore_v failed\n");
124 return 0;
125 }
126 return 1;
127 }
无名管道:
(1) 无名管道主要用于有血缘关系的两个进程间通信,是内核使用环形队列机制实现,借助内核缓冲区实现的。
(2) 函数 pipe()
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(void)
{
pid_t pid;
char buf[1024];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0) {
sys_err("fork err");
} else if (pid == 0) {
close(fd[1]);//关闭写端
int len = read(fd[0], buf, sizeof(buf));//读数据
write(STDOUT_FILENO, buf, len);
close(fd[0]);
} else {
close(fd[0]);//关闭读端
write(fd[1], p, strlen(p));//写数据
wait(NULL);
close(fd[1]);
}
return 0;
}
有名管道
有名管道主要用于两个不相干的进程间通信,我认为之所以叫有名管道是因为他们借助mkfifo函数创建的伪文件利用内核缓冲区进行通信,因为创建文件可以指定文件名所以操作和使用文件几乎一样。
(1) 函数:
创建管道mkfifo
打开管道open
读管道read
写管道write
关闭管道close
删除管道unlink
/* read_fiofo.c*/
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <fcntl.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 void sys_err(char *str)
10 {
11 perror(str);
12 exit(1);
13 }
14
15 int main(int argc, char *argv[])
16 {
17 int fd, len;
18 char buf[4096];
19
20 if (argc < 2) {
21 printf("./a.out fifoname\n");
22 return -1;
23 }
24 fd = open(argv[1], O_RDONLY);
25 if (fd < 0)
26 sys_err("open");
27 while (1) {
28 len = read(fd, buf, sizeof(buf));
29 write(STDOUT_FILENO, buf, len);
30 sleep(3); //多個读端时应增加睡眠秒数,放大效果.
31 }
32 close(fd);
33
34 return 0;
35 }
/* write_fiofo.c*/
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <fcntl.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 void sys_err(char *str)
10 {
11 perror(str);
12 exit(-1);
13 }
14
15 int main(int argc, char *argv[])
16 {
17 int fd, i;
18 char buf[4096];
19
20 if (argc < 2) {
21 printf("Enter like this: ./a.out fifoname\n");
22 return -1;
23 }
24 fd = open(argv[1], O_WRONLY);
25 if (fd < 0)
26 sys_err("open");
27
28 i = 0;
29 while (1) {
30 sprintf(buf, "hello itcast %d\n", i++);
31
32 write(fd, buf, strlen(buf));
33 sleep(1);
34 }
35 close(fd);
36
37 return 0;
38 }
39
socket
socket这里不做过多解释,内容比较多,大致流程下一章做个总结,下一章介绍下多线程,并发的一些网络编程