天天看點

帶你搞懂系統程式設計之--------程序程序

程序

程序通信

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

帶你搞懂系統程式設計之--------程式程式