天天看點

IO進線程程式設計一、标準IO二、檔案IO三、程序四、線程五、程序間的通信跳轉:IO進線程概念理論!跳轉:上一篇,資料結構與算法!跳轉:下一篇,網絡程式設計!

IO進線程程式設計

  • 一、标準IO
  • 二、檔案IO
  • 三、程序
  • 四、線程
  • 五、程序間的通信
  • 跳轉:IO進線程概念理論!
  • 跳轉:上一篇,資料結構與算法!
  • 跳轉:下一篇,網絡程式設計!

統一聲明:

部落格轉載 聲 明 : 本部落格部分内容來源于網絡、書籍、及各類手冊。

        内容宗旨為友善查詢、總結備份、開源分享。

        部分轉載内容均有注明出處,如有侵權請聯系部落格告知并删除,謝謝!

百度雲盤提取碼:統一提取碼:

ziyu

一、标準IO

1.标準IO------(C庫提供)

C庫函數:在系統調用接口之上封裝的接口,一個C庫函數可以封裝多個系統調用函數。

作用:

  1. 增強了代碼的可移植性,複用性
  2. 提高了效率。

    标準IO增加了一個【緩沖機制】

2.檔案IO------(linux系統提供)

系統調用:當我們的應用程式要使用一些底層的功能的時候,不應該自行通路底層,而應該向作業系統送出請求。

特點:

  1. 不帶緩沖區
  2. 作業系統直接提供的函數接口
  3. 調用系統調用是很耗費資源的

1. 标椎IO (c庫)

FILE fopen(const char path, const char mode);

/***************************************************************

  • 功能: 打開檔案
  • 參數: path:帶路徑的檔案名,mode:打開方式 r:隻讀 w:隻寫
  • 傳回值: 成功傳回FILE指針。失敗傳回NULL

    *******************************************************************/

r: 以讀的方式打開一個檔案,同時檔案指針指向檔案開頭。

r+: 以讀寫的方式打開一個檔案,同時檔案指針指向檔案開頭。

w:以寫的方式打開一個檔案,如果檔案存在清空,如果不存在建立檔案,同時檔案指針指向檔案開頭。

w+: 以讀寫的方式打開一個檔案,如果檔案存在清空,如果不存在建立檔案,

同時檔案指針指向檔案開頭。

a:以寫的方式打開一個檔案,如果不存在建立檔案,如果檔案存在以追加方式寫,同時檔案指針指向檔案結尾。

a+:以讀寫的方式打開一個檔案,如果不存在建立檔案,如果檔案存在讀檔案指針在檔案開頭,寫檔案指針在檔案結尾(追加方式寫)

FILE:系統會自動為使用的檔案在記憶體中開辟一片空間,來存儲該檔案的詳細資訊,這個空間類型為 FILE 結構體類型,該結構體由系統設計。

FILE *:流指針,在标準IO中,每次成功打開一個檔案,都會傳回一個流指針,這個流指針就描述了一個檔案,所有的标準IO都圍繞流指針來進行。

同一個檔案,可以存在多個流指針,與之對應。

vi -t FILE

vi -t _IO_FILE

struct _IO_FILE {

char* _IO_buf_base;

int _fileno;

}

示例:利用标椎IO函數測試目前系統最大能打開的檔案個數。

void perror(const char s);

/***************************************************************

  • 功能:根據 errno 列印出錯誤資訊
  • 參數:@s 提示用的字元串 可以寫任意字元串
  • 傳回值: void

    *****************************************************************/

int fclose(FILE fp);

/*****************************************************************

  • 功能: 關閉檔案
  • 參數: 檔案指針
  • 傳回值: 成功傳回0 失敗傳回-1

    *******************************************************************/

/*建立一個檔案*/
#include <stdio.h>
int main(void)
{
    FILE *fp = fopen("./1.txt", "w");
    if(NULL == fp)
    {
        perror("fopen\n");  
        return -1;  
    }
    
    printf("fopen success!\n");
    
    fclose(fp);	//關閉流指針
    
    return 0;
} 
           

為什麼要關閉一個檔案?

一、防止其他程序操作這個檔案

二、釋放結構體占用的資源

在程式結束時,系統自動回收資源(不完全),是以盡量寫上fclose。

系統預設打開了3個流指針 stdin stdout stderr

#include <stdio.h>


int main(void)
{
    FILE *fp = fopen("./1.txt", "r");
    if(NULL == fp)
    {
        perror("fopen");  
        return -1;  
    }
        

    int n = fgetc(fp);

    printf("n = %d\n", n);
    
    printf("len = %d\n", fp->_IO_buf_end - fp->_IO_buf_base);
   
    printf("fopen success!\n");
    
    fclose(fp);	//關閉流指針
    
     fputc(fgetc(stdin),stdout);
     fputc('\n', stdout);
    
    return 0;
} 
           

int fgetc(FILE *stream);

int fputc(int c, FILE *stream);

/*追加aaaaaaaaaa*/
#include <stdio.h>


int main(void)
{
    FILE *fp = fopen("./1.txt", "r+");
    if(NULL == fp)
    {
        perror("fopen");  
        return -1;  
    }


    printf("%c\n", fgetc(fp));

    int i = 0;
    for(i=0; i<10; i++)
    {
	    fputc('a', fp);	//:aaaaaaaaaa	//寫入
    }

    printf("fopen success!\n");
    
    fclose(fp);		//關閉流指針
    
    return 0;
} 
 
 
/*
運作結果:
aaaaaaaaaa
fopen success!
*/
           

每一個終端都是一個檔案: pts/xxx 這個就是終端對應的檔案,這個檔案的名字是以數字命名的

這個檔案存儲在 : /dev/pts/xxx

這些檔案是由linux系統自動建立。當打開一個終端時,就會重建一個新的檔案與之對應

stdin、stdout、stderr都指向的是同一個檔案(終端檔案)。

示例:直接向終端寫入’x’

/*向一個檔案寫入内容*/
#include <stdio.h>

int main(void)
{
	FILE *fp = fopen("./2", "w");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}

	fputc('x', fp);
	fputc('\n', fp);

	fputc('b', fp);
	fputc('\n', fp);

	fclose(fp);	//關閉流指針

	return 0;
} 
/*
運作結果:新檔案2内容,cat 2 檢視
x
d
*/
           

緩沖區: 行緩存、無緩存、全緩存

printf、stdin 、stdout是行緩存,緩沖區大小是 1024byte == 1Kbyte

//1.一行滿了 或遇到’\n’輸出條件

//2.fflush可以強制重新整理

//3.檔案關閉的時候 fclose(stdout) exit return

int fflush(FILE *stream);

/*行緩存:隻有當遇見'\n'時,才重新整理緩存區*/
#include <stdio.h>

int main(void)
{
    printf("hello word");	// '\n'
    printf("line size = %d\n", stdout->_IO_buf_end - stdout->_IO_buf_base);	//:1024 ;行緩存大小都是1024
    
    int i = 0;
    for(i=0; i<1024; i++)		//前面占有字元,這裡添加998即可占滿
    {
    	fputc('a', stdout);      //添加'a'滿的情況     
    }
    
    //fclose(stdout);
    //exit(0);
    //return 1;
    //fflush (stdout);
    
    while(1);         		//死循環      

	return 0;
} 

/*
運作結果:行緩存占滿

*/
           

無緩存:stderr

/*無緩存*/
#include <stdio.h>

int main(void)
{
	fputc('a', stdout);
	fputc('b', stdout);
	//fputc('\n', stdout);	//不加'\n',a,b不列印


	fputc('a', stderr);	//:a
	fputc('b',stderr);	//:b

	while(1);

	return 0;
} 
           

全緩存:通過fopen函數打開的流指針,這個流指針fp的緩沖區大小是 4*1024 4Kbyte

//1.緩存區滿

//2.fclose(fp)

//3.return

//4.exit

//5.fflush(fp)

/*全緩存:占4096,4k*/
#include <stdio.h>

int main(void)
{
	FILE *fp = fopen("./1.txt", "w");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}

	int i = 0;
	for(i=0; i<4097; i++)	//當緩存區4096之後滿了将資料存儲到緩存區去
				//1.txt檔案内容才列印
	{
		fputc('a', fp);         
	}

	while(1);          		    

	return 0;
} 
/*
運作結果:當全緩存沒滿時,資料不會列印到緩存區,占滿才會放入1.txt中

*/
           

char *fgets(char *buf, int size, FILE *stream);

檔案有多少行?

/*fgets用法,判斷檔案有多少行?*/
#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE *fp = fopen("./1.txt", "r");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}

	char buf[100];
	int cont = 0;
	while(1)
	{
		char *p = fgets(buf, sizeof(buf), fp);
		if(NULL == p)
		{
			perror("fgets");
			break;
		}
		printf("p = %s", p);

		memset(buf, 0, sizeof(buf));

		cont++;
	}

	printf("count = %d\n", cont);
 
  	fclose(fp);	//關閉流指針

	return 0;
} 
/*
運作結果:計算檔案有多少行。

*/
           

int fputs(const char *buf, FILE *stream);

寫入"hello world"

示例:從标椎輸入stdin 字元串到檔案當中

練習:複制檔案

/*fputs用法終端輸入資料到檔案中,可以輸入quit直接退出*/
#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE *fp = fopen("./1.txt", "w");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}

	char buf[50];

	while(fgets(buf, sizeof(buf), stdin) != NULL)
	{
		if(strcmp(buf,"quit\n")==0)	//輸入quit直接退出
		{
			break;
		}

		fputs(buf, fp);
	}

	printf("success!\n");
	
	fclose(fp);

	return 0;
} 
           

練習:複制檔案

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	FILE *fp1 = fopen("a.txt", "r");
	if(NULL == fp1)
	{
		perror("fopen");
		return -1;
	}
	FILE *fp2 = fopen("b.txt", "w");
	if(NULL == fp2)
	{
		perror("fopen");
		return -1;
	}

	char buf[100];
	while( fgets(buf, sizeof(buf), fp1) != NULL )
	{
		fputs(buf, fp2);
	}
	
	printf("copy success!\n");

	fclose(fp1);
	fclose(fp2);


	return 0;
}
           

int fseek(FILE *stream, long offset, int whence);

定位到檔案末尾的前一個位元組 fseek(fp, -1 , SEEK_END);

long ftell(FILE *stream);

擷取檔案大小

void rewind(FILE *stream);

#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE *fp = fopen("./1.txt", "r+");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}
	
	fseek(fp, 0, SEEK_END);	//移動末尾

	fputc('x', fp);

	int num = ftell(fp);	//統計位元組數
	printf("num = %d\n", num);

	rewind(fp);		//移動到開頭
	
	int num2 = ftell(fp);
	printf("num = %d\n", num2);


	printf("success!\n");
	
	fclose(fp);

	return 0;
}
/*
運作結果:
num = 22
num = 0
success!
*/
           

練習:查單詞

僞代碼{

1. 打開檔案

2. 循環

3. 輸入一個單詞

4. 周遊檔案

5. 列印出單詞資訊

6. 重新開始查詢

7. 關閉檔案

}

//查單詞
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>

int Stat(char *filename)
{
	struct stat my_stat;

	int ret = stat(filename, &my_stat);
	if(ret == -1)
	{
		perror("stat");
		return -1;
	}

	if(S_ISREG(my_stat.st_mode))
		printf("-");
	else if(S_ISDIR(my_stat.st_mode))
		printf("d");
	else if(S_ISCHR(my_stat.st_mode))
		printf("c");
	else if(S_ISBLK(my_stat.st_mode))
		printf("b");
	else if(S_ISFIFO(my_stat.st_mode))
		printf("p");
	else if(S_ISSOCK(my_stat.st_mode))
		printf("s");
	else
		printf("l");

	
	printf("%c", (my_stat.st_mode & (1 << 8))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 7))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 6))?'x':'-' );

	printf("%c", (my_stat.st_mode & (1 << 5))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 4))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 3))?'x':'-' );

	printf("%c", (my_stat.st_mode & (1 << 2))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 1))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 0))?'x':'-' );

    printf(" %ld", my_stat.st_nlink);

    struct passwd *p = getpwuid(my_stat.st_uid);
    printf(" %s", p->pw_name);

    struct group *q = getgrgid(my_stat.st_gid);                              
    printf(" %s", q->gr_name); 

	printf("%6ld ", my_stat.st_size);

	time_t t;
	time(&t);

	struct tm *k = localtime(&my_stat.st_mtime);
	printf("%d月  %2d %d:%d ", k->tm_mon+1, k->tm_mday,k->tm_hour,k->tm_min);

	return 0;
}

int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("please input %s pathname!\n", argv[0]);
		return -1;
	}

	DIR *dir = opendir(argv[1]);
	if(NULL == dir)
	{
		perror("opendir");
		return -1;
	}

	while(1)
	{
		struct dirent *p = readdir(dir);
		if(p == NULL)
			break;
		if( strncmp(p->d_name, ".", 1) == 0)
			continue; 
		Stat(p->d_name);
		printf("%s\n", p->d_name);
	}

	closedir(dir);

	return 0;
}

           
IO進線程程式設計一、标準IO二、檔案IO三、程式四、線程五、程式間的通信跳轉:IO進線程概念理論!跳轉:上一篇,資料結構與算法!跳轉:下一篇,網絡程式設計!

【Printf函數更新】

int fprintf(FILE *stream, const char *format, …);

int sprintf(char *str, const char *format, …);

int sscanf(const char *str, const char *format, …);

#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE *fp = fopen("./dict.txt", "rb");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}
	char buf[100];
 
   int ret = fread(buf, 50, 2, fp);
   
   printf("ret = %d %s", ret, buf);           



	printf("success!\n");
	
	fclose(fp);

	return 0;
}
/*
運作結果:

*/
           

當我們用 UE 打開一個二進制檔案(圖檔,視訊)的時候,發現檔案中到處都是文本的标志性字元,但是對于 fread 和 fwrite 來說,一視同仁,都是一個普通的位元組而己,

是以,二進制檔案的讀寫就要用對文本标記不敏感的 fread 和 fwrite 來進行。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

例如:從檔案fp裡讀取100個位元組,可用以下語句

fread(buffer,100,1,fp);

fread(buffer,50,2,fp);

fread(buffer,1,100,fp);

/*fread用法*/
#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE *fp = fopen("./dict.txt", "rb");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}
	char buf[100];
 
   int ret = fread(buf, 50, 2, fp);
   
   printf("ret = %d %s", ret, buf);           



	printf("success!\n");
	
	fclose(fp);

	return 0;
}
/*
運作結果:

*/
           

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

/*fwrite用法*/
#include <stdio.h>
#include <string.h>

 typedef struct node{
     int a;
     char b;
     float c; 
 }node_t;

int main(void)
{
	FILE *fp = fopen("./a.txt", "wb");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}
	
 	node_t XX = {15, 'a', 3.14};
  
  	fwrite(&XX, sizeof(node_t), 1, fp);
   
   printf("fwrite success!\n");
   
	
	fclose(fp);

	return 0;
}
/*
運作結果:

*/
           

練習:怪獸家園

typedef struct Monster{

int ID;

char name[10];

char species[10];

char type[10];

}MOS;

/*怪獸家園*/
#include <stdio.h>
#include <string.h>

#define N (50)

 typedef struct Monster{
     int ID;
     char name[N];
     char species[N];
     char type[N];
 }MOS;

int createMonster(int n)
{
	MOS m1[n], m2[n];

	printf("set monster number!\n");

	int i = 0;
	for(i=0; i<n; i++)
	{
		printf("第%d個怪獸!\n", i+1);
		printf("ID 名字 種族 戰鬥類型\n");
		scanf("%d %s %s %s", &m1[i].ID, m1[i].name, m1[i].species, m1[i].type);
	}

	printf("怪獸資訊輸入完畢!請儲存!\n");
	
	FILE *fp = fopen("./monster.txt", "wb+");
	if(NULL == fp)
	{
		perror("fopen");  
		return -1;  
	}

	fwrite(m1, sizeof(MOS), n, fp);

	rewind(fp);	//檔案指到開頭

	fread(m2, sizeof(MOS), n, fp);

	for(i=0; i<n; i++)
	{
		printf("%d %s %s %s\n",m2[i].ID, m2[i].name, m2[i].species, m2[i].type);
	}

}

int main(void)
{

	createMonster(3);


	return 0;
}
           

【Printf函數更新】

int fprintf(FILE *stream, const char *format, …);

/*fprintf用法*/
#include <stdio.h>
#include <string.h>

int main(void)
{

	printf("hello world\n");
	int a = 100;
	printf("a = %d\n", a);

	fprintf(stdout, "%d-%d-%d\n", 2121,07,02);

	FILE *fp = fopen("q.txt", "a+");
	if(NULL == fp)
	{
		perror("fopen");
		return -1;
	}

	fprintf(fp, "%d-%d-%d-%d-%d\n", 2121, 07, 02, 11, 56);

	return 0;
}
           

int sprintf(char *str, const char *format, …);

/*sprintf用法*/
#include <stdio.h>
#include <string.h>

int main(void)
{

	char buf[100];


	sprintf(buf, "%d-%d-%d\n", 2121,07,02);

	printf(buf, "%s\n", 2121, buf);

	return 0;
}
           

int sscanf(const char *str, const char *format, …);

/*sscanf的用法*/
#include <stdio.h>
#include <string.h>

int main(void)
{
	int a, b, c;

	sscanf("2121-7-2", "%d-%d-%d", &a, &b, &c);
	printf("%d %d %d\n", a, b, c);
	
	float a1, b1, c1;

	sscanf("3.14-1.5-9.27", "%f-%f-%f", &a1, &b1, &c1);
	printf("%f %f %f\n", a1, b1, c1);

	return 0;
}
           

FILE *freopen(const char *path, const char *mode, FILE *stream);

#incude <time.h>

time_t time(time_t *t);

#include <time.h>

struct tm *localtime(const time_t *timep);

struct tm {

int tm_sec;

};

示例:擷取系統時間

練習:擷取系統時間,并将系統時間 列印輸出到 檔案中

[2020-05-07 14:07:30]

[2020-05-07 14:07:31]

[2020-05-07 14:07:32]

#include <stdio.h>
#include <string.h>
#include <time.h>

int main(void)
{
	while(1)
	{
		time_t t;

		time(&t);

		//printf("tim:%d\n",m);

		struct tm *p = localtime(&t);
		printf("%d %d %d\n", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday);

		sleep(1);
	}

	return 0;
}
           
/*更新日志:*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main(int argc, const char *argv[])
{
	while(1)
	{
		FILE *fp = fopen("time.txt", "a");
		if(NULL == fp)
		{
			perror("fopen");
			return -1;
		}


		time_t t;

		time(&t);

		struct tm *p = localtime(&t);

		printf("[%d-%d-%d  %d:%d:%d]\n", p->tm_year + 1900, p->tm_mon+1, p->tm_mday,
							p->tm_hour, p->tm_min, p->tm_sec);

		fprintf( fp ,"[%d-%d-%d  %d:%d:%d]\n", p->tm_year + 1900, p->tm_mon+1, p->tm_mday,
							p->tm_hour, p->tm_min, p->tm_sec);
		fflush(fp);
		sleep(1);

	}
	return 0;
}
           

二、檔案IO

1、檔案IO

檔案描述符

檔案描述符實際上是一個索引值,指向核心為每一個程序所維護的該程序打開檔案的記錄表。當程式打開一個現有檔案或者建立一個新檔案時,核心向程序傳回一個檔案描述符,程序使用它來辨別打開的檔案。在程式設計中,一些涉及底層的程式編寫往往會圍繞着檔案描述符展開。但是檔案描述符這一概念往往隻适用于UNIX、Linux這樣的作業系統。

Linux系統為程式中每個打開的檔案都配置設定一個檔案描述符,檔案IO操作通過檔案描述符來完成。

檔案描述符在形式上是一個順序配置設定的非負整數。從0開始配置設定,依次遞增。比如 0,1,2表示 stdin stdout stderr,一般最大打開的檔案描述符數量為1024(0~1023)

//列印檔案描述符

fdopen: 将檔案描述符轉化為對應的流指針

FILE *fdopen(int fd, const char *mode);

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

/*open/close*/
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fp = open("a.txt", O_RDONLY | O_CREAT, 0666);
	if(fp < 0)
	{
		perror("open");
		return -1;
	}

	printf("open succes!\n");

	close(fp);

	return 0;
}
           

#include <unistd.h>

int close(int fd);

标準IO 檔案IO

r O_RDONLY 隻讀檔案; 且檔案必須存在

r+ O_RDWR 可讀寫檔案; 且且檔案必須存在

w O_WRONLY | O_CREAT | O_TRUNC 隻寫檔案; 若檔案存在則長度清0,擦除檔案之前内容; 檔案不存在則建立

w+ O_RDWR | O_CREAT | O_TRUNC 可讀寫檔案; 若檔案存在則長度清0,擦除檔案之前内容; 檔案不存在則建立

a O_WRONLY | O_CREAT | O_APPEND 追加方式打開隻寫檔案; 檔案不存在則建立; 如果檔案存在,寫入資料追加到檔案尾,保留原先内容

a+ O_RDWR | O_CREAT | O_APPEND 追加方式打開可讀寫寫檔案; 檔案不存在則建立; 如果檔案存在,從頭讀取,寫入資料追加到檔案尾,保留原先内容

實際建立的檔案權限需要經過一個公式計算得到: mode & (~umask)

0777 111 111 111

0002 000 000 010

111 111 111

&

111 111 101

–>

111 111 101 (0775)

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

/*write/read*/
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fp = open("a.txt", O_RDWR | O_CREAT, 0666);	//讀寫方式打開
	if(fp < 0)
	{
		perror("open");
		return -1;
	}

	printf("%d\n", fp);

	printf("open succes!\n");

	char buf[100] = "hello\n";

	int ret = write(fp, buf, sizeof(buf));
	
	printf("ret = %d\n", ret);


	lseek(fp, 0, SEEK_SET);		//指針偏移到頭

	int num = read(fp, buf, ret);

	printf("num = %d, %s\n", num, buf);

	close(fp);

	return 0;
}
           
/*複制檔案*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd1 = open("dict.txt", O_RDONLY);
	if(fd1 < 0)
	{
		perror("open");
		return -1;
	}
	int fd2 = open("3.txt", O_WRONLY | O_CREAT, 0666);
	if(fd2 < 0)
	{
		perror("open");
		return -1;
	}

	char buf[100];

	int n;
	
	while(	(n = read(fd1, buf, 100)) > 0 )
	{
		write(fd2, buf, n);
	}

	printf("copy success!\n");

	close(fd1);
	close(fd2);

	return 0;
}
           
/*查詢檔案路徑下的檔案内容*/
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

#include <dirent.h>

int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("please input %s pathname!\n", argv[0]);
		return -1;
	}

	DIR *dir = opendir(argv[1]);
	if(NULL == dir)
	{
		perror("opendir");
		return -1;
	}

	while(1)
	{
		struct dirent *p = readdir(dir);
		if(p == NULL)
		{
			break;
		}
    		
      	//不顯示.
 		if(strncmp(p->d_name, ".", 1) == 0)
   		continue;	//放棄本次循環,執行下一個循環
     
		printf("%s\n", p->d_name);
	}

	closedir(dir);

	return 0;
}
           
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("%s pathname!\n", argv[0]);
		return -1;
	}

	struct stat my_stat;

	int ret = stat(argv[1], &my_stat);
	if(ret == -1)
	{
		perror("stat");
		return -1;
	}

	printf("size = %ld\n", my_stat.st_size);


	struct passwd *p = getpwuid(my_stat.st_uid);

	printf("%s\n", p->pw_name);


	struct group *q = getgrgid(my_stat.st_gid);

	printf("%s\n", q->gr_name);


	time_t t;
	time(&t);

	struct tm *k = localtime(&my_stat.st_mtime);
	printf("%d-%d-%d %d:%d:%d\n", k->tm_year+1900, k->tm_mon+1, k->tm_mday,
k->tm_hour,k->tm_min,k->tm_sec
);

	if(S_ISREG(my_stat.st_mode))
		printf("-\n");
	else if(S_ISDIR(my_stat.st_mode))
		printf("d\n");
	else if(S_ISCHR(my_stat.st_mode))
		printf("c\n");
	else if(S_ISBLK(my_stat.st_mode))
		printf("b\n");
	else if(S_ISFIFO(my_stat.st_mode))
		printf("p\n");
	else if(S_ISSOCK(my_stat.st_mode))
		printf("s\n");
	else
		printf("l\n");

	
	printf("user %c", (my_stat.st_mode & (1 << 8))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 7))?'w':'-' );
	printf("%c\n", (my_stat.st_mode & (1 << 6))?'x':'-' );

	printf("group %c", (my_stat.st_mode & (1 << 5))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 4))?'w':'-' );
	printf("%c\n", (my_stat.st_mode & (1 << 3))?'x':'-' );

	printf("other %c", (my_stat.st_mode & (1 << 2))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 1))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 0))?'x':'-' );

	puts("");

	return 0;
}
           

#include <sys/types.h>

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

【目錄操作函數】

include <sys/types.h>

#include <dirent.h>

DIR *opendir(const char *name);

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

};

#include <sys/types.h>

#include <dirent.h>

int closedir(DIR *dirp);

示例:檢視目錄下的檔案

【檔案資訊函數】

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

int stat(const char *path, struct stat *buf);

};

将uid 轉換為 使用者名

#include <sys/types.h>

#include <pwd.h>

struct passwd *getpwuid(uid_t uid);

struct passwd {

char pw_name; / username */

char pw_passwd; / user password /

uid_t pw_uid; / user ID /

gid_t pw_gid; / group ID */

char pw_gecos; / user information */

char pw_dir; / home directory */

char pw_shell; / shell program */

};

将gid 轉換為 組名

#include <sys/types.h>

#include <grp.h>

struct group *getgrgid(gid_t gid);

struct group {

char gr_name; / group name */

char gr_passwd; / group password /

gid_t gr_gid; / group ID */

char *gr_mem; / group members */

};

示例:stat.c

檔案類型

{

正常檔案:S_ISREG ‘-’

目錄:S_ISDIR ‘d’

字元裝置:S_ISCHR ‘c’

塊裝置:S_ISBLK ‘b’

管道:S_ISFIFO ‘p’

套接字:S_ISSOCK ‘s’

符号連結:S_ISLNK ‘l’

}

練習: 完成ls -l

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>

int Stat(char *filename)
{
	struct stat my_stat;

	int ret = stat(filename, &my_stat);
	if(ret == -1)
	{
		perror("stat");
		return -1;
	}

	if(S_ISREG(my_stat.st_mode))
		printf("-");
	else if(S_ISDIR(my_stat.st_mode))
		printf("d");
	else if(S_ISCHR(my_stat.st_mode))
		printf("c");
	else if(S_ISBLK(my_stat.st_mode))
		printf("b");
	else if(S_ISFIFO(my_stat.st_mode))
		printf("p");
	else if(S_ISSOCK(my_stat.st_mode))
		printf("s");
	else
		printf("l");

	
	printf("%c", (my_stat.st_mode & (1 << 8))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 7))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 6))?'x':'-' );

	printf("%c", (my_stat.st_mode & (1 << 5))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 4))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 3))?'x':'-' );

	printf("%c", (my_stat.st_mode & (1 << 2))?'r':'-' );
	printf("%c", (my_stat.st_mode & (1 << 1))?'w':'-' );
	printf("%c", (my_stat.st_mode & (1 << 0))?'x':'-' );

    printf(" %ld", my_stat.st_nlink);

    struct passwd *p = getpwuid(my_stat.st_uid);
    printf(" %s", p->pw_name);

    struct group *q = getgrgid(my_stat.st_gid);                              
    printf(" %s", q->gr_name); 

	printf("%6ld ", my_stat.st_size);

	time_t t;
	time(&t);

	struct tm *k = localtime(&my_stat.st_mtime);
	printf("%d月  %2d %d:%d ", k->tm_mon+1, k->tm_mday,k->tm_hour,k->tm_min);

	return 0;
}

int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("please input %s pathname!\n", argv[0]);
		return -1;
	}

	DIR *dir = opendir(argv[1]);
	if(NULL == dir)
	{
		perror("opendir");
		return -1;
	}

	while(1)
	{
		struct dirent *p = readdir(dir);
		if(p == NULL)
			break;
		if( strncmp(p->d_name, ".", 1) == 0)
			continue; 
		Stat(p->d_name);
		printf("%s\n", p->d_name);
	}

	closedir(dir);

	return 0;
}

           
IO進線程程式設計一、标準IO二、檔案IO三、程式四、線程五、程式間的通信跳轉:IO進線程概念理論!跳轉:上一篇,資料結構與算法!跳轉:下一篇,網絡程式設計!

【庫的制作】

1、什麼是庫

/lib/i386-linux-gnu

/usr/include

  1. 庫是一種加密的二進制檔案
  2. 需要被作業系統載入記憶體運作
  3. 相比于可執行程式,它不可以直接運作
  4. window 和 linux 都有自己的庫,但是不相容
  5. 庫有兩種,1. 靜态庫 2. 共享庫(又叫動态庫)

    了解: 靜态庫 動态庫

    linux *.a *.so

    window *.lib *.dll

    $ gcc -E a.c -o a.i // 預編譯 (預處理)

    $ gcc -s a.i -o a.s // 編譯 C文法裝換為彙編文法

    $ gcc -c a.s -o a.o // 彙編 彙編文法轉換為二進制機器碼

    $ gcc a.o -o a.out // 可執行程式

2、靜态庫的制作和使用

1. 制作

$ gcc -c hello.c -o hello.o

$ ar -crs libxxx.a hello.o

靜态庫的命名規範:

必須以lib開頭,緊跟庫的名字,跟擴充名 .a

例如: libxxx.a

  1. 使用

    $ gcc main.c -L路徑 -lxxx

    -L: 指定靜态庫所在的目錄

    -l: 指定靜态庫的名字 xxx部分

  2. 運作

    $ ./a.out

    優點:a.out 運作後不需要庫,可以直接運作

    缺點:

    每個a.out都要包含庫,體積較大, 浪費資源;

    對程式更新,部署,釋出帶來麻煩;

3、動态庫的制作和使用

1. 制作

$ gcc -fPIC -c add.c -o add.o

$ gcc -shared -o libxxx.so add.o

動态庫的命名規範:

必須以lib開頭,緊跟庫的名字,跟擴充名 .so

例如: libxxx.so

  1. 使用

    $ gcc main.c -L路徑 -lxxx

    $ ldd a.out # 用于檢視可執行程式依賴的動态庫有哪些

  2. 運作

    $ ./a.out # 會報錯

    動态庫的搜尋方式(3種,任意選一種):

    1. 将動态庫拷貝到 /lib/ 或者 /usr/lib/

      $ sudo cp libxxx.so /usr/lib/

    2. export LD_LIBRARY_PATH=.或者so所在的路徑
    3. pwd

      cd /etc/ld.so.conf.d

      ls

      sudo vim my.conf

      添加路徑

      sudo ldconfig生效

    特點:在編譯時不會連結到可執行檔案中,隻是再其中儲存一個索引,在運作時,才真正的連結(動态),是以可執行程式體積小。

    優點:

    a.out 體積較小, 節約資源;

    隻需要修改.so動态庫,有利于程式的更新,部署,釋出;

    缺點:a.out 運作後需要庫,不能直接運作。

三、程序

3.1.1、理論知識:

(1)、程序:程式的一次執行過程,是系統資源配置設定和排程的最小機關,是動态的。

(2)、程式:存儲在磁盤上的指令的有序集合,是靜态的。

3.1.2、程序的内容:

資料段:全局變量,malloc配置設定的空間

正文段:程式的代碼行

堆棧段:函數傳回值、參數、局部變量等

3.1.3、程序的記憶體管理

正文段、使用者資料段、系統資料段。

3.1.4、程序号(PID)

唯一的辨別一個程序

3.1.5、程序的類型:

(1)互動程序:由 shell 控制和運作的程序,也就是在終端中運作産生的程序。

ctrl+z: 使程序進入挂起狀态,被挂起的程序被稱為作業

jobs -l: 檢視挂起的程序

bg % 作業号: 使這個作業恢複前台運作,不能Ctrl+c結束,将挂起的程序在背景運作。

fg % 作業号: 使這個作業恢複前台運作,可以Ctrl+c結束,把背景運作的程序發在前台運作。

&: 與bg一樣

kill -l: 檢視信号種類

kill -9 PID: 殺死程序

ps -ajx: 檢視程序的運作狀态

(2)批處理程序:不屬于某個終端,是在隊列中被順序執行,作業系統開機時就有很多批處理程序。

一般做運維的。

(3)守護程序:與終端無關,開機時自動執行,系統關機時 結束。(系統時間、系統中的伺服器)。

守護程序(1 int)

3.16、程序的狀态

(1)就緒态:程序準備運作

(2)運作态®:程序正在運作

(3)等待态:(休眠态)程序正在等待一件事情或者系統資源

可中斷(S):如果程序收到信号會醒來 ctrl+c

不可中斷(D):如果程序收到信号不會醒來

(4)停止态(T):程序被中止的狀态,還可以重新運作,此時程序被中止SIGSTOP

(5)死亡态(Z):程序被終止(終結),但是task_struct還存在,也就是程序資源還沒有回收。、

已終止的程序、僵屍程序

但還在程序向量數組中占有一個task_struct結構

task_struct{

pid_t pid;

R;

};

< 高優先級

N 低優先級

L 有些頁被鎖

s 會話組組長

+位于前台的程序組

l 多線程,克隆線程

Ctrl + alt +f1 -f6: 打開字元終端

為了不同使用者使用計算機

結束字元終端: alt + f7

Ctrl + shift + n:打開目前路徑終端

top -p PID: 動态檢視程序狀态

renice -5 PID: 改變程序的NI值(預設0)

IO進線程程式設計一、标準IO二、檔案IO三、程式四、線程五、程式間的通信跳轉:IO進線程概念理論!跳轉:上一篇,資料結構與算法!跳轉:下一篇,網絡程式設計!

3.1.8、程序相關的函數

1、建立子程序

#include <sys/types.h>

#include <unistd.h>

函數原型:pid_t fork(void)

從fork函數往下分成兩個程序開始運作。

父程序和子程序執行順序的随機的。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(void)
{
	printf("開始建立程序:\n");

	pid_t pid;	//程序号

	fork();		//建立程序

	while(1)
	{
		printf("abc\n");
		sleep(1);
	}

	return 0;
}
/*
運作結果:每1秒列印2次abc

*/
           
/*建立一個程序*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, const char *argv[])
{
	printf("開始建立程序:\n");

	pid_t pid;		//程序号

	pid = fork();		//建立程序

	if(pid == -1)		//-1,失敗
	{
		perror("fork");
		exit(-1);
	}
	else if(pid > 0)	//父程序
	{
		while(1)
		{
			printf("father is running\n");
			sleep(3);
		}
	}
	else	//子程序
	{
		while(1)
		{
			printf("son is running\n");
			sleep(3);
		}

	}

	return 0;
}
/*
運作結果:每3秒列印子程序和父程序,父程序和子程序運作先後順序随機的。

*/
           

fork函數特性:

(1)、子程序建立時,幾乎拷貝了父程序全部内容,包括代碼段、資料段、堆棧段、檔案描述符、虛拟位址空間

(2)、同一個父程序建立的子程序都是屬于同一個程序組 pkill -9 -g PGID

(3)、程序是管理資源的最小機關

思考:會建立多少個程序,程序之間的關系是什麼樣的?

int main(void)

{

fork();

fork();

}

答:會建立4個多少個程序,關系如下圖。

IO進線程程式設計一、标準IO二、檔案IO三、程式四、線程五、程式間的通信跳轉:IO進線程概念理論!跳轉:上一篇,資料結構與算法!跳轉:下一篇,網絡程式設計!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, const char *argv[])
{
	pid_t pid1;	

	pid1 = fork();	

	if(pid1 < 0)	
	{
		perror("fork");
		exit(-1);
	}
	else if(pid1 > 0)	
	{
		printf("pid1: = %d\n", getpid());
	}
	else
	{
		printf("pid2: = %d\n", getpid());
	}


	pid_t pid2;

	pid2 = fork();	

	if(pid2 < 0)	
	{
		perror("fork");
		exit(-1);
	}
	else if(pid2 > 0)	
	{
		printf("pid3: = %d\n", getpid());
	}
	else
	{
		printf("pid4: = %d\n", getpid());
	}

	while(1);

	return 0;
}
/*
運作結果:建立了4個程序,類似二叉樹,如上圖。
pid1: = 4786
pid3: = 4786
pid4: = 4788
pid2: = 4787
pid3: = 4787
pid4: = 4789

*/
           

2、相關考點概念:

僵屍程序: 子程序先于父程序結束,父程序沒有回收子程序資源。

孤兒程序: 父程序先于子程序結束,子程序被系統 init.d 托管。

3、在程序中執行另外一個可執行程式,産生新的程序。

原程序中除了程序号,其他的都将被替換。

(1)、

int execl(const char *path, const char *arg, …);

以 l結尾,表示第一個參數必須是 可執行檔案名包含路徑,後面以清單形式填寫參數

第二個參數,必須是可執行程式的 執行指令

第三個參數,可以是 執行指令的 參數選項

最後一個,必須以 NULL 結束

(2)、

int execlp(const char *file, const char *arg, …);

以 lp結尾表示,

第一個參數,可執行檔案名,後面以清單形式填寫參數,自動搜尋檔案的路徑

第二個參數,必須是可執行程式的 執行指令

第三個參數,可以是 執行指令 的 參數選項

最後一個,必須以 NULL 結束

4、結束程序的函數

(1)、庫函數 #include <stdlib.h>

int exit(int status);

結束程序,并且重新整理緩沖區

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void fun()
{
	printf("ccccccccc");

	exit(0); //結束程序  C庫函數 重新整理緩沖區
}

int main(int argc, const char *argv[])
{
	printf("aaaaaa\n");

	fun();

	printf("bbbbbb\n");

	return 0;
}
/*
運作結果:
aaaaaa
ccccccccc

*/
           

(2)、系統調用 #include <unistd.h>

int _exit(int status);

結束程序,不會重新整理緩沖區

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void fun()
{
	printf("ccccccccc");

	_exit(0); //結束程序  系統調用 不會重新整理緩沖區
}

int main(int argc, const char *argv[])
{
	printf("aaaaaa\n");

	fun();

	printf("bbbbbb\n");

	return 0;
}
/*
運作結果:
aaaaaa


*/
           

5、僵屍程序的解決方法 wait 、 waitpid、信号

(1)、pid_t wait(int *status);

功能: 阻塞父程序,等待任何一個子程序結束,回收資源

頭檔案:

#include <sys/types.h>

#include <wait.h>

參數:

status: 儲存子程序退出時的狀态或者 exit函數的實參,

可以使用 固定的宏函數實作(WIFEXITED, WEXITSTATUS)

WEXITSTATUS(status) 擷取子程序傳回值,

WIFEXITED(status) 判斷子程序是否正常結束

傳回值:

成功: 傳回退出的子程序PID

失敗: 傳回 -1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, const char *argv[])
{
	pid_t pid;	

	pid = fork();	

	if(pid < 0)	
	{
		perror("fork");
		exit(-1);
	}
	else if(pid > 0)	//父程序	
	{
		printf("father wait...\n");

		int status;

		//wait(NULL);	//阻塞:可以直接使用這段
		wait(&status);

		printf("%d %d\n", WEXITSTATUS(status), WIFEXITED(status));

		printf("-------------->\n");
		while(1);
	}
	else	//子程序
	{
		sleep(5);
		exit(0);
	}

	return 0;
}	
/*
運作結果:
father wait...
0 1
-------------->

*/
           

== (2)、僵屍程序/孤兒程序。==

子程序先與父程序退出—父程序未回收資源—子程序會變成僵屍程序

危害:占用程序号、記憶體空間、pcb程序控制塊等

解決:wait / waitpid / 信号

注意:任何程序結束都會變成僵屍程序,隻是時間有長有短

父程序先與子程序退出—子程序會變成孤兒程序—被init程序接管(收養)

init程序:系統啟動後運作的第一個使用者程序,pid=1,會定期掃描系統,收養孤兒程序。

注意:孤兒程序一般沒什麼危害

(3)、pid_t waitpid(pid_t pid, int *status, int option);

功能:

阻塞父程序,等待子程序結束,回收資源

頭檔案:

#include <sys/types.h>

#include <wait.h>

參數:

pid : -1, 等價于wait,回收任何一個退出的子程序資源

>0, 為子程序PID, 表示指定要回收的子程序

status: 儲存子程序退出時的狀态或者 exit函數的實參,

可以使用 固定的宏函數實作(WIFEXITED, WEXITSTATUS)

option: WNOHNG : 非阻塞模式

0 :阻塞模式,等價于 wait

傳回值:

成功:

阻塞模式下: 傳回退出的子程序PID

非阻塞模式下:傳回 0

失敗: 傳回 -1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, const char *argv[])
{
	pid_t pid;	

	pid = fork();	

	if(pid < 0)	
	{
		perror("fork");
		exit(-1);
	}
	else if(pid > 0)	//父程序	
	{
		printf("father wait...\n");

		//wait(NULL);	//阻塞
		waitpid(-1, NULL, WNOHANG);	//WNOHANG非阻塞
  						//-1:所有子程序  			

		printf("-------------->\n");
		while(1);
	}
	else	//子程序
	{
		sleep(5);
		exit(10);
	}

	return 0;
}
/*
運作結果:
father wait...
-------------->

*/
           

5、exec函數簇

(1). 概念:

函數族提供了一種在程序中啟動另一個程式執行的方法。

它可以根據指定的檔案名或目錄名找到可執行檔案,并用它來取代原調用程序的資料段、代碼段和堆棧段,在執行完之後,原調用程序的内容除了程序号外,其他全部被新程式的内容替換了。

另外,這裡的可執行檔案既可以是二進制檔案,也可以是Linux下任何可執行腳本檔案。

比如bash用到了exec函數來執行我們的可執行檔案。

(2). 在Linux中使用exec函數族主要有以下兩種情況

當程序認為自己不能再為系統和使用者做出任何貢獻時,就可以調用任何exec 函數族讓自己重生。

如果一個程序想執行另一個程式,那麼它就可以調用fork函數建立一個程序,然後調用任何一個exec函數使子程序重生。

==(3). 函數 ==

#include <unistd.h>

int execl(const char *path, const char *arg, …);

int execv(const char *path, char *const argv[]);

int execlp(const char *file, const char *arg, …);

int execvp(const char *file, char *const argv[]);

int execle(const char *path, const char *arg, …, char *const envp[]);

int execve(const char *path, char *const argv[], char *const envp[]);

傳回值:

成功不傳回

失敗傳回 -1 更新 errno

注意:

exec函數的參數表傳遞方式以函數名的第五位字母來區分:

字母為"l"(list)的表示逐個列舉的方式;

字母為"v"(vertor)的表示将所有參數構造成指針數組傳遞;

以p結尾的函數可以隻給出檔案名

以"e"(enviromen)結尾的兩個函數execle、execve就可以在envp[]中設定目前程序所使用的環境變量

使用execle和execve可以自己向執行程序傳遞環境變量,但不會繼承Shell程序的環境變量

事實上,這6個函數中真正的系統調用隻有execve,其他5個都是庫函數,它們最終都會調用execve這個系

/*execl	用法*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	int ret = execl("/bin/ls", "ls", "-l", NULL);
	if(ret == -1)
	{
		perror("execl");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:
-rwxrwxr-x 1 farsight farsight 7269  7月  6 09:30 a.out
-rw-rw-r-- 1 farsight farsight  617  7月  6 09:30 fork.c


*/
           
/*execv	用法*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	char *arg[] = {"ls", "-l", NULL};

	int ret = execv("/bin/ls", arg);
	if(ret == -1)
	{
		perror("execv");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:
-rwxrwxr-x 1 farsight farsight 7269  7月  6 09:30 a.out
-rw-rw-r-- 1 farsight farsight  617  7月  6 09:30 fork.c


*/
           
/*execlp	用法*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	int ret = execlp("ls", "ls", "-l", NULL);
	if(ret == -1)
	{
		perror("execlp");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:
-rwxrwxr-x 1 farsight farsight 7269  7月  6 09:30 a.out
-rw-rw-r-- 1 farsight farsight  617  7月  6 09:30 fork.c

*/
           
/*execp	用法*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	char *arg[] = {"ls", "-l", NULL};

	int ret = execvp("ls", arg);
	if(ret == -1)
	{
		perror("execvp");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:
-rwxrwxr-x 1 farsight farsight 7269  7月  6 09:30 a.out
-rw-rw-r-- 1 farsight farsight  617  7月  6 09:30 fork.c
*/
           
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>


int main(int argc, const char *argv[])
{
	char *envp[] = {"PATH=xxx", "USER=yyy", NULL};

	int ret = execle("/bin/ls/env", "env", envp);
	if(ret == -1)
	{
		perror("execle");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:???????????????
PATH=xxx
USER=yyy

*/
           
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	char *arg[] = {"env", NULL};
	char *envp[] = {"PATH=xxx", "USER=yyy", NULL};

	int ret = execve("/usr/bin/env", arg, envp);
	if(ret == -1)
	{
		perror("execve");
		exit(-1);
	}
	
	printf("------->\n");

	return 0;

}
/*
運作結果:
PATH=xxx
USER=yyy

*/
           
/*my-shell*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main(int argc, const char *argv[])
{
	char buf[100];
	char *arg[100];
	int i;
	pid_t pid;

	while(1)
	{
		printf("input>");
		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = '\0';

		if(strncmp(buf, "quit", 4) == 0)
			break;
		arg[0] = strtok(buf, " ");

		i = 1;

		while((arg[i] = strtok(NULL, " ")) != NULL)
		{
			i++;
			pid = fork();
			if(pid == -1)
			{
				perror("fork");
				exit(-1);
			}
			else if(pid == 0)
			{
				int ret = execvp(arg[0], arg);
				if(ret == -1)
				{
					perror("execvp");
					exit(-1);
				}
			}
			else
			{
				wait(NULL);
			}
		}
	}
}
/*
運作結果:可以實作shell
input>	ls-l

*/
           

6、守護程序daemon

(1).守護程序:

在linux中與使用者互動的界面叫終端,從終端運作起來的程式都依附于這個終端,

當終端關關閉時,相應的程序都會被關閉,守護程序可以突破這個限制。

(2).特點:

在背景服務的程序

生存期很長

守護程序獨立于控制終端

比如:init程序 pid=1 開機運作 關機才結束

(3).守護程序建立流程:

1. 建立子程序,父程序退出 (擺脫父程序的控制)

fork(void);

2. 在子程序中建立新會話 (拜托終端控制,将孤兒程序獨立出來)

setsid(void);

3. 修改工作目錄

chdir("");

4. 修改umask (增加安全性)

umask();

5. 關閉檔案描述(回收資源)

close();

根據具體功能要求,實作算法

/*建立一個守護程序*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <time.h>

int main(int argc, const char *argv[])
{
	pid_t pid;
	pid = fork();

	if(pid == 0)		//子程序
	{
		setsid();	//建立新的會話

		chdir("./");	//修改工作目錄

		umask(0);	//設定檔案權限掩碼,增加安全性

		close(0);	//關閉檔案描述符
		close(1);	//關閉檔案描述符
		close(2);	//關閉檔案描述符

		FILE *fp = fopen("./time.txt", "a");
		while(1)
		{
			time_t t;
			time(&t);

			fprintf(fp, "%s", ctime(&t));

			fflush(fp);

			sleep(2);
		}
	}
	else			//父程序退出
	{
    	exit(0);
	}
}
/*
運作結果:系統不關閉,程式一直運作,可以通過kill殺掉程序
Tue Jul  6 11:13:26 2021
Tue Jul  6 11:13:28 2021
Tue Jul  6 11:13:30 2021
Tue Jul  6 11:13:32 2021
Tue Jul  6 11:13:34 2021
Tue Jul  6 11:13:36 2021

*/
           

四、線程

4.1、概念:

輕量級的程序,共享同一位址空間的多個任務。

4.2、特點:

1、優點:

1、同一程序中的多個線程,共享該程序位址空間,節約系統空間資源。

2、同一程序中的多線程,進行任務切換時,提高切換的效率,

避免額外的重新整理cache和TLB頁表的系統時間。

3、同一程序中的多個多線程之間進行資料傳遞比較友善,可以使用全局變量。

2、缺點:

1、同一程序中的一個線程意外結束或死掉,那麼該程序中的其他線程都不能繼續運作。

2、同一程序中多個線程,容易競争共享資源,也就是資源搶占問題。

3、線程所屬的程序結束,那麼線程就不存在。

4.3、庫函數

1、建立線程

int pthread_create(pthread_t *thread, const pthread_attr_t attr,

void * ( routine)(void *), void *arg)

頭檔案:

#include <pthred.h>

參數:

thread :儲存線程id 号的位址

attr :設定線程的屬性,通常使用 NULL 預設屬性

routine :線程的執行函數名,執行函數為 void 類型的指針函數

arg :要傳遞到線程的資料,如果不傳資料使用 NULL

傳回值:

0 :成功

-1 :失敗,并設定錯誤資訊

程式編譯時,後面必須 -lpthread

== eg: gcc xianc.c -lpthread==

/*pthread_create 建立線程*/
#include <stdio.h>

//線程函數fun
void *fun(void *arg)
{
    while(1)
    {
     	printf("fun is running\n");
     	sleep(3);       
    }
}

//主線程
int main()
{
  pthread_t tid;//線程号
  int ret = pthread_create(&tid, NULL, fun, NULL);
  if(ret < 0)
  {
    perror("pthread_create");
    return -1;        
  }
  while(1)
  {
       	printf("main is running\n");
     	sleep(3);  
  }
  return 0;
}
/*
運作結果:
main is running
fun is running
main is running
fun is running
main is running
fun is running
main is running
fun is running

*/
           

回收線程資源

int pthread_join(pthread_t thread, void **retval);

/*回收線程資源:pthread_join,阻塞*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

//線程函數fun
void *fun(void *arg)
{
	int i = 5;
	while(i--)
	{
		printf("fun is running\n");
		sleep(3);       
	}
}

//主線程
int main()
{
	pthread_t tid;	//線程号
	int ret = pthread_create(&tid, NULL, fun, NULL);
	if(ret < 0)
	{
		perror("pthread_create");
		return -1;        
	}
	
	printf("--------------------->\n");

	pthread_join(tid, NULL);	//等待子線程結束,回收資源

	while(1)
	{
		printf("main is running\n");
		sleep(3);  
	}
	return 0;
}
/*
運作結果:
--------------------->
fun is running
fun is running
fun is running
fun is running
fun is running
main is running
main is running
main is running
main is running
main is running
main is running

*/
           

int pthread_detach(pthread_t thread);

/*回收線程資源:pthread_detach,非阻塞*/ 
/*工程中常用的,配套的,有建立和系統自動回收資源*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

//線程函數fun
void *fun(void *arg)
{
	int i = 5;
	while(i--)
	{
		printf("fun is running\n");
		sleep(3);       
	}
}

//主線程
int main()
{
	pthread_t tid;	//線程号
	int ret = pthread_create(&tid, NULL, fun, NULL);
	if(ret < 0)
	{
		perror("pthread_create");
		return -1;        
	}
	
	printf("--------------------->\n");

	//pthread_join(tid, NULL);	//等待子線程結束,回收資源
	pthread_detach(tid);	//主線程和子線程分離開來,子線程結束,系統自動回收資源,非阻塞

	while(1)
	{
		printf("main is running\n");
		sleep(3);  
	}
	return 0;
}
/*
運作結果:
--------------------->
main is running
fun is running
main is running
fun is running
main is running
fun is running
main is running
fun is running
main is running
fun is running
main is running
main is running
main is running
main is running
main is running

*/
           

結束子線程

void pthread_exit(void *retval);

注意:

pthread_exit == return

線程傳參{

位址

}

/*pthread_exit 用法*/
/*位址傳遞*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

//線程函數fun
void *fun(void *arg)
{
	int i = 3;
	while(i--)
	{
		printf("fun is running\n");
		sleep(1);       
	}

	pthread_exit(arg);
}

//主線程
int main()
{
	pthread_t tid;	//線程号

	char a[10] = "hello";

	int ret = pthread_create(&tid, NULL, fun, (void *)a);
	if(ret < 0)
	{
		perror("pthread_create");
		return -1;        
	}
	
	void *arg;

	pthread_join(tid, &arg);  //等待子線程結束,回收資源
	printf("arg = %s\n", (char *)arg);

	return 0;
}
/*
運作結果:
fun is running
fun is running
fun is running
arg = hello

*/
           
/*傳參:值傳參*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

//線程函數fun
void *fun(void *arg)
{
   printf("%d\n", (int)arg);
	int i = 3;
	while(i--)
	{
		printf("fun is running\n");
		sleep(1);       
	}

	pthread_exit(arg);
}

//主線程
int main()
{
	pthread_t tid;	//線程号

	char a[10] = "hello";
	int b = 100;

	int ret = pthread_create(&tid, NULL, fun, (void *)b);
	if(ret < 0)
	{
		perror("pthread_create");
		return -1;        
	}
	
	void *arg;

	pthread_join(tid, &arg);  //等待子線程結束,回收資源
	printf("arg = %d\n", (int)arg);

	return 0;
}
/*
運作結果:
arg = 100
fun is running
fun is running
fun is running
arg = 100

*/
           

int pthread_cancel(pthread_t thread);

/*pthread_cancel 關閉某個線程的請求*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

//線程函數fun
void *fun(void *arg)
{ 
	while(1)
	{
		printf("fun is running\n");
		sleep(1);       
	}

	pthread_exit(arg);
}

//主線程
int main()
{
	pthread_t tid;	//線程号

	int ret = pthread_create(&tid, NULL, fun, NULL);
	if(ret < 0)
	{
		perror("pthread_create");
		return -1;        
	}
	
	pthread_detach(tid);

	char buf[100];
	while(1)
	{
		gets(buf);
		if(strcmp(buf, "q") == 0)
		{
			pthread_cancel(tid);
		}
		if(strcmp(buf, "Q") == 0)
		{
			exit(0);
		}
		printf("buf = %s\n", buf);
	}

	return 0;
}
/*
運作結果:q關閉某個線程的請求,Q退出程式的執行
fun is running
fun is running
fun is running
fun is running
q
buf = q
Q

*/
           

優點:線程間很容易進行通信

通過全局變量實作資料共享和交換

缺點:多個線程同時通路共享對象

   時需要引入同步和互斥機制

同步和互斥 :保護共享資源,避免竟态

同步:多個任務按理想的順序/步調來進行

互斥:不能同時通路

都是為了避免竟态:多個任務同時通路共享資源

線程間的通信機制:

1、同步通信:

多個任務按照某種約定的順序共同配合的完成一件事情。線程中使用 信号量來實作。

2、信号量:

系統中的資源數量。

3、api接口:

1、初始化信号量

==int sem_init(sem_t *sem, int pshared, unsigned int value) ==

頭檔案:

#include <semaphore.h>

參數:

sem : 存儲信号量的位址

pshared : 信号量的使用範圍(0:線程中使用,非0:程序中使用)

value : 信号量的初始值

傳回值:

成功: 傳回 0

失敗: 傳回-1

2、p操作,申請資源,相當于消費者

int sem_wait(sem_t *sem);

功能:

申請資源(也就是判斷信号量的值)

如果信号量值為 0, 阻塞,當信号量值 不為 0,被喚醒繼續運作

如果信号量值為 >0, 非阻塞

資源申請成功,信号量值 -1

參數:

sem : 信号量

傳回值:

成功 :0

失敗 :-1

3、v操作,釋放資源,相當于生産者

int sem_post(sem_t *sem);

功能:

釋放資源(也就是将信号量的值 + 1)

如果系統中有等待該資源的任務,就立馬喚醒該任務繼續運作

如果系統中沒有等待該資源的任務,系統中信号量的數值 + 1

參數:

sem :信号量

傳回值:

成功: 0

失敗:-1

P -1 申請資源 任務繼續運作 本來就是0 就會阻塞

V +1 釋放資源

/*同步通信,信号量*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <semaphore.h>

sem_t sem;	//信号量

//線程函數fun
void *fun(void *arg)
{
	int i = 5;
	while(i--)
	{
		printf("fun is running\n");
		sleep(1);       
	}

	sem_post(&sem);	//信号量+1
}

//主線程
int main()
{
	pthread_t tid;	//線程号

	pthread_create(&tid, NULL, fun, NULL);


	sem_init(&sem, 0, 0);	//信号量初始化,((&sem), (0-線程,非0-程序),(設定處置為0)).

	printf("aaaaaaaaaaaaaa\n");

	sem_wait(&sem);

	printf("bbbbbbbbbbbbbb\n");
	
	pthread_detach(tid);	//信号量-1,遇到0時阻塞

	return 0;
}
/*
運作結果:
aaaaaaaaaaaaaa
fun is running
fun is running
fun is running
fun is running
fun is running
bbbbbbbbbbbbbb

*/
           
/*輸入字元計算字元長度*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <semaphore.h>	//信号量頭檔案

sem_t sem;	//信号量

char buf[100];	//全局變量

//線程函數fun
void *fun(void *arg)
{
	int i = 5;
	while(1)
	{
		sem_wait(&sem);	//-1,為阻塞
		printf("%d\n", strlen(buf)-1);
	}
}

//主線程
int main()
{
	pthread_t tid;	//線程号

	pthread_create(&tid, NULL, fun, NULL);
 


	sem_init(&sem, 0, 0);

	do
	{
		fgets(buf, sizeof(buf), stdin);
		sem_post(&sem);	//+1
	}
	while(strncmp(buf, "quit", 4) != 0);

	return 0;
}
/*
運作結果:
hello
5
open
4
haryou
6

*/
           

4、臨界資源:

多個任務共享的全局資料。

5、臨界區:

通路操作臨界資源的代碼行。

6、鎖粒度:

互斥鎖保護的臨界區的大小。臨界區越大,鎖粒度就越大。

7、保護臨界資源的方法:互斥鎖

1、互斥鎖目的:

保護臨界資源,保證資料的完整性,一個線程使用臨界資源中,另一個線程就不能使用。

2、api接口:

1、初始化互斥鎖

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr) ;

頭檔案:

#include <pthread.h>

參數:

mutex : 儲存互斥鎖id 的位址

attr : 互斥鎖的屬性,一般寫 NULL

傳回值:

成功:傳回 0

失敗:傳回-1

2、加鎖(申請互斥鎖)

int pthread_mutex_lock(pthread_mutex_t *mutex)

參數:

mutex :互斥鎖

傳回值:

成功:傳回 0

失敗:傳回-1

3、解鎖鎖(釋放互斥鎖)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

參數:

mutex :互斥鎖

傳回值:

成功:傳回 0

失敗:傳回-1

3、互斥鎖使用時,需要注意:

1、同一臨界資源的所有臨界區,必須使用同一把互斥鎖。

2、每個臨界區之前加鎖,臨界區最後必須解鎖,不然會産生死鎖。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int a = 0, value1, value2; //全局變量

pthread_mutex_t mutex; //互斥鎖

void *fun1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);   //加鎖 在全局變量上下寫
		if(value1 != value2)
		{
			printf("a=%d value1=%d value2=%d\n", a, value1, value2);
		}
		pthread_mutex_unlock(&mutex); //解鎖
	}
}

void *fun2(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);   //加鎖 在全局變量上下寫
		a++;
		value1 = a;
		value2 = a;
		pthread_mutex_unlock(&mutex); //解鎖
	}
}

int main(int argc, const char *argv[])
{
	pthread_t tid1, tid2;

	pthread_mutex_init(&mutex, NULL); //初始化互斥鎖
	
	pthread_create(&tid1, NULL, fun1, NULL);
	pthread_detach(tid1);

	pthread_create(&tid2, NULL, fun2, NULL);
	pthread_detach(tid2);


	while(1);

	return 0;
}
           

五、程序間的通信

51、概念:

1、傳統程序間通信方式

無名管道、有名管道、信号

2、(System V5)IPC對象通信方式

共享記憶體、消息隊列、信号燈集

3、BSD套接字通信

網絡程式設計 socket 通信

5.2、無名管道:

1、定義

是一個在核心中存在的特殊檔案(看不到檔案名),使用檔案IO進行資料互動。

2、特點:

1、是一種半雙工通信(資料隻能一個方向傳遞),有固定的的讀端和寫端。

2、必須在具有親緣關系的程序之間使用

3、api接口

1、建立無名管道檔案

int pipe(int fd[2]);

頭檔案:

#include <unistd.h>

參數:

fd :數組名,用來儲存讀端和寫端的檔案描述符

傳回值:

成功: 傳回 0

失敗: 傳回-1

pipefd[2] :無名管道的兩個檔案描述符,int型的數組,大小為2,

pipefd[0]為讀端,pipefd[1]為寫端

4、注意:

1、管道中沒有資料,讀端會阻塞等待,直到有資料

2、讀端不存在時,寫端寫入資料,會收到核心的SIGPIPE信号,終止程序的運作。

3、管道中緩沖區滿了,寫端寫入資料将會阻塞,直到讀端讀取資料。緩沖區64k = 1024*64

5、無名管道的特點:

a、沒有名字,是以無法使用open()打開

b、隻能用于親緣程序間(如父子程序、兄弟程序、祖孫程序等)通信

c、半雙工工作方式,讀寫端是分開的,pipefd[0]為讀端,pipefd[1]為寫端

d、是一種特殊的檔案,隻存在記憶體中,由核心進行管理,随程序生,随程序死

e、對于它的讀寫可以使用檔案IO如read、write函數

f、無名管道的操作屬于一次性操作,如果對無名管道進行讀操作,資料會被全部讀走

6、管道讀寫注意事項:

1> 當管道中無資料時,執行讀操作,讀操作阻塞

2>向管道中寫入資料時,linux将不保證寫入的原子性,管道緩沖區一有空閑區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀走管道緩沖區中的資料,那麼寫操作将一直阻塞。

3>對無名管道的操作,類似一個隊列,後寫入的資料不會覆寫之前的資料,會在其後面存儲,讀取完的資料會從管道裡面移除

4>向無名管道中寫資料,将讀端關閉,管道破裂,程序收到信号(SIGPIPE),預設這個信号會将程序殺死

單工:固定一種方向進行通信 廣播

半雙工:方向不定,但是同一時間隻能由一端發送到另一端 對講機

全雙工:通信方向都可以,同時可以發送也可以接收 電話

/*一個程序的讀寫*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <unistd.h>

int main()
{
	int fd[2];	//無名管道

	int ret = pipe(fd);
	if(ret == -1)
	{
		perror("pipe");
		return -1;
	}
	//f[1],寫端
	
	write(fd[1], "hello", 5);

	char buf[100] = {0};
    
    //f[0],讀端
	read(fd[0], buf, sizeof(buf));

	printf("buf = %s\n", buf);

	close(fd[0]);
	close(fd[1]);
}
/*
運作結果:
buf = hello

*/
           
/*一個管道讀,一個管道寫*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <unistd.h>

int main()
{
	//pipe
	int fd[2];

	int ret = pipe(fd);
	if(ret < 0)
	{
		perror("pipe");
		return -1;
	}

	//fork
	pid_t pid;
	pid = fork();

	if(pid < 0)
	{
		perror("fork");
		return -1;
	}
	else if(pid == 0)	//子程序
	{
		//寫,關閉讀端
		close(fd[0]);

		write(fd[1], "world\n", 6);

		close(fd[1]);
	}
	else			//父程序
	{
		//讀,關閉寫端
		close(fd[1]);

		char buf[100] = {0};

		read(fd[0], buf, sizeof(buf));

		printf("buf = %s\n", buf);

		close(fd[0]);
	}

	return 0;
}
/*
運作結果:
buf = world

*/
           

4.3、有名管道:

1、定義:

是一種特殊的管道檔案,在本地磁盤可見

有名管道也叫命名管道,在本地磁盤可見的管道檔案,可以進行檔案IO操作。

2、特點:

1、可以在具有親緣關系的程序間使用,也可以在非親緣關系的程序中使用。

2、資料遵循先進先出

3、可以使用檔案IO中除了lseek之外的函數操作

3、api接口

1、建立管道檔案

int mkfifo(const char *filename, mode_t mode);

頭檔案:

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

參數:

filename: 要操作的管道檔案名(可包含路徑)

mode: 8進制的權限

傳回值:

成功: 傳回0

失敗: 傳回-1

在shell中使用mkfifo指令: mkfifo filename

eg: mkfifo f1

或者

if(mkfifo(“f1”,0666) == -1)

{

perror("mkfifo ");

return -1;

}

==4、注意: ==

1、有名管道檔案的資料互動在 核心中,本地磁盤檔案中沒有資料

2、有名管道的讀程序結束,寫程序寫入資料會終止程式運作

3、有名管道的寫程序結束,讀程序會一直運作,讀到0直接資料

信号處理原型

void(*signal(int signum,void(*handler)(int)))(int)

5、有名管道和無名管道的異同點

1、相同點

open打開管道檔案以後,在記憶體中開辟了一塊空間,管道的内容在記憶體中存放,有兩個指針—-頭指針(指向寫的位置)和尾指針(指向讀的位置)指向它。

讀寫資料都是在給記憶體的操作,并且都是半雙工通訊。

2、差別

有名在任意程序之間使用,無名在父子程序之間使用

//親緣程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
	int ret = mkfifo("f1", 0666);
	if(ret < 0)
	{
		perror("mkfifo");
		return -1;
	}

	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
		perror("fork");
		exit(-1);
	}
	else if(pid == 0) //子程序
	{
		int fd = open("f1", O_WRONLY);

		write(fd, "hello\n", 6);

		close(fd);
	}
	else{ //父程序
	
		int fd = open("f1", O_RDONLY);

		char buf[100] = {0};

		read(fd, buf, sizeof(buf));

		printf("buf = %s", buf);

		close(fd);
	}

	return 0;
}
           
/*非親緣程序*/
/*fifo_write.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
#if 0
	int ret = mkfifo("f2", 0666);
	if(ret < 0)
	{
		perror("mkfifo");
		return -1;
	}
#endif
	int fd = open("f2", O_WRONLY);
	if(fd < 0)
	{
		perror("open");
		return -1;
	}

	while(1)
	{
		printf("input>");
	
		char buf[100] = {0};

		fgets(buf, sizeof(buf), stdin);
	
	
		write(fd, buf, strlen(buf));

		if(strcmp(buf, "quit\n") == 0)
			break;
	}


	return 0;
}

/*fifo_read.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
#if 0
	int ret = mkfifo("f2", 0666);
	if(ret < 0)
	{
		perror("mkfifo");
		return -1;
	}
#endif
	int fd = open("f2", O_RDONLY);
	if(fd < 0)
	{
		perror("open");
		return -1;
	}

	printf("wait a write......\n");

	while(1)
	{	
		char buf[100] = {0};
		
		int n = read(fd, buf, sizeof(buf));
		if(n <= 0)
		{
			break;
		}

		printf("recv: %s\n", buf);

		if(strncmp(buf, "quit", 4) == 0)
			break;

		memset(buf, 0, sizeof(buf));
	}

	printf("over\n");

	return 0;
}

           

資料傳輸特點:

1、讀端不存在時,寫端寫入資料将會阻塞

2、讀端意外結束,寫端再寫資料将會管道破裂,該程序結束

3.信号

簡單概念:信号是在軟體層次上對中斷機制的一種模拟

#include <signal.h>

int kill(pid_t pid, int signo); //kill把信号發送給程序或程序組;

int raise(int signo); //raise把信号發送給(程序)自身.

傳回值:成功則傳回0,

出錯則傳回-1

特點:

raise(signo); 等價于 kill(getpid(), signo);

//結束一個程序

alarm();//設定鬧鐘

pause();//程式暫停

可以為目前程序定義鬧鐘,時間到了會發出SIGALRM信号。

每個程序隻能有一個alarm,當重新定義時,會重新計時。

如果之前定義了一個鬧鐘,則這次定義傳回的是上次鬧鐘剩餘的時間,否則傳回0.

pause函數的作用,是讓目前程序暫停運作,交出CPU給其他程序去執行;

目前程序進入pause狀态後,目前程序會表現為“卡住、阻塞住”;

要退出pause狀态,目前程序需要被信号喚醒。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	alarm(5);		//5秒之後會發出一個SIGALRM信号

	sleep(2);		//睡眠2秒

	int ret = alarm(5);	//傳回上一次鬧鐘剩餘時間3s

	printf("%d\n", ret);	//傳回3s

	pause();		//讓程式暫停

	printf("------------>\n");

	while(1);

	return 0;
}
/*
運作結果:
3
Alarm clock

*/
           

信号的三種處理方式:

1.忽略 2.預設 3.自定義信号處理函數

sighandler_t signal(int signum, sighandler_t handler);

參數:

SIGINT : CTRL + C

SIGQUIT : CTRL +

SIGTSTP : CTRL + Z

SIGKILL : 立即結束程序

SIGALRM : 當一個定時器結束時發出

SIGSTOP : 暫停一個程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>

//信号處理函數:隻要産生了SIGQUIT\SIGTSTP信号,都會執行處理函數
void handler(int signum)	//信号處理函數
{
	if(signum == SIGQUIT)	//ctrl + '\'
	{
		printf("笑臉\n");
	}
	else if(signum == SIGTSTP)//ctrl + 'z'
	{
		printf("哭臉\n");
	}
}

int main(int argc, const char *argv[])
{

	//signal(SIGINT, SIG_IGN);	//忽略ctrl + c
	
	//signal(SIGINT, SIG_DFL);	//預設ctrl + c可以停止運作

	signal(SIGQUIT, handler);	//自定義,注冊
	
	signal(SIGTSTP, handler);	//自定義,注冊   

	while(1)
	{
		printf("hello word\n");
		sleep(2);
	}

	return 0;
}
/*
運作結果:
hello word
^Z哭臉
hello word
^\笑臉
hello word
^\笑臉
hello word
^\笑臉
hello word

*/
           

共享記憶體:

共享記憶體是一種最為高效的程序通信方式,而不需要任何資料拷貝。

步驟:

1、ftok

2、shmget

3、shmat

4、程序間的通信

5、shmdt

6、shmctl

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/ipc.h>

#include <sys/ipc.h>
#include <sys/shm.h>

#include <sys/types.h>
#include <sys/shm.h>

#include <unistd.h>

#include <sys/types.h>
#include <sys/shm.h>

#include <sys/ipc.h>
#include <sys/shm.h>

int main(void)
{
	key_t key;

	//1.得到key值
	key = ftok("./", 1);
	if(key == -1)
	{
		perror("ftok");
		return -1;
	}
	printf("%#x\n", key);

	//2.建立共享記憶體
	int shmid = shmget(key, 1024, IPC_CREAT | 0666);
	if(shmid == -1)
	{
		perror("shmget");
		return -1;
	}
	printf("shmid = %d\n", shmid);
	
	system("ipcs -m");

	//3.映射
	char *p = shmat(shmid, NULL, 0);
	if(NULL == p)
	{
		perror("shmat");
		goto xxx;
	}

	//4.fork
	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
		perror("fork");
		goto xxx;
	}
	else if(pid == 0)//子程序,寫入資料
	{
		while(1)
		{
			char buf[32] = {0};

			fgets(buf, sizeof(buf), stdin);

			strcpy(p, buf);	//往共享記憶體寫入資料

			if(strncmp(p, "quit", 4) == 0)
				break;
		}
	}
	else		//父程序,讀取共享記憶體區資料
	{
		waitpid(pid, NULL, WNOHANG);	//非阻塞等待
		
		while(1)
		{
			printf("p = %s\n", p);

			sleep(3);

			if(strncmp(p, "quit", 4) == 0)
				break;
		}
	}

	//5.解除映射
	int ret = shmdt(p);
	if(ret == -1)
	{
		perror("shmdt");
		goto xxx;
	}	

	//6.删除共享記憶體
	ret = shmctl(shmid, IPC_RMID, NULL);
	{
		if(ret == -1)
		{
			perror("shmctl");
			goto xxx;
		}

	}

xxx:
	sleep(2);	//防止出錯
	char buf[32] = {0};
	sprintf(buf, "ipcrm -m %d", shmid);

	system(buf);

	system("ipcs -m");

	return 0;
}
/*
運作結果:
hello
p = hhello

p = hhello

p = hhello

p = hhello

p = hhello



*/
           

消息隊列

步驟:

1、ftok

2、msgget

3、程序間的通信

4、msgsnd

5、magrcv

6、msgctl

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/ipc.h>

#include <sys/types.h>
#include <sys/shm.h>

#include <unistd.h>

#include <sys/msg.h>
#include <sys/wait.h>

struct msgbuf{
	long mtype;	//消息類型
	char buf[100];	//消息正文
};

int main(void)
{
	key_t key;

	//1.得到key值
	key = ftok("./", 'a');
	if(key == -1)
	{
		perror("ftok");
		return -1;
	}
	printf("%#x\n", key);

	//2.建立消息隊列
	int msgid = msgget(key, IPC_CREAT | 0666);
	if(msgid == -1)
	{
		perror("msgget");
		goto xxx;
	}
	printf("msgid = %d\n", msgid);
	
	system("ipcs -q");

	//3.fork
	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
		perror("fork");
		goto xxx;
	}
	else if(pid == 0)//子程序,寫入資料
	{
		while(1)
		{
			char buf[32] = {0};

			fgets(buf, sizeof(buf), stdin);

			struct msgbuf msg;	//結構體變量

			msg.mtype = 100;	//消息類型
			strcpy(msg.buf, buf);	//消息正文

			//發送消息,将消息添加到消息隊列
			msgsnd(msgid, &msg, sizeof(msg)-sizeof(long), 0);

			if(strncmp(msg.buf, "quit", 4) == 0)
				break;
		}
	}
	else		//父程序,
	{
		waitpid(pid, NULL, WNOHANG);	//非阻塞等待
		
		while(1)
		{
			struct msgbuf msg;

			msgrcv(msgid, &msg, sizeof(msg)-sizeof(long), 100, 0);
			printf("msg.buf = %s\n", msg.buf);

			if(strncmp(msg.buf, "quit", 4) == 0)
				break;
		}
	}

	//6.删除消息隊列
	int ret = msgctl(msgid, IPC_RMID, NULL);

	if(ret == -1)
	{
		perror("msgctl");
		goto xxx;
	}

xxx:
	sleep(2);	//防止出錯
	char buf[32] = {0};
	sprintf(buf, "ipcrm -q %d", msgid);

	system(buf);

	system("ipcs -q");

	return 0;
}
/*
運作結果:
ok
msg.buf = ok

open
msg.buf = open

look
msg.buf = look

quit
msg.buf = quit




*/
           

信号燈集

步驟:

1、ftok

2、shmget

3、shmat

4、semget

5、semctl

6、程序間的通信

5、

7、shmctl

跳轉:IO進線程概念理論!

跳轉:IO進線程概念理論!

跳轉:上一篇,資料結構與算法!

跳轉:上一篇,資料結構與算法!

跳轉:下一篇,網絡程式設計!

跳轉:下一篇,網絡程式設計!
跳轉:開頭

繼續閱讀