程序
程序通信
共享記憶體
消息隊列
信号量
無名管道
有名管道
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這裡不做過多解釋,内容比較多,大緻流程下一章做個總結,下一章介紹下多線程,并發的一些網絡程式設計