一、标准IO
1.标准IO------(C库提供) C库函数:在系统调用接口之上封装的接口,一个C库函数可以封装多个系统调用函数。 作用:
- 增强了代码的可移植性,复用性
- 提高了效率。
标准IO增加了一个【缓冲机制】
2.文件IO------(linux系统提供) 系统调用:当我们的应用程序要使用一些底层的功能的时候,不应该自行访问底层,而应该向操作系统发出请求。
特点:
- 不带缓冲区
- 操作系统直接提供的函数接口
- 调用系统调用是很耗费资源的
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; /* Start of reserve area. / char _IO_buf_end; /* End of reserve area. */
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); /******************************************************************
- 功能: 读文件 (按字符读取)
- 参数: stream:文件
- 返回值: 成功返字符编码值 失败返回-1
*******************************************************************/
int fputc(int c, FILE *stream); /******************************************************************
- 功能: 写文件 (按字符写)
- 参数: stream:文件
- 返回值: 成功返字符编码值 失败返回-1
*******************************************************************/
#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);
}
printf("fopen success!\n");
fclose(fp);
return 0;
}
每一个终端都是一个文件: 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;
}
缓冲区: 行缓存、无缓存、全缓存
printf、stdin 、stdout是行缓存,缓冲区大小是 1024byte == 1Kbyte
//1.一行满了 或遇到’\n’输出条件 //2.fflush可以强制刷新 //3.文件关闭的时候 fclose(stdout) exit return
int fflush(FILE *stream); /*******************************
- 功能: 刷新缓存区
- 参数:@stream 流指针
- 返回值: 成功返回 0,
- 错误返回 EOF 更新 errno
*******************************/
#include <stdio.h>
int main(void)
{
printf("hello word");
printf("line size = %d\n", stdout->_IO_buf_end - stdout->_IO_buf_base);
int i = 0;
for(i=0; i<1024; i++)
{
fputc('a', stdout);
}
while(1);
return 0;
}
无缓存:stderr
#include <stdio.h>
int main(void)
{
fputc('a', stdout);
fputc('b', stdout);
fputc('a', stderr);
fputc('b',stderr);
while(1);
return 0;
}
全缓存:通过fopen函数打开的流指针,这个流指针fp的缓冲区大小是 4*1024 4Kbyte //1.缓存区满 //2.fclose(fp) //3.return //4.exit //5.fflush(fp)
#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++)
{
fputc('a', fp);
}
while(1);
return 0;
}
char *fgets(char *buf, int size, FILE *stream); /******************************************************************
- 功能: 读文件(按行读取)
- 参数: buf:地址
size :读取的字节数 stream: 流指针 - 返回值: 成功返回读取到的字符串的首地址 失败返回NULL
*******************************************************************/ 文件有多少行?
#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); /******************************************************************
- 功能: 写文件(按行写)
- 参数:
- 返回值: 成功返回0 失败返回-1
*******************************************************************/ 写入"hello world"
示例:从标椎输入stdin 字符串到文件当中
练习:复制文件
#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)
{
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); /******************************************************************
-
功能: 文件定位 -
参数: stream:文件, offset:偏移量 偏移的字节数 +100 -100 whence:基准值 SEEK_SET: 文件开头 SEEK_CUR: 文件当前 SEEK_END: 文件末尾 -
返回值: 失败返回-1 成功返回:相对起点的偏移量。 *******************************************************************/ 定位到文件末尾的前一个字节 fseek(fp, -1 , SEEK_END);
long ftell(FILE *stream); /********************************************* *功能: 获取文件指针偏移位置 *参数: @stream 文件流 *返回值: 成功: 返回 相对于文件开头的偏移值
- 失败: 返回 -1 更新 errno
***********************************************/ 获取文件大小
void rewind(FILE *stream); /********************************************* *功能: 移动文件指针,移动到文件开头
- 等价于
- (void) fseek(stream, 0L, SEEK_SET)
*参数: @stream 文件流 *返回值: void ***********************************************/
#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;
}
练习:查单词
伪代码{ 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;
}
【Printf函数升级】 int fprintf(FILE *stream, const char *format, …); /*********************************************** *功能: 将格式化的字符串,输出到指定的流指针中(stream) *参数:
- @stream 流指针
- @format 格式化字符串
- @… 不定参数
*返回值: - 成功: 格式化的字符串的长度
- 失败: 负数 更新errno
***********************************************/
int sprintf(char *str, const char *format, …); /********************************** *功能:格式化输出字符串到str指向的内存空间中 *参数:@str 字符串内存空间首地址 注意内存空间
- @format 格式化输出内容
- @… 不定参数
*返回值: 成功 格式化的的字符数 - 失败 负数
***********************************/
int sscanf(const char *str, const char *format, …); /********************************** *功能:以固定字符串为输入源进行输入 *参数:@str 字符串内存空间首地址 注意内存空间
- @format
- @… 不定参数
*返回值: 成功则返回参数数目 - 失败则返回-1
***********************************/
#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); /******************************************************************
- 功能: 读文件 (按块读取)
- 参数: stream:文件,ptr:保存内容地址,size:一块大小,nmemb:块数
- 返回值: 成功返回块数,失败或到达文件末尾返回值小于块数。
*******************************************************************/ 例如:从文件fp里读取100个字节,可用以下语句
fread(buffer,100,1,fp); fread(buffer,50,2,fp); fread(buffer,1,100,fp);
#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); /******************************************************************
- 功能: 写文件 (按块写)
- 参数: stream:文件,ptr:保存内容地址,size:一块大小,nmemb:块数
- 返回值: 成功返回块数,失败或到达文件末尾返回值小于块数。
*******************************************************************/
#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, …); /*********************************************** *功能: 将格式化的字符串,输出到指定的流指针中(stream) *参数:
- @stream 流指针【stdout / FILE *fp】
- @format 格式化字符串
- @… 不定参数
*返回值: - 成功: 格式化的字符串的长度
- 失败: 负数 更新errno
***********************************************/
#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, …); /********************************** *功能:将格式化的字符串,输出到内存空间中 *参数:@str 字符串内存空间首地址 注意内存空间
- @format 格式化输出内容
- @… 不定参数
*返回值: 成功 格式化的的字符数 - 失败 负数
***********************************/
#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, …); /********************************** *功能:利用它可以从字符串中取出整数、浮点数和字符串等等 *参数:@str 字符串内存空间首地址 注意内存空间
- @format
- @… 不定参数
*返回值: 成功则返回参数数目 - 失败则返回-1
***********************************/
#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); /********************************** *功能:改变某个流的输出方向 作用: 日志文件 *参数:@path 路径 *@mode 打开方式
- @stream 流指针
*返回值: 成功则返回 - 失败则返回NULL
***********************************/
#incude <time.h> time_t time(time_t *t); /********************************************* *功能: 获取从1970-1-1 00:00:00到现在的秒数 *参数: @t time_t 指针变量,自己定义一个time_t
- 类型的变量,然后将变量的地址作为参数传入time()
*返回值: 成功: 返回时间值,秒数 - 失败: 返回 -1 更新 errno
***********************************************/
#include <time.h> struct tm *localtime(const time_t *timep); /********************************************* *功能: 将从1970-1-1 00:00:00到现在的秒数,转换为日历时间 *参数: @tloc time_t 指针变量,自己定义一个time_t
- 类型的变量,然后将变量的地址作为参数传入time()
*返回值: 成功: 返回日历结构体 - 失败: 返回 NULL 更新 errno
***********************************************/
struct tm { int tm_sec; /* Seconds (0-60) / int tm_min; / Minutes (0-59) / int tm_hour; / Hours (0-23) / int tm_mday; / Day of the month (1-31) / int tm_mon; / Month (0-11) / int tm_year; / Year - 1900 / int tm_wday; / Day of the week (0-6, Sunday = 0) / int tm_yday; / Day in the year (0-365, 1 Jan = 0) / int tm_isdst; / Daylight saving time */ };
示例:获取系统时间
练习:获取系统时间,并将系统时间 打印输出到 文件中 [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);
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); /***************************************** *功能: 打开文件,获取文件描述符 *参数: *@pathname 文件名(指定完整路径) *@flags 打开文件的方式 *O_RDONLY 只读方式打开 *O_WRONLY 只写方式打开 *O_RDWR 读写方式打开 *O_APPEND 如果文件存在,则以追加方式打开 *O_CREAT 如果文件不存在,则创建 *O_TRUNC 如果文件存在,则清空 ( 主要讲解 或 运算 ) *@mode 当指定O_CREAT,则表示创建文件的文件权限(user group other)
- 如果未指定O_CREAT,则被忽略
*返回值: 成功 返回文件描述符 - 失败 返回 -1 更新 errno
******************************************/
#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); /***************************************** *功能: 关闭文件 *参数: @fd 文件描述符 * *返回值: 成功 返回 0
- 失败 返回 -1 更新 errno
******************************************/
标准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); /***************************************** *功能: 读文件 *参数: @fd 文件描述符
- @buf 从文件中读取的内容放到内存的那个位置
*@count 要读取多少字节
*返回值: 成功 返回 读取的字节个数
- 返回值为 0 表示读到文件结尾
- 失败 返回 -1 更新 errno
******************************************/
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); /***************************************** *功能: 写文件 *参数: @fd 文件描述符
- @buf 要把内存中那些内容写到文件中
- @count 要写入多少字节
*返回值: 成功 返回 写入的字节个数
- 失败 返回 -1 更新 errno
******************************************/
#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); /***************************************** *功能: 移动文件指针 *参数: @fd 文件描述符
- @offset 偏移值
*@whence *SEEK_SET: 文件开头位置 *SEEK_CUR: 当前文件指针位置 *SEEK_END: 文件结尾位置
*返回值: 成功 返回 设置后的文件指针相对于文件开始的偏移值
- 失败 返回 -1 更新 errno
******************************************/
【目录操作函数】
include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); /***************************************** *功能: 打开一个目录文件 *参数: @name 目录文件路径 *返回值: 成功 返回 目录流指针 *失败 返回 NULL 更新 errno ******************************************/
#include <dirent.h> struct dirent *readdir(DIR *dirp); /***************************************** *功能: 读目录流指针 *参数: @dirp 目录流指针 *返回值: 成功 返回 struct dirent 指针
- 失败 返回 NULL 表示目录中的文件读完
*****************************************/ struct dirent { ino_t d_ino; / inode number / off_t d_off; / not an offset; see NOTES / unsigned short d_reclen; / length of this record / unsigned char d_type; / type of file; not supported by all filesystem types / char d_name[256]; / filename */ };
#include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); /***************************************** *功能: 关闭目录流 *参数: @dirp 目录流指针 *返回值: 成功 返回 0 *失败 返回 -1 更新 errno ******************************************/
示例:查看目录下的文件
【文件信息函数】
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat *buf); /***************************************** *功能: 获取指定文件的信息 *参数: @path 指定文件路径
*返回值: 成功 返回 0
- 失败 返回 -1 更新 errno
*****************************************/ struct stat { dev_t st_dev; / ID of device containing file / ino_t st_ino; / inode number / mode_t st_mode; 文件的权限、文件的类型 / protection / nlink_t st_nlink; / number of hard links / uid_t st_uid; 所属用户ID / user ID of owner / gid_t st_gid; 所属组ID / group ID of owner / dev_t st_rdev; / device ID (if special file) / off_t st_size; 文件的大小 / total size, in bytes / blksize_t st_blksize; / blocksize for filesystem I/O / blkcnt_t st_blocks; / number of 512B blocks allocated / time_t st_atime; 最后一次访问时间 / time of last access / time_t st_mtime; 最后一次修改时间 / time of last modification / time_t st_ctime; 最后一次文件文件属性修改时间 / time of last status change */ };
将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;
}
【库的制作】
1、什么是库 /lib/i386-linux-gnu /usr/include
-
库是一种加密的二进制文件 -
需要被操作系统载入内存运行 -
相比于可执行程序,它不可以直接运行 -
window 和 linux 都有自己的库,但是不兼容 -
库有两种,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
-
使用 $ gcc main.c -L路径 -lxxx -L: 指定静态库所在的目录 -l: 指定静态库的名字 xxx部分 -
运行 $ ./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
-
使用 $ gcc main.c -L路径 -lxxx $ ldd a.out # 用于查看可执行程序依赖的动态库有哪些 -
运行 $ ./a.out # 会报错 动态库的搜索方式(3种,任意选一种):
-
将动态库拷贝到 /lib/ 或者 /usr/lib/ $ sudo cp libxxx.so /usr/lib/ -
export LD_LIBRARY_PATH=.或者so所在的路径 -
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) 3.1.8、进程相关的函数 1、创建子进程 #include <sys/types.h> #include <unistd.h> 函数原型:pid_t fork(void) /***************************************** *功能: 创建子进程。 *参数: 无 * *返回值: 成功 返回值为 0 :表示子进程区域
- 返回值为 >0 :表示父进程区域
- 失败 -1,并设置错误信息
******************************************/
从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;
}
#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)
{
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;
}
fork函数特性: (1)、子进程创建时,几乎拷贝了父进程全部内容,包括代码段、数据段、堆栈段、文件描述符、虚拟地址空间 (2)、同一个父进程创建的子进程都是属于同一个进程组 pkill -9 -g PGID (3)、进程是管理资源的最小单位
思考:会创建多少个进程,进程之间的关系是什么样的? int main(void) { fork(); fork(); }
答:会创建4个多少个进程,关系如下图。
#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;
}
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);
}
int main(int argc, const char *argv[])
{
printf("aaaaaa\n");
fun();
printf("bbbbbb\n");
return 0;
}
(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;
}
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(&status);
printf("%d %d\n", WEXITSTATUS(status), WIFEXITED(status));
printf("-------------->\n");
while(1);
}
else
{
sleep(5);
exit(0);
}
return 0;
}
== (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");
waitpid(-1, NULL, WNOHANG);
printf("-------------->\n");
while(1);
}
else
{
sleep(5);
exit(10);
}
return 0;
}
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这个系
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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);
}
}
}
}
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);
}
}
四、线程
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==
#include <stdio.h>
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;
}
回收线程资源 int pthread_join(pthread_t thread, void **retval); /*************************************** *功能: 阻塞等待指定线程退出, *接收退出状态并回收子线程资源 *参数:@thread 线程号 *@retval 返回值 *返回值: 成功: 0 *失败: -1 errno值 ****************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
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;
}
int pthread_detach(pthread_t thread); /*************************************** *设置线程为detach,这样线程退出, 资源自动释放,无需使用pthread_join ****************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
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_detach(tid);
while(1)
{
printf("main is running\n");
sleep(3);
}
return 0;
}
结束子线程 void pthread_exit(void *retval); /*************************************** *功能: *参数:@retval 线程退出时返回的值,可被其他线程通过pthread_join获取 *返回值: 无 ****************************************/
注意: pthread_exit == return
线程传参{ 值 地址 }
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
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;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
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;
}
int pthread_cancel(pthread_t thread); /*************************************** *功能: 在线程中发送关闭指定线程的请求 *参数:@thread 线程号 *返回值: 成功: 0 *失败: 负数,更新 errno ****************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
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;
}
优点:线程间很容易进行通信 通过全局变量实现数据共享和交换
缺点:多个线程同时访问共享对象 时需要引入同步和互斥机制
同步和互斥 :保护共享资源,避免竟态
同步:多个任务按理想的顺序/步调来进行 互斥:不能同时访问
都是为了避免竟态:多个任务同时访问共享资源
线程间的通信机制: 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;
void *fun(void *arg)
{
int i = 5;
while(i--)
{
printf("fun is running\n");
sleep(1);
}
sem_post(&sem);
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
sem_init(&sem, 0, 0);
printf("aaaaaaaaaaaaaa\n");
sem_wait(&sem);
printf("bbbbbbbbbbbbbb\n");
pthread_detach(tid);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <semaphore.h>
sem_t sem;
char buf[100];
void *fun(void *arg)
{
int i = 5;
while(1)
{
sem_wait(&sem);
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);
}
while(strncmp(buf, "quit", 4) != 0);
return 0;
}
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;
}
write(fd[1], "hello", 5);
char buf[100] = {0};
read(fd[0], buf, sizeof(buf));
printf("buf = %s\n", buf);
close(fd[0]);
close(fd[1]);
}
#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 < 0)
{
perror("pipe");
return -1;
}
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;
}
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;
}
#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;
}
#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);
sleep(2);
int ret = alarm(5);
printf("%d\n", ret);
pause();
printf("------------>\n");
while(1);
return 0;
}
信号的三种处理方式: 1.忽略 2.默认 3.自定义信号处理函数
sighandler_t signal(int signum, sighandler_t handler); 参数: /******************************** *signum: 捕获信号,设置信号的处理方式 *handler: SIG_IGN:忽略 *SIG_DFL:默认 *自定义的信号处理函数 *********************************/
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>
void handler(int signum)
{
if(signum == SIGQUIT)
{
printf("笑脸\n");
}
else if(signum == SIGTSTP)
{
printf("哭脸\n");
}
}
int main(int argc, const char *argv[])
{
signal(SIGQUIT, handler);
signal(SIGTSTP, handler);
while(1)
{
printf("hello word\n");
sleep(2);
}
return 0;
}
共享内存: 共享内存是一种最为高效的进程通信方式,而不需要任何数据拷贝。
步骤: 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;
key = ftok("./", 1);
if(key == -1)
{
perror("ftok");
return -1;
}
printf("%#x\n", key);
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
if(shmid == -1)
{
perror("shmget");
return -1;
}
printf("shmid = %d\n", shmid);
system("ipcs -m");
char *p = shmat(shmid, NULL, 0);
if(NULL == p)
{
perror("shmat");
goto xxx;
}
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;
}
}
int ret = shmdt(p);
if(ret == -1)
{
perror("shmdt");
goto xxx;
}
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;
}
消息队列 步骤: 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;
key = ftok("./", 'a');
if(key == -1)
{
perror("ftok");
return -1;
}
printf("%#x\n", key);
int msgid = msgget(key, IPC_CREAT | 0666);
if(msgid == -1)
{
perror("msgget");
goto xxx;
}
printf("msgid = %d\n", msgid);
system("ipcs -q");
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;
}
}
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;
}
信号灯集 步骤: 1、ftok 2、shmget 3、shmat 4、semget 5、semctl 6、进程间的通信 5、 7、shmctl
跳转:IO进线程概念理论!
跳转:IO进线程概念理论!
跳转:下一篇,网络编程总结!
跳转:下一篇,网络编程总结!
跳转:开头
|