unix编程
2018-05-12 17:31:04 7 举报
AI智能生成
Unix编程
作者其他创作
大纲/内容
https://blog.csdn.net/wenqian1991/article/details/40110703
文件编程
文件系统结构
系统文件编程
struct stat{
dev_t st_dev;
//文件常驻设备ID
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev; //特别文件(字符文件或块文件)设备号
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
}
int GetFileMode(mode_t st_mode, char *resp)
{
if(resp == NULL) return 0;
memset(resp, ‘-‘, 9);
if(st_mode & S_IRUSR) resp[0] = ‘r’;
if(st_mode & S_IWUSR) resp[1] = ‘w’;
if(st_mode & S_IXUSR) resp[2] = ‘x’;
if(st_mode & S_IRGRP) resp[3] = ‘r’;
if(st_mode & S_IWGRP) resp[4] = ‘w’;
if(st_mode & S_IXGRP) resp[5] = ‘x’;
if(st_mode & S_IROTH) resp[6] = ‘r’;
if(st_mode & S_IWOTH) resp[7] = ‘w’;
if(st_mode & S_IXOTH) resp[8] = ‘r’;
return 9;
}
int GetFileType(mode_t st_mode, char *resp){
if(resp == NULL)
return 0;
if(S_ISDIR(st_mode)) resp[0] = 'd'; // 使用宏定义判断
else if(S_ISCHR(st_mode)) resp[0] = 'c';
else if(S_ISBLK(st_mode)) resp[0] = 'b';
else if(S_ISREG(st_mode)) resp[0] = '-';
else if(S_ISFIFO(st_mode)) resp[0] = 'p';
else if(S_ISLNK(st_mode)) resp[0] = 'l';
else resp[0] = ' ';
return 1;
}
int GetFileOtherAttr(struct stat info, char *resp){
struct tm *mtime;
if(resp == NULL)
return 0;
mtime = localtime(&info.st_mtime);
// 按 ls 命令显示顺序处理其他属性
return(sprintf(resp, " %3d %6d %6d %11d %04d%02d%02d", info.st_nlink, info.st_uid, /
info.st_gid, info.st_size,mtime->tm_year+1900, mtime->tm_mon+1, mtime->tm_mday));
}
#include
#include
#include
#include
int GetFileType(mode_t st_mode, char *resp);
int GetFileMode(mode_t st_mode, char *resp);
int GetFileOtherAttr(struct stat info, char *resp);
int main(int argc, char **argv)
{
struct stat info;
char buf[100], *p = buf;
if(argc != 2){
printf("Usage: lsl filename/n");
return;
}
memset(buf, 0, sizeof(buf));
if(lstat(argv[1], &info) == 0){
p += GetFileType(info.st_mode, p);
p += GetFileMode(info.st_mode, p);
p += GetFileOtherAttr(info, p);
printf("%s %s/n", buf, argv[1]);
}
else
printf("Open file failed!/n");
return 0;
}
标准文件编程库
文件的创建、打开、关闭与删除
1、创建、打开、关闭和删除文件的函数族
#include
FILE *fopen(const char *filename, const char *type);
FILE *freopen(const char *filename, const char *type, FILE *stream);
int fclose(FILE *stream);
int remove(const char *filename);
int rename(const char *oldname, const char *newname);
filename = 打开文件名称(带路径)
type = 打开文件方式,由权限和类型两部分组成,前者可以是r、w、a(追加)、r+、w+(存在则清空)、a+,后者默认为文本文件,使用b表示二进制文件
stream =已打开的文件指针
UNIX进程默认打开三个文件:标准输出、标准输入和标准错误输出,它们的FILE标识符分别是stdout、stdin和stderr。函数freopen常用于以上三个文件流重定向。
#include
#include
void main(){
FILE *fp;
char szBuf[100];
if((fp = freopen(“/tmp/1”, “w”, stderr)) == NULL) {
printf(“stderr -- tmp/1 failed.\n”);
return;
}
fputs(“I like UNIX.\n”, stderr);
fclose(fp);
if((fp = freopen(“/tmp/1”, “r”, stdin)) == NULL)
{
printf(“stdin -- tmp/1 failed.\n”);
return;
}
memset(szBuf, 0, sizeof(szBuf));
fgets(szBuf, sizeof(szBuf), stdin);
printf(“szBuf=[%s]”, szBuf);
fclose(fp);
}
文件的无格式读写
字符输入函数族
#include
int getc(File *stream);
int getchar(void);
int fgetc(FILE *stream);
函数fgetc功能类似于getc,但执行速度远低于getc,因此getc常被定义在宏中使用。
注:字符输入函数族虽然每次读取一个字符,但是其返回值为整型,否则会读取到错误的信息。
设计一个使用字符读写函数解析报文的例子,程序读取文件“/etc/passwd”中的每一个字符,并将“用户名称”域单独提取出来,存入文件“copyname.txt”中。
passwd文件格式 -- 用户名称:密码:用户ID:…。
#include
void main()
{
FILE *fpr, *fpw;
int c = 0, f = 0;
if((fpr = fopen(“/etc/passwd”, “r”)) == NULL)
{
printf(“open file /etc/passwd failed.\n”);
return;
}
if((fpw = fopen(“./copyname.txt”, “w”)) == NULL) {
printf(“open file ./copyname.txt failed.\n”);
fclose(fpr);
return;
}
while ((c = getc(fpr)) != EOF) {
if(f == 0) {
if(c != ‘:’) putchar(putc(c, fpw));
else f = 1;
}
else if(c == ‘\n’)
{
f = 0;
putchar(putc(c, fpw));
}
}
fclose(fpr);
fclose(fpw);
}
字符输出函数族
#include
int putc(int c, FILE *stream);
int putchar(int c);
int fputc(int c, FILE *stream);
行输入函数族
#include
char *gets(char *s);//从标准输入设备得到输入放到 char *s里去
char *fgets(char *s,int n,FILE *stream);
行输出函数族
#include
int puts(const char *s);
int fputs(const cahr *s, FILE *stream);
设计一个使用行读写函数解析报文的例子,程序读取文件“/etc/passwd”中的每一个字符,并将“用户名称”和“用户ID”域单独提取出来,存入文件“copynameid.txt”中。
注:passwd文件格式 -- 用户名称:密码:用户ID:…。
#include
void main()
{
FILE *fpr, *fpw;
char buf[1024], *p1, *p2;
if((fpr = fopen(“/etc/passwd”, “r”)) == NULL)
{
printf(“open file /etc/passwd failed.\n”);
return;
}
if((fpw = fopen(“./copynameid.txt”, “w”)) == NULL){
printf(“open file ./copyname.txt failed.\n”);
fclose(fpr);
return;
}
memset(buf, 0, sizeof(buf));
while (fgets(buf, sizeof(buf), fpr) != NULL) {
if((p1 = strstr(buf, “:”)) == NULL) break;
if((p2 = strstr(p1 + 1, “:”)) == NULL) break;
p1 ++; p2 ++;
while(*p2 != ‘:’) {
*p1 = *p2; p1 ++; p2 ++;
}
*p1 = 0;
puts(buf);
fputs(buf, fpw); fputs(“\n”, fpw);
memset(buf, 0, sizeof(buf));
}
fclose(fpr); fclose(fpw);
}
块读写
double f[5];
fread(f, sizeof(double), 5, INSTREAM);
fwrite(f, sizeof(double), 5, OUTSTREAM);
文件的格式化读写
文件的格式化输入函数族
#include
int scanf(const char *format, /* [pointer,] */ …);
int fscanf(FILE *stream, const char *format, /* [pointer,] */ …);
int sscanf(const char *s, const char *format, /* [pointer,] */ …);
文件的格式化输出函数族
#include
int printf(const char *format, /* [arg,] */…);
int fprintf(FILE *stream, const char *format, /* [arg,] */…);
int sprintf(char *s, const char *format, /* [arg,] */…);
函数的变长参数
#include
int mysum(int i, …)
{
int r = 0, j = 0;
va_list pvar;
va_start(pvar, i);
for(j = 0; j < i; j++)
r += va_arg(pvar, int);
va_end(pvar);
return r;
}
void main()
{
printf(“sum(1, 4)=%d\n”, mysum(1, 4));
printf(“sum(2, 4, 8)=%d\n”, mysum(2, 4, 8));
}
文件读写位置的定位
#include
int fseek(FILE *stream, long int offset, int whence);
void rewind(FILE *stream);
long int ftell(FILE *stream);
fseek函数改变文件流stream中的访问位置,参数whence表示文件定位方式,参数offset表明了定位的位移量。
文件定位方式:
SEEK_SET:从文件头开始定位,文件定位于offset处;
SEEK_CUR:从当前位置开始定位,定位于当前位置+offset处
SEEK_END:从文件尾开始定位,定位于文件末+offset处
获取文件长度
long len;
fseek(stream, 0, SEEK_END);
len = ftell(stream);
设计一个文件定位实例,函数seekwrite将整数值narray写入文件“rec*sizeof(int)”位置处;函数seekread从文件的第“rec*sizeof(int)”位置中读取整数值存入narray。
#include
void seekwrite(FILE *fp, int narray, int rec)
{
fseek(fp, sizeof(int) * rec, SEEK_SET);
fwrite(&narray, sizeof(int), 1, fp);
}
void seekread(FILE *fp, int *narray, int rec)
{
fseek(fp, sizeof(int) * rec, SEEK_SET);
fread(narray, sizeof(int), 1, fp);
}
void main()
{
FILE *fp;
int narray[2] = {100, 200}, narrayr[2];
if((fp = fopen(“seek.dat”, “w+b”)) == NULL)
{
printf(“open file seek.dat failed.\n”);
return;
}
seekwrite(fp, narray[0], 1);
seekwrite(fp, narray[1], 10);
seekread(fp, narrayr, 1);
seekread(fp, narrayr+1, 10);
printf(“now array[0]=%d, array[1]=%d\n”, narrayr[0], narrayr[1]);
fclose(fp);
}
文件缓冲
缓冲块大小为0,所有I/O立刻执行,字符被逐个地写入文件或从文件读出。为了便于错误的及时提示,stderr采用无缓冲模式。
#inlcude
void setbuf(FILE *stream, char * buf);
int setvbuf(FILE *stream, char * buf, int type, size_t size);
int fflush(FILE *stream);
缓冲区内存块的定义一般为:char buf[BUFSIZE];BUFSIZ是一个系统常量,代表缓冲区大小,常为256的整数倍。
缓冲模式由type确定:
_IOFBF:全缓冲;
_IOLBF:行缓冲;
_IONBF:无缓冲。
并发程序设计
进程的创建
方法1
(1) fork-exec
调用fork创建的子进程, 将共享父进程的代码空间, 复制父进程数据空间, 如堆栈等. 调用exec族函数将使用新程序的代码覆盖进程中原来的程序代码, 并使进程使用函数提供的命令行参数和环境变量去执行新的程序.
exec函数族有六个函数如下:
#include
int execl(const char *path, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const envp[]);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[]);
int execvp(const char *file, const char *argv[]);
extern char **environ;
调用fork创建的子进程, 将共享父进程的代码空间, 复制父进程数据空间, 如堆栈等. 调用exec族函数将使用新程序的代码覆盖进程中原来的程序代码, 并使进程使用函数提供的命令行参数和环境变量去执行新的程序.
exec函数族有六个函数如下:
#include
int execl(const char *path, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const envp[]);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[]);
int execvp(const char *file, const char *argv[]);
extern char **environ;
[bill@billstone Unix_study]$ cat exec1.c
#include
#include
#include
int main()
{
pid_t pid;
if((pid = fork()) == 0){
fprintf(stderr, "---- begin ----\n");
// sleep(3); // 睡眠3秒会导致子进程成为僵死进程
execl("/bin/uname", "uname", "-a", 0);
fprintf(stderr, "---- end ----\n");
}
else if(pid > 0)
fprintf(stderr, "fork child pid = [%d]\n", pid);
else
fprintf(stderr, "Fork failed.\n");
return 0;
}
[bill@billstone Unix_study]$ make exec1
cc exec1.c -o exec1
[bill@billstone Unix_study]$ ./exec1
---- begin ----
Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
fork child pid = [13276]
[bill@billstone Unix_study]$ ./exec1
---- begin ----
fork child pid = [13278]
[bill@billstone Unix_study]$ Linux billstone 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686 athlon i386 GNU/Linux
方法2
(2) vfork-exec
vfork比起fork函数更快, 二者的区别如下:
a) vfork创建的子进程并不复制父进程的数据, 在随后的exec调用中系统会复制新程序的数据到内存, 继而避免了一次数据复制过程
b) 父进程以vfork方式创建子进程后将被阻塞, 直到子进程退出或执行exec调用后才能继续运行.
当子进程只用来执行新程序时, vfork-exec模型比fork-exec模型具有更高的效率, 这种方法也是Shell创建新进程的方式.
僵死进程
僵死进程是已经终止, 但没有从进程表中清除的进程, 下面是一个僵死进程的实例
解决方法
其中, 'defunct'代表僵死进程. 对于僵死进程, 不能奢望通过kill命令杀死之, 因为它已经'死'了, 不再接收任何系统信号.
当子进程终止时, 它释放资源, 并且发送SIGCHLD信号通知父进程. 父进程接收SIGCHLD信号,调用wait返回子进程的状态, 并且释放系统进程表资源. 故如果子进程先于父进程终止, 而父进程没有调用wait接收子进程信息,则子进程将转化为僵死进程, 直到其父进程结束.
一旦知道了僵死进程的成因, 我们可以采用如下方法预防僵死进程:
(1) wait法
父进程主动调用wait接收子进程的死亡报告, 释放子进程占用的系统进程表资源.
(2) 托管法
如果父进程先于子进程而死亡, 则它的所有子进程转由进程init领养, 即它所有子进程的父进程ID号变为1. 当子进程结束时init为其释放进程表资源.
(3) 忽略SIGC(H)LD信号
当父进程忽略SIGC(H)LD信号后, 即使不执行wait, 子进程结束时也不会产生僵死进程.
(4) 捕获SIGC(H)LD信号
当父进程捕获SIGC(H)LD信号, 并在捕获函数代码中等待(wait)子进程
守护
实例
#include
#include
#include
int main()
{
pid_t pid;
if((pid = fork()) == 0){
printf("child[%d]\n", getpid());
exit(0);
}
// wait();
printf("parent[%d]\n", getpid());
sleep(10);
return 0;
}
[bill@billstone Unix_study]$
后台运行程序szobm1, 并在子进程结束后, 父进程没有结束前, 运行命令查询进程情况:
[bill@billstone Unix_study]$ make szomb1
cc szomb1.c -o szomb1
[bill@billstone Unix_study]$ ./szomb1 &
[2] 13707
child[13708]
[bill@billstone Unix_study]$ parent[13707]
ps -ef | grep 13707
bill 13707 1441 0 04:17 pts/0 00:00:00 ./szomb1
bill 13708 13707 0 04:17 pts/0 00:00:00 [szomb1 ] // 僵死进程
bill 13710 1441 0 04:17 pts/0 00:00:00 grep 13707
时钟与信号
时间
下面给出一个打印本地时间的例子
[bill@billstone Unix_study]$ cat time1.c
#include
#include
int main()
{
struct tm when;
time_t now;
time(&now);
when = *localtime(&now);
printf("now=[%d] [%04d %02d %02d %02d:%02d:%02d]\n", now, \
when.tm_year+1900, when.tm_mon+1, when.tm_mday, \
when.tm_hour, when.tm_min, when.tm_sec);
return 0;
}
[bill@billstone Unix_study]$ make time1
cc time1.c -o time1
[bill@billstone Unix_study]$ ./time1
now=[1239927129] [2009 04 17 08:12:09]
信号
信号的概念
信号是传送给进程的事件通知, 它可以完成进程间异步事件的通信.
导致信号产生的原因很多, 但总体说来有三种可能:
(1) 程序错误. 当硬件出现异常, 除数为0或者软件非法访问等情况时发生.
(2) 外部事件. 当定时器到达, 用户按健中断或者进程调用abort等信号发送函数时方生.
(3) 显式请求. 当进程调用kill, raise等信号发送函数或者用户执行shell命令kill传递信号时发生.
同样的, 当进程收到信号时有三种处理方式:
(1) 系统默认. 系统针对不同的信号有不同的默认处理方式.
(2) 忽略信号. 信号收到后, 立即丢弃. 注意信号SIGSTOP和SIGKILL不能忽略.
(3) 捕获信号. 进程接收信号, 并调用自定义的代码响应之
#include
#include
int main()
{
signal(SIGINT, SIG_IGN);
sleep(10); // 睡眠10秒
return 0;
}
[bill@billstone Unix_study]$ make sig1
cc sig1.c -o sig1
[bill@billstone Unix_study]$ ./sig1
[bill@billstone Unix_study]$
在程序运行的10秒内,即使你键入Ctrl+C中断命令, 进程也不退出.
再看一个捕获自定义信号的例子.
[bill@billstone Unix_study]$ cat sig2.c
#include
#include
int usr1 = 0, usr2 = 0;
void func(int);
int main()
{
signal(SIGUSR1, func);
signal(SIGUSR2, func);
for(;;)
sleep(1); // 死循环, 方便运行观察
return 0;
}
void func(int sig){
if(sig == SIGUSR1)
usr1++;
if(sig == SIGUSR2)
usr2++;
fprintf(stderr, "SIGUSR1[%d], SIGUSR2[%d]\n", usr1, usr2);
signal(SIGUSR1, func);
signal(SIGUSR2, func);
}
在后台运行, 结果如下:
[bill@billstone Unix_study]$ make sig2
cc sig2.c -o sig2
[bill@billstone Unix_study]$ ./sig2& // 后台运行
[2] 13822
[bill@billstone Unix_study]$ kill -USR1 13822 // 发送信号SIGUSR1
SIGUSR1[1], SIGUSR2[0]
[bill@billstone Unix_study]$ kill -USR2 13822 // 发送信号SIGUSR2
SIGUSR1[1], SIGUSR2[1]
[bill@billstone Unix_study]$ kill -USR2 13822 // 发送信号SIGUSR2
SIGUSR1[1], SIGUSR2[2]
[bill@billstone Unix_study]$ kill -9 13822 // 发送信号SIGSTOP, 杀死进程
[bill@billstone Unix_study]$
[2]+ 已杀死 ./sig2
[bill@billstone Unix_study]$
定时器
在UNIX中使用普通定时器需要三个步骤:
(0) 编写响应定时信号函数
(1) 调用signal函数设置捕获定时信号
(2) 调用函数alarm定时.
[bill@billstone Unix_study]$ cat time2.c
#include
#include
#include
int n = 0;
void timefunc(int sig){
fprintf(stderr, "Alarm %d\n", n++);
signal(SIGALRM, timefunc);
alarm(1);
}
int main()
{
int status;
signal(SIGALRM, timefunc);
alarm(1);
while(1);
return 0;
}
[bill@billstone Unix_study]$ make time2
cc time2.c -o time2
[bill@billstone Unix_study]$ ./time2
Alarm 0
Alarm 1
Alarm 2
// 按Ctrl+C结束
精确到毫秒
函数alarm设置的定时器只能精确到秒, 而下面函数理论上可以精确到毫秒:
#include
#include
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval value, struct itimerval *ovalue);
函数setitimer可以提供三种定时器, 它们相互独立, 任意一个定时完成都将发送定时信号到进程, 并且重新计时. 参数which确定了定时器的类型:
(1) ITIMER_REAL. 定时真实时间, 与alarm类型相同. 对应信号为SIGALRM.
(2) ITIMER_VIRT. 定时进程在用户态下的实际执行时间. 对应信号为SIGVTALRM.
(3) ITIMER_PROF. 定时进程在用户态和核心态下的实际执行时间. 对应信号为SIGPROF.
在一个UNIX进程中, 不能同时使用alarm和ITIMER_REAL类定时器.
#include
#include
#include
#include
#include
int n = 0;
void timefunc(int sig){
fprintf(stderr, "ITIMER_PROF[%d]\n", n++);
signal(SIGPROF, timefunc);
}
int main()
{
struct itimerval value;
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 500000;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 500000;
signal(SIGPROF, timefunc);
setitimer(ITIMER_PROF, &value, NULL);
while(1);
return 0;
}
[bill@billstone Unix_study]$ make time3
cc time3.c -o time3
[bill@billstone Unix_study]$ ./time3
ITIMER_PROF[0]
ITIMER_PROF[1]
ITIMER_PROF[2]
全局跳转
#include
int setjmp(jmp_buf env);
void longjump(jmp_buf env, int val);
setjmp的返回值:直接调用该函数,则返回0;若由longjmp的调用,导致setjmp被调用,则返回val(longjmp的第二个参数)。
使用longjmp的第二个参数原因:一个setjmp可以有多个longjmp;通过setjmp的返回值(longjmp的第二个参数val)来判断造成返回的longjmp是在哪里。
#include
int j = 0;
jmp_buf env;
int main()
{
int i, k = 0;
i = setjmp(env);
printf("setjmp = [%d], j = [%d], k = [%d]\n", i, j++, k++);
if(j > 2)
exit(0);
sleep(1);
longjmp(env, 1);
return 0;
}
[bill@billstone Unix_study]$ make jmp1
cc jmp1.c -o jmp1
[bill@billstone Unix_study]$ ./jmp1
setjmp = [0], j = [0], k = [0]
setjmp = [1], j = [1], k = [1]
setjmp = [1], j = [2], k = [2]
单线程I/O超时处理
UNIX下的I/O超时处理是一个很常见的问题, 它的通常做法是接收输入(或发送输出)后立刻返回, 如果无输入(或输出)则n秒后定时返回.
一般情况下, 处理UNIX中I/O超时的方式有终端方式, 信号跳转方式和多路复用方式等三种. 本节设计一个定时I/O的例子, 它从文件描述符0中读取一个字符, 当有输入时继续, 或者3秒钟后超时退出,并打印超时信息.
(2) 信号与跳转I/O超时方式
#include
#include
#include
#include
int timeout = 0;
jmp_buf env;
void timefunc(int sig){
timeout = 1;
longjmp(env, 1);
}
int main()
{
char c;
signal(SIGALRM, timefunc);
setjmp(env);
if(timeout == 0){
alarm(3);
read(0, &c, 1);
alarm(0);
printf("%d\n", c);
}
else
printf("timeout\n");
return 0;
}
[bill@billstone Unix_study]$ make timeout2
cc timeout2.c -o timeout2
[bill@billstone Unix_study]$ ./timeout2
v // 需要按Enter健激活输入
118
[bill@billstone Unix_study]$ ./timeout2
timeout
进程通信
管道
管道是进程之间的一种单向交流方法, 要实现进程间的双向交流, 就必须通过两个管道来完成. 双向管道流的创立过程如下:
int fildes[2];
assert(pipe(fildes) == 0);
(1) 创建管道, 返回两个无名管道文件描述符fildes1和fildes2:
(2) 创建子进程, 子进程中继承管道fildes1和fildes2.
(3) 父进程关闭只读文件描述符fildes1[0], 只写描述符fildes2[1]
(4) 子进程关闭只写文件描述符fildes1[1], 只读描述符fildes2[0]
创建的结果如下:
父进程 --写--> fildes1[1] --管道--> fildes1[0] --读--> 子进程
父进程 <--读-- fildes2[0] <--管道-- fildes2[1] <--写-- 子进程
这里实现一个父子进程间双向通信的实例: 父进程先向子进程发送两次数据, 再接收子进程传送
刚来的两次数据. 为了正确拆分时间留从父进程流向子进程的管道采用'固定长度'方法传送数据; 从子进程流向父进程的管道采用'显式长度'方法传回数据.
#include
#include
#include
#include
int main()
{
int fildes[2];
pid_t pid;
int i,j;
char buf[256];
assert(pipe(fildes) == 0); // 创建管道
assert((pid = fork()) >= 0); // 创建子进程
if(pid == 0){ // 子进程
close(fildes[1]); // 子进程关闭管道输出
memset(buf, 0, sizeof(buf));
j = read(fildes[0], buf, sizeof(buf));
fprintf(stderr, "[child] buf=[%s] len[%d]\n", buf, j);
return;
}
close(fildes[0]); // 父进程关闭管道输入
write(fildes[1], "hello!", strlen("hello!"));
write(fildes[1], "world!", strlen("world!"));
return 0;
}
[bill@billstone Unix_study]$ make pipe1
cc pipe1.c -o pipe1
[bill@billstone Unix_study]$ ./pipe1
[child] buf=[hello!world!] len[12]
有名管道
要使用有名管道, 需要下面几个步骤:
(1) 创建管道文件assert((mkfifo("myfifo", S_IFIFO|0666) > 0) || (errno == EEXIST));
(2) 在某个进程中以只写方式打开管道文件, 并写管道
assert((fp = fopen("myfifo", "w")) != NULL);
fputs(buf, fp);
(3) 在某个进程中以只读方式打开管道文件, 并读管道
assert((fp = fopen("myfifo", "r")) != NULL);
fgets(buf, strlen(buf), fp);
(4) 关闭管道文件.fclose(fp);
#include
#include
#include
#include
#include
extern int errno;
int main()
{
FILE *fp;
char buf[255];
assert((mkfifo("myfifo", S_IFIFO|0666) > 0) || (errno == EEXIST));
while(1){
assert((fp = fopen("myfifo", "w")) != NULL);
printf("please input: ");
fgets(buf, sizeof(buf), stdin);
fputs(buf, fp);
fclose(fp);
if(strncmp(buf, "quit", 4) == 0 || strncmp(buf, "exit", 4) == 0)
break;
}
return 0;
}
[bill@billstone Unix_study]$ make fifo1
cc fifo1.c -o fifo1
[bill@billstone Unix_study]$
然后是读进程: 打开管道的读端口, 从管道中读取信息(以行为单位), 并将此信息打印到屏幕上. 当读取到'exit'或者'quit'时程序退出.
[bill@billstone Unix_study]$ cat fifo2.c
#include
#include
#include
#include
int main()
{
FILE *fp;
char buf[255];
while(1){
assert((fp = fopen("myfifo", "r")) != NULL);
fgets(buf, strlen(buf), fp);
printf("gets: [%s]", buf);
fclose(fp);
if(strncmp(buf, "quit", 4) == 0 || strncmp(buf, "exit", 4) == 0)
break;
}
return 0;
}
[bill@billstone Unix_study]$ make fifo2
cc fifo2.c -o fifo2
[bill@billstone Unix_study]$
在一个终端上执行fifo1, 而在另一个终端上执行fifo2.
我们先输入'hello', 'world', 然后再输入'exit'退出:
[bill@billstone Unix_study]$ ./fifo1
please input: hello
please input: world
please input: exit
[bill@billstone Unix_study]$
我们可以看到读出结果如下:
[bill@billstone Unix_study]$ ./fifo2
gets: [hello
]gets: [world]gets: [exit][bill@billstone Unix_study]$
消息队列
a) 根据自己的需要定义消息结构
struct msgbuf {
long mtype; /* type of message */
char ctext[100]; /* message data */
};
b) 打开或创建消息队列
msgid = msgget(0x1234, 0666 | IPC_CREAT);
if(msgid < 0) 打开(或创建)队列失败
c) 组装消息
printf("Please input: ");
memset(&buf, 0, sizeof(buf));
fgets(buf.ctext, sizeof(buf.ctext), stdin);
buf.mtype = getpid();
d) 发送消息
while((msgsnd(msgid, (void *)&buf, strlen(buf), IPC_NOWAIT)) < 0){
if(errno == EINTR)
continue;
exit(2);
}
d)接收消息
int msgrcv( int msgid , struct msgbuf* msgp , int msgsz , long msgtyp, int msgflg);
memset(&buf, 0, sizeof(buf));
while((ret = msgrcv(msgid, (void *)&buf, sizeof(buf), 0, 0)) < 0){
if(errno == EINTR)
continue;
fprintf(stderr, "Error no: %d", errno);
exit(2);
}
fprintf(stderr, "Msg: Type=%d, Len=%d, Text:%s", buf.mtype, ret, buf.ctext);
例子
#include
#include
#include
#include
#include
#include
extern int errno;
struct mymsgbuf1{
long mtype;
char ctext[100];
};
struct mymsgbuf{
long mtype;
pid_t pid;
char ctext[100];
};
void main(){
struct mymsgbuf buf;
struct mymsgbuf1 buf1;
int msgid,ret,i,len;
if((msgid = msgget(0x1234, 0666|IPC_CREAT)) < 0) {
fprintf(stderr, "open msg %x failed.\n", 0x1234);
return;
}
while(1)
{
memset(&buf, 0, sizeof(buf));
while((ret = msgrcv(msgid, &buf, sizeof(buf.ctext)+ sizeof(buf.pid), 1, 0)) < 0)
{
if(errno == EINTR) continue;
return;
}
fprintf(stderr, "Receive message form %d, content: %s\n", buf.pid, buf.ctext);
memset(&buf1, 0, sizeof(buf1));
//strcpy(buf1.ctext,buf.ctext);
len=strlen(buf.ctext)
for(i=0;i
#include
#include
#include
#include
#include
extern int errno;
struct mymsgbuf1{
long mtype;
char ctext[100];
};
struct mymsgbuf{
long mtype;
pid_t pid;
char ctext[100];
};
void main(){
struct mymsgbuf buf;
struct mymsgbuf1 buf1;
int msgid, ret;
pid_t pid=getpid();
if((msgid = msgget(0x1234, 0666|IPC_CREAT)) < 0) {
fprintf(stderr, "open msg %x failed.\n", 0x1234);
return;
}
while(1)
{
memset(&buf, 0, sizeof(buf));
fgets(buf.ctext, sizeof(buf.ctext), stdin);
if(!strcmp(buf.ctext,"exit"))
break;
buf.mtype = 1; buf.pid=pid;
while((msgsnd(msgid, &buf, strlen(buf.ctext)+sizeof(buf.pid),0)) < 0)
{
if(errno == EINTR) continue;
return;
}
memset(&buf1, 0, sizeof(buf1));
while((ret = msgrcv(msgid, &buf1, sizeof(buf1.ctext), pid, 0)) < 0)
{
if(errno == EINTR) continue;
return;
}
fprintf(stderr, "Receive message form server content: %s\n", buf1.ctext);
}
}
信号量(一个整数就是一个信号)
#include
int semget(key_t key, int nsems, int semflg);
VerifyErr((semid = semget(2000, 2, 0666)) < 0, "Open Sem 2000");
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "P sem 2000:0");
sb.sem_num = 1;
sb.sem_op = 1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "V sem 2000:1");
生产者进程sema.c如下:
[bill@billstone Unix_study]$ cat sema.c
#include
#include
#include
#include
#include
#define VerifyErr(a,b) \
if (a) { fprintf(stderr, "%s failed.\n", (b)); exit(1); } \
else fprintf(stderr, "%s success.\n", (b));
int main(void)
{
int semid;
struct sembuf sb;
VerifyErr((semid = semget(2000, 2, 0666)) < 0, "Open Sem 2000");
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "P sem 2000:0");
fprintf(stderr, "[%d] producing ... ... \n", getpid());
sleep(1);
fprintf(stderr, "[%d] produced\n", getpid());
sb.sem_num = 1;
sb.sem_op = 1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "V sem 2000:1");
return 0;
}
[bill@billstone Unix_study]$
消费者进程semb.c如下:
[bill@billstone Unix_study]$ cat semb.c
#include
#include
#include
#include
#include
#define VerifyErr(a,b) \
if (a) { fprintf(stderr, "%s failed.\n", (b)); exit(1); } \
else fprintf(stderr, "%s success.\n", (b));
int main(void)
{
int semid;
struct sembuf sb;
VerifyErr((semid = semget(2000, 2, 0666)) < 0, "Open Sem 2000");
sb.sem_num = 1;
sb.sem_op = -1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "P sem 2000:1");
fprintf(stderr, "[%d] consuming ... ... \n", getpid());
sleep(1);
fprintf(stderr, "[%d] consumed\n", getpid());
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg &= ~IPC_NOWAIT;
VerifyErr(semop(semid, &sb, 1) != 0, "V sem 2000:1");
return 0;
}
[bill@billstone Unix_study]$
编译程序并使用之前的程序ipcsem创建信号量集合:
[bill@billstone Unix_study]$ ./ipcsem 2000 2 c
Create sem success.
[bill@billstone Unix_study]$ ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
0x000003e8 0 bill 666 10
0x000007d0 98305 bill 666 2
[bill@billstone Unix_study]$ ./ipcsem 98305 0 5
Set Sem Val success.
[bill@billstone Unix_study]$ ./ipcsem 98305 1 0
Set Sem Val success.
[bill@billstone Unix_study]$ ./ipcsem 98305 0 a
Get Sem Stat success.
Get Sem All success.
sem no [0]: [5]
sem no [1]: [0]
[bill@billstone Unix_study]$
在一个终端上运行sema:
[bill@billstone Unix_study]$ ./ipcsem 98305 0 a
Get Sem Stat success.
Get Sem All success.
sem no [0]: [3]
sem no [1]: [2]
[bill@billstone Unix_study]$ ./sema
Open Sem 2000 success.
P sem 2000:0 success.
[23940] producing ... ...
[23940] produced
V sem 2000:1 success.
[bill@billstone Unix_study]$ ./ipcsem 98305 0 a
Get Sem Stat success.
Get Sem All success.
sem no [0]: [2]
sem no [1]: [3]
[bill@billstone Unix_study]$
在另一个终端上执行semb:
[bill@billstone Unix_study]$ ./ipcsem 98305 0 a
Get Sem Stat success.
Get Sem All success.
sem no [0]: [2]
sem no [1]: [3]
[bill@billstone Unix_study]$ ./semb
Open Sem 2000 success.
P sem 2000:1 success.
[23942] consuming ... ...
[23942] consumed
V sem 2000:1 success.
[bill@billstone Unix_study]$ ./ipcsem 98305 0 a
Get Sem Stat success.
Get Sem All success.
sem no [0]: [3]
sem no [1]: [2]
共享内存
得到共享内存
VerifyErr((shmid = shmget(0x1234, 10*1024, 0666|IPC_CREAT)) == -1, "Open(Create) Shm");
VerifyErr((pmat = (char *)shmat(shmid, 0, 0)) == 0, "Link Shm");
#include
#include
#include
#include
#include
#define VerifyErr(a, b) \
if (a) { fprintf(stderr, "%s failed.\n", (b)); return; } \
else fprintf(stderr, "%s success.\n", (b));
int main(void)
{
int shmid, no;
char *pmat = NULL, buf[1024];
VerifyErr((shmid = shmget(0x1234, 10*1024, 0666|IPC_CREAT)) == -1, "Open(Create) Shm");
VerifyErr((pmat = (char *)shmat(shmid, 0, 0)) == 0, "Link Shm");
printf("Please input NO.(0-9): ");
scanf("%d", &no);
VerifyErr(no<0 || no>9, "Input No.");
printf("Please input data: ");
memset(buf, 0, sizeof(buf));
getchar(); // 读入'\n'回车符
fgets(buf, sizeof(buf), stdin);
memcpy(pmat+no*1024, buf, 1024);
shmdt(pmat);
return 0;
}
程序shm2从共享内存指定位置读取数据
#include
#include
#include
#include
#include
#define VerifyErr(a, b) \
if (a) { fprintf(stderr, "%s failed.\n", (b)); return; } \
else fprintf(stderr, "%s success.\n", (b));
int main(void)
{
int shmid, no;
char *pmat = NULL, buf[1024];
VerifyErr((shmid = shmget(0x1234, 10*1024, 0666|IPC_CREAT)) == -1, "Open(Create) Shm");
VerifyErr((pmat = (char *)shmat(shmid, 0, 0)) == 0, "Link Shm");
printf("Please input NO.(0-9): ");
scanf("%d", &no);
VerifyErr(no<0 || no>9, "Input No.");
memcpy(buf, pmat+no*1024, 1024);
printf("Data: [%s]\n", buf);
shmdt(pmat);
return 0;
}
运行结果如下
[bill@billstone Unix_study]$ make shm1
cc shm1.c -o shm1
[bill@billstone Unix_study]$ make shm2
cc shm2.c -o shm2
[bill@billstone Unix_study]$ ./shm1
Open(Create) Shm success.
Link Shm success.
Please input NO.(0-9): 1
Input No. success.
Please input data: this is a test!
[bill@billstone Unix_study]$ ./shm2
Open(Create) Shm success.
Link Shm success.
Please input NO.(0-9): 1
Input No. success.
Data: [this is a test!
]
[bill@
把共享内存的空间映射进程序空间来
复制数据进去
printf("Please input NO.(0-9): ");
scanf("%d", &no);
VerifyErr(no<0 || no>9, "Input No.");
memcpy(buf, pmat+no*1024, 1024);
printf("Data: [%s]\n", buf);
取消隐射
shmdt(pmat);
0 条评论
下一页