‘壹’ linux fopen返回NULL,errno为0,用perror输出的是Success,求解释。
你直接1:touch /home/1 创建一个文件;
2:vi 1;
3.输入几个字母abcde
4:wq//保存
fp = fopen( /home/1, "r");
if(fp==NULL) //如果失败了
{
printf("错误!");
exit(1); //中止程序
}
看看打开能行么,可能是宏定义的问题。
errno 记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义的,你看到不一定是fopen失败记录的值。
手工,望采纳!
‘贰’ linux下 C语言perror、strerror函数的作用
void perror(const char *s);
perror ("open_port");
函数说明
perror()用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错手困误原因依照全局变量errno 的值来决定要输出的字符串。 在库函数中有个errno变量,每个errno值对应着以字符串族薯吵表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一兆侍起输出。
范例:
运行结果:
@ubuntu:~/work/dev/test ./perrortest
error code = 2, error msg = No such file or directory
noexitfile: No such file or directory
‘叁’ linux内核 perror 什么作用
perror是用来输出错误的租扰穗,如果某些函数调用不正确的话,调用perror会李闭先输出错误号,然后输出你在perror()参数中指定的内容。printf则是用来输出内容的,内容弊卜由参数指定
‘肆’ linux关于管道说法错误的是什么
Linux关于管道 原创
2018-09-14 12:22:41
Gaodes
码龄5年
关注
管道的概念
管道是Unix中枣镇蔽最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道” 我们通常把是把一个进程的输出连接或“管接”(经过管道来连接)到另一个进程的输入。
管道特点
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道 只能用于父子进程或者兄弟进程之间(具有亲凳州缘关系的进程)进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
pipe函数
包含头文件<unistd.h> 功能:创建一无名管道 原型
int pipe(int file_descriptor[2]);
参数 file_descriptor:文件描述符数组,其中file_descriptor[0]表示读端,file_descriptor[1]表示写端 返回值:成功返回0,失败返回错误代码
示例代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
int main(int argc,char *argv[])
{
int fd[2];
printf("f[0]=%d,f[1]=%d\n",fd[0],fd[1]);
pipe(fd);
printf("f[0]=%d,f[1]=%d\n",fd[0],fd[1]);
char buf[1024]={0};
int fid = fork();
if(fid > 0)
{
read(fd[0],buf,1024);
printf("read data %s\n",buf);
}
else if(fid == 0)
{
write(fd[1],"helloworld",strlen("helloworld"));
}
else
{
perror("fork error");
}
return 0;
}
打印结果
管道读写规则:如果试图从管道写端旅塌读取数据,或者向管道读端写入数据都将导致错误发生 当没有数据可读时,read调用就会阻塞,即进程暂停执行,一直等到有数据来到为止。 如果管道的另一端已经被关闭,也就是没有进程打开这个管道并向它写数据时,read调用就会阻塞
复制文件描述符p
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
int main()
{
int fd = p(1);
printf("file fd= %d\n",fd);
write(fd,"helloworld",strlen("helloworld"));
return 0;
}
打印结果:
1为输入到终端
shell管道的实现
原理通过把发的fd[1]写复制到shell的1(标准输入),fd[0]复制到shell的2(标准输出)
以下是代码:
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
int main()
{
int fd[2];
char buf[1024] ={0};
pipe(fd);
int pid = fork();
if(pid > 0)
{
read(fd[0],buf,1024);
printf(buf);
}
else if(pid == 0)
{
p2(fd[1],1);
close(fd[0]);
close(fd[1]);
execlp("ls","ls","-al",NULL);
}
else
{
}
return 0;
}
实现结果:
popen函数
作用:允许一个程序把另外一个程序当作一个新的进程来启 动,并能对它发送数据或接收数据
FILE* popen(const char *command, const char *open_mode);
command:待运行程序的名字和相应的参数 open_mode:必须是“r”或“w” 如果操作失败,popen会返回一个空指针
以下代码:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
FILE *file = popen("ls -al","r");
char buf[1024] = {0};
fread(buf,1,1024,file);
fclose(file);
FILE *wcfile = popen("wc","w");
fwrite(buf,1,strlen(buf),wcfile);
fclose(wcfile);
return 0;
}
代码结果:
命名管道破裂测试
我们首先要知道命名管道,要读段和写段同时开启,才能向文件读写数据。
贴上代码来理解命名管道的规则
首先是读端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
printf("open before\n");
int fd = open("/home/gao/tmp/fifo",O_RDONLY);
printf("open after\n");
//休眠5秒,读端退出
sleep(5);
return 0;
}
接下来是写端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<fcntl.h>
void handle(int signo)
{
printf("cat signale = %d\n",signo);
}
int main(int argc,char *argv[])
{
signal(SIGPIPE,handle);
printf("open before\n");
int fd = open("/home/gao/tmp/fifo",O_WRONLY);
printf("open after\n");
//命名管道规则,如果写入读断被中断,写入会返回-1,并且管道会破裂,产生信号(SIGPIPE)
while(1)
{
int wrsize = write(fd,"helloworld",strlen("helloworld"));
printf("size data:%d\n",wrsize);
sleep(1);
}
}
执行写端:
它在等待另一端的开启,才能向里面写入数据
此时我们开启读端:
马上可以看到写段可以写数据
而执行5秒后,我们可以看到写的时候返回-1,并且获取到管道破裂的信息(SIGPIPE)
所以这里就是我们所注意的点,当我们写客户端和服务器进行管道传输的时候,如果客户端一旦退出来,就会使管道破裂,所以我们必须通过捕捉信号,来避免这种事情发生。
‘伍’ linux c 程序 运行出现 断错误 小菜求助!
由于逗斗右括号写错位置,导致山瞎磨fp=0,即神帆为空指针,再往下执行就错了。因此:
if((fp=popen(cmd,"r")==NULL))
要改为
if((fp=popen(cmd,"r"))==NULL)
‘陆’ Linux命令SYSTEM()启动命令
system是一个使用简单,设计复杂的程序。
它主要包含fork exec waitpid三个步骤。
下来我来还原楼主的错误:
程序A:
/* socksrv.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#define BACKLOG 10
#define MYPORT 4000
int main()
{
char *addr;
int sockfd;
int new_fd;
struct sockaddr_in my_addr, their_addr;
int res;
int sin_size;
char *buf;
/* 取得套接字描述符*/
sockfd = socket(AF_INET, /* domain*/
SOCK_STREAM, /* type*/
0); /* protocol*/
if (sockfd == -1) {
perror("socket");
exit(1);
}
/* Init sockaddr_in */
my_addr.sin_family = AF_INET; /* 注意: 应使用主机字节顺序*/
my_addr.sin_port = htons(MYPORT); /* 注意: 应使用网络字节顺序*/
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 使用自己的 IP 地址 */
bzero(&(my_addr.sin_zero), 8); /* 结构的其余的部分须置 0*/
/* 指定一个套接字使用的地址及端口*/
res = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));
if (res == -1) {
perror("bind");
exit(1);
}
/* 监听请求, 等待连接*/
res = listen(sockfd,
BACKLOG); /* 未经处理的连接请求队列可容纳的最大数目*/
if (res == -1) {
perror("listen");
exit(1);
}
system("./hello&");
/* 接受对方的连接请求, 建立连接,返回一个新的连接描述符.
* 而第一个套接字描述符仍在你的机器上原来的端口 listen()
*/
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, (void *)&their_addr, &sin_size);
buf = (char *)malloc(255);
if (buf == NULL) {
printf("malloc failed\n");
exit(1);
}
/* 接受对方发来的数据*/
res = recv(new_fd, buf, 255, 0);
if (res == -1) {
perror("recv()");
exit(1);
}
/* 关闭本次连接*/
close(new_fd);
/* 关闭系统监听*/
close(sockfd);
printf("recv data:%s\n", buf);
free(buf);
return 0;
}
程序B:hello,在主程序中用system("./hello&)调用。
#include <stdlib.h>
int main(){
while(1){
sleep(1000);
}
return 0;
}
编译后运行程序A。我们可以在其它终端窗口看到 ./A ./hello正在运行,netstat -a 看到,tcp 4000端口被 占用。
我们用Ctrl+c中断程序A模拟楼主的崩溃操作。
这时,再在其它终端窗口看看,./A没有了。./hello还在运行。netstat -a看到。4000端口还在占用。
这时再次运行./A,提示bind: Address already in use而退出。
情况就是这样。
因为执行system时,系统会fork一个A的子进程,再去执行B.
当你的A崩溃以后,它的一个子进程实际上还在运行,它打开的端口,文件,等还在使用。
所以再次运行A时,由于自定的互斥机制而退出。
如:再次绑定端口时提示端口已在使用。
杀死B后,A的子进程结束,它的资源释放,所以才能再次运行A。
我建议楼主使用exec系列函数来启动B。
‘柒’ Linux系统中的守护进程讲解
守护进程daemon,是生存期较长的一种进程。它们常常在系统自举时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX系统有很多守护进程,它们执行日常事务活动。
1、系统自举
自举(bootstrapping)一词来自于人都是靠自身的自举机构站立起来的这一思想。计算机必须具备自举能力将自己所有的元件激活,以便能完成加载操作系统这一目的,然后再由操作系统承担起那些单靠自举代码无法完成的更复杂的任务。
自举只有两个功能:加电自检和磁盘引导。
加电自检:当我们按下计算机电源开关时,头几秒钟机器似乎什么反应也没有,其实,这时的计算机正在进行加电自检,以断定它的所有元件都在正确地工作。如果某个元件有故障,显示器上就会出现报警提示信息(如果显示器也不能正常工作,则以一串嘟嘟声来报警)。由于大多数计算机工作非常可靠,加电自检报警非常罕见。
磁盘引导:查找装有操作系统的磁盘驱动器。从磁盘加载操作系统的原因有二,一是操作系统升级简单容易,二是使用户拥有选择操作系统的自由。
当加电自检和磁盘引导完成时,自举操作就启动一个读写操作系统文件和将它们复制到随机存储器中的过程,此时的机器才是真正意义上的计算机。计算机的启动可以有冷启动和热启动两种方式 ,它们之间的差别是热启动不进行机器的自检(机器本身配置的检查与测试),当计算机在使用过程中由于某些原因造成死机时,可以对计算机进行热启动处理。
2、守护进程的概念
通过ps axj命令可以查看到守护进程:
参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
代码如下:PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 1 1 1 ? -1 Ss 0 0:01 /sbin/init 0 2 0 0 ? -1 S< 0 0:00 [kthreadd] 2 3 0 0 ? -1 S< 0 0:00 [migration/0] 2 4 0 0 ? -1 S< 0 0:00 [ksoftirqd/0]... 1 2373 2373 2373 ? -1 S<s 0 0:00 /sbin/udevd --daemon... 1 4680 4680 4680 ? -1 Ss 0 0:00 /usr/sbin/acpid -c /etc... 1 4808 4808 4808 ? -1 Ss 102 0:00 /sbin/syslogd -u syslog...
凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel。init进程我们已经很熟悉了,udevd负责维护/dev目录下的设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件,可以看出,守护进程通常采用以d结尾的`名字,表示Daemon。 创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。 例子: C/C++ Code复制内容到剪贴板 void daemonize(void) { pid_t pid; printf("into deamonizen"); if (pid=fork() < 0) { perror("fork"); exit(1); } else if (pid !=0) { exit(0); } setsid(); if (chdir("/") < 0) { perror("chdir"); exit(1); } close(0); open("/dev/null", O_RDWR); p2(0, 1); p2(0, 2); printf("out deamonizen"); }
3、编写守护进程 在编写守护进程程序时,需遵循一些基本规则:
(1)首先要做的是调用umask将文件模式创建屏蔽字设置为0。
(2)调用fork,然后使父进程退出。
(3)调用setsid以创建一个新会话。
(4)将当前工作目录更改为根目录。
(5)关闭不再需要的文件描述符。
(6)某些守护进程打开/dev/null使其具有文件描述符0、1和2,任何一个试图读标准输入、写标准输出或标准出错的库例程都不会产生任何效果。 与守护进程有关的一个问题是如何处理出错消息,需要有一个集中的守护进程出错记录设施,这就是syslogd进程。
4、守护进程惯例 为了正常运作,某些守护进程实现为单实例的,有就是在任一时刻只运行该守护进程的一个副本。文件锁和记录锁机制是一种方法的基础,该方法用来保证一个守护进程只有一个副本在运行。
在UNIX系统中,守护进程遵循下列公共惯例:
(1)若守护进程使用锁文件,那么该文件通常存放在/var/run目录中。锁文件的名字通常是name.pid,name是该守护进程或服务的名字。
(2)若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。配置文件的名字通常是name.conf。
(3)守护进程可用命令行启动,但通常它们是由系统初始化脚本启动的。
(4)若一守护进程有一配置文件,那么当该守护进程启动时,它读该文件,但在此之后一般就不会再查看它。
‘捌’ LinuxC语言头里面的ERROR函数怎么使用
errno会返回一个数字,每个数字代表一个错误类型。详细的可以查看头文件。/usr/include/asm/errno.h
如何把errno的数字转换成相应的文字说明?
方式一:可以使用strerrno函数
char
*strerror(int
errno)
使用方式如下:
fprintf(stderr,"error
in
CreateProcess
%s,
Process
ID
%d
",strerror(errno),processID)
将错误代码转换为字符串错误信息,可以将该字符串和其它的信息组合输出到用户界面。
注:假设processID是一个已经获取了的整形ID
方式二:使用perror函数
void
perror(const
char
*s)
函数说明
perror
(
)用来将上一个函数发生错误的原因输出到标准错误(stderr),参数s
所指的字符串会先打印出,后面再加上错误原因
字符串。此错误原因依照全局变量
errno
的值来决定要输出的字符串。
另外并不是所有的c函数调用发生的错误信息都会修改errno。例如gethostbyname函数。
errno是否是线程安全的?
errno是支持线程安全的,而且,一般而言,编译器会自动保证errno的安全性。
我们看下相关头文件
/usr/include/bits/errno.h
会看到如下内容:
#
if
!defined
_LIBC
||
defined
_LIBC_REENTRANT
/*
When
using
threads,
errno
is
a
per-thread
value.
*/
#
define
errno
(*__errno_location
())
#
endif
#
endif
/*
!__ASSEMBLER__
*/
#endif
/*
_ERRNO_H
*/
也就是说,在没有定义__LIBC或者定义_LIBC_REENTRANT的时候,errno是多线程/进程安全的。
为了检测一下你编译器是否定义上述变量,不妨使用下面一个简单程序。
#include
<stdio.h>
#include
<errno.h>
int
main(
void
)
{
#ifndef
__ASSEMBLER__
printf(
"Undefine
__ASSEMBLER__/n"
);
#else
printf(
"define
__ASSEMBLER__/n"
);
#endif
#ifndef
__LIBC
printf(
"Undefine
__LIBC/n"
);
#else
printf(
"define
__LIBC/n"
);
#endif
#ifndef
_LIBC_REENTRANT
printf(
"Undefine
_LIBC_REENTRANT/n"
);
#else
printf(
"define
_LIBC_REENTRANT/n"
);
#endif
return
0;
}
‘玖’ linux 程序运行错误 perror输出之后 前面的printf输出在显示器上也没有了吗
不会的 perror对比printf 无非就是多了一个错误原因
‘拾’ Linux, rmdir系统调用后perror显示错误“illegal seek”, 什么意思
你是在写一段代码?然后执行过后,出现的,那个报错提示?图片看的不是很清楚!```system在成功执行时会返回0,在失败时会睁槐败返回1, 这样明茄的话,system语句则应该为:悉颤
system($cmd)==0 or die $!;